Skip to content

Commit d477c97

Browse files
hkulekciclaude
andcommitted
Add integration tests for Query endpoint
Cover all three Query endpoint methods (query, batch, groups) with integration tests including nearest vector search, filters, payload, vector inclusion, score threshold, offset/limit, search params, using, recommend queries, query parameters, batch queries, and group queries with payload and filter support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c9c6254 commit d477c97

1 file changed

Lines changed: 351 additions & 0 deletions

File tree

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
<?php
2+
/**
3+
* @since Mar 2026
4+
* @author Haydar KULEKCI <haydarkulekci@gmail.com>
5+
*/
6+
7+
namespace Qdrant\Tests\Integration\Endpoints\Collections\Points;
8+
9+
use Qdrant\Endpoints\Collections;
10+
use Qdrant\Exception\InvalidArgumentException;
11+
use Qdrant\Models\Filter\Condition\MatchString;
12+
use Qdrant\Models\Filter\Filter;
13+
use Qdrant\Models\PointsStruct;
14+
use Qdrant\Models\Request\Points\BatchQueryRequest;
15+
use Qdrant\Models\Request\Points\QueryGroupsRequest;
16+
use Qdrant\Models\Request\Points\QueryRequest;
17+
use Qdrant\Models\VectorStruct;
18+
use Qdrant\Tests\Integration\AbstractIntegration;
19+
20+
class QueryTest extends AbstractIntegration
21+
{
22+
/**
23+
* @throws InvalidArgumentException
24+
*/
25+
public function setUp(): void
26+
{
27+
parent::setUp();
28+
29+
$this->createCollections('sample-collection');
30+
$response = $this->getCollections('sample-collection')->points()
31+
->upsert(PointsStruct::createFromArray(self::basicPointDataProvider()));
32+
$this->assertEquals('ok', $response['status']);
33+
$this->assertEquals('acknowledged', $response['result']['status']);
34+
}
35+
36+
public static function basicPointDataProvider(): array
37+
{
38+
return [
39+
[
40+
'id' => 1,
41+
'vector' => new VectorStruct([1, 3, 400], 'image'),
42+
'payload' => [
43+
'image' => 'sample image',
44+
'category' => 'landscape',
45+
]
46+
],
47+
[
48+
'id' => 2,
49+
'vector' => new VectorStruct([1, 3, 300], 'image'),
50+
'payload' => [
51+
'image' => 'sample image',
52+
'category' => 'landscape',
53+
]
54+
],
55+
[
56+
'id' => 3,
57+
'vector' => new VectorStruct([1, 3, 200], 'image'),
58+
'payload' => [
59+
'image' => 'sample image',
60+
'category' => 'portrait',
61+
]
62+
],
63+
[
64+
'id' => 4,
65+
'vector' => new VectorStruct([1, 3, 100], 'image'),
66+
'payload' => [
67+
'image' => 'another image',
68+
'category' => 'portrait',
69+
]
70+
],
71+
];
72+
}
73+
74+
public function testQueryWithNearestVector(): void
75+
{
76+
$queryRequest = (new QueryRequest())
77+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
78+
->setLimit(3);
79+
80+
$response = $this->getCollections('sample-collection')
81+
->points()->query()->query($queryRequest);
82+
83+
$this->assertEquals('ok', $response['status']);
84+
$this->assertNotEmpty($response['result']['points']);
85+
}
86+
87+
public function testQueryWithFilter(): void
88+
{
89+
$queryRequest = (new QueryRequest())
90+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
91+
->setFilter(
92+
(new Filter())->addMust(
93+
new MatchString('image', 'sample image')
94+
)
95+
)
96+
->setLimit(3);
97+
98+
$response = $this->getCollections('sample-collection')
99+
->points()->query()->query($queryRequest);
100+
101+
$this->assertEquals('ok', $response['status']);
102+
$this->assertNotEmpty($response['result']['points']);
103+
$this->assertLessThanOrEqual(3, count($response['result']['points']));
104+
}
105+
106+
public function testQueryWithPayload(): void
107+
{
108+
$queryRequest = (new QueryRequest())
109+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
110+
->setWithPayload(true)
111+
->setLimit(3);
112+
113+
$response = $this->getCollections('sample-collection')
114+
->points()->query()->query($queryRequest);
115+
116+
$this->assertEquals('ok', $response['status']);
117+
$this->assertNotEmpty($response['result']['points']);
118+
$this->assertArrayHasKey('payload', $response['result']['points'][0]);
119+
}
120+
121+
public function testQueryWithVector(): void
122+
{
123+
$queryRequest = (new QueryRequest())
124+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
125+
->setWithVector(true)
126+
->setLimit(3);
127+
128+
$response = $this->getCollections('sample-collection')
129+
->points()->query()->query($queryRequest);
130+
131+
$this->assertEquals('ok', $response['status']);
132+
$this->assertNotEmpty($response['result']['points']);
133+
$this->assertArrayHasKey('vector', $response['result']['points'][0]);
134+
}
135+
136+
public function testQueryWithScoreThreshold(): void
137+
{
138+
$queryRequest = (new QueryRequest())
139+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
140+
->setScoreThreshold(0.99)
141+
->setLimit(10);
142+
143+
$response = $this->getCollections('sample-collection')
144+
->points()->query()->query($queryRequest);
145+
146+
$this->assertEquals('ok', $response['status']);
147+
148+
$queryRequestNoThreshold = (new QueryRequest())
149+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
150+
->setLimit(10);
151+
152+
$responseNoThreshold = $this->getCollections('sample-collection')
153+
->points()->query()->query($queryRequestNoThreshold);
154+
155+
$this->assertGreaterThanOrEqual(
156+
count($response['result']['points']),
157+
count($responseNoThreshold['result']['points'])
158+
);
159+
}
160+
161+
public function testQueryWithOffset(): void
162+
{
163+
$queryRequest = (new QueryRequest())
164+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
165+
->setLimit(2)
166+
->setOffset(0);
167+
168+
$response = $this->getCollections('sample-collection')
169+
->points()->query()->query($queryRequest);
170+
171+
$this->assertEquals('ok', $response['status']);
172+
173+
$queryRequestOffset = (new QueryRequest())
174+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
175+
->setLimit(2)
176+
->setOffset(2);
177+
178+
$responseOffset = $this->getCollections('sample-collection')
179+
->points()->query()->query($queryRequestOffset);
180+
181+
$this->assertEquals('ok', $responseOffset['status']);
182+
}
183+
184+
public function testQueryWithParams(): void
185+
{
186+
$queryRequest = (new QueryRequest())
187+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
188+
->setParams([
189+
'hnsw_ef' => 128,
190+
'exact' => false,
191+
])
192+
->setLimit(3);
193+
194+
$response = $this->getCollections('sample-collection')
195+
->points()->query()->query($queryRequest);
196+
197+
$this->assertEquals('ok', $response['status']);
198+
}
199+
200+
public function testQueryWithUsing(): void
201+
{
202+
$queryRequest = (new QueryRequest())
203+
->setQuery(['nearest' => [1, 2, 300]])
204+
->setUsing('image')
205+
->setLimit(3);
206+
207+
$response = $this->getCollections('sample-collection')
208+
->points()->query()->query($queryRequest);
209+
210+
$this->assertEquals('ok', $response['status']);
211+
$this->assertNotEmpty($response['result']['points']);
212+
}
213+
214+
public function testQueryRecommend(): void
215+
{
216+
$queryRequest = (new QueryRequest())
217+
->setQuery([
218+
'recommend' => [
219+
'positive' => [1],
220+
'negative' => [2],
221+
]
222+
])
223+
->setUsing('image')
224+
->setFilter(
225+
(new Filter())->addMust(
226+
new MatchString('image', 'sample image')
227+
)
228+
)
229+
->setLimit(3);
230+
231+
$response = $this->getCollections('sample-collection')
232+
->points()->query()->query($queryRequest);
233+
234+
$this->assertEquals('ok', $response['status']);
235+
}
236+
237+
public function testQueryWithQueryParams(): void
238+
{
239+
$queryRequest = (new QueryRequest())
240+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
241+
->setLimit(3);
242+
243+
$response = $this->getCollections('sample-collection')
244+
->points()->query()->query($queryRequest, ['timeout' => 1]);
245+
246+
$this->assertEquals('ok', $response['status']);
247+
}
248+
249+
public function testBatchQuery(): void
250+
{
251+
$request1 = (new QueryRequest())
252+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
253+
->setLimit(3);
254+
255+
$request2 = (new QueryRequest())
256+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 3, 400]]])
257+
->setLimit(2);
258+
259+
$batchRequest = new BatchQueryRequest([$request1, $request2]);
260+
261+
$response = $this->getCollections('sample-collection')
262+
->points()->query()->batch($batchRequest);
263+
264+
$this->assertEquals('ok', $response['status']);
265+
$this->assertCount(2, $response['result']);
266+
}
267+
268+
public function testBatchQueryWithQueryParams(): void
269+
{
270+
$request1 = (new QueryRequest())
271+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
272+
->setLimit(3);
273+
274+
$batchRequest = new BatchQueryRequest([$request1]);
275+
276+
$response = $this->getCollections('sample-collection')
277+
->points()->query()->batch($batchRequest, ['timeout' => 1]);
278+
279+
$this->assertEquals('ok', $response['status']);
280+
}
281+
282+
public function testQueryGroups(): void
283+
{
284+
$groupsRequest = (new QueryGroupsRequest('category'))
285+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
286+
->setGroupSize(2)
287+
->setLimit(5);
288+
289+
$response = $this->getCollections('sample-collection')
290+
->points()->query()->groups($groupsRequest);
291+
292+
$this->assertEquals('ok', $response['status']);
293+
$this->assertArrayHasKey('groups', $response['result']);
294+
}
295+
296+
public function testQueryGroupsWithPayload(): void
297+
{
298+
$groupsRequest = (new QueryGroupsRequest('category'))
299+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
300+
->setGroupSize(2)
301+
->setLimit(5)
302+
->setWithPayload(true);
303+
304+
$response = $this->getCollections('sample-collection')
305+
->points()->query()->groups($groupsRequest);
306+
307+
$this->assertEquals('ok', $response['status']);
308+
$this->assertArrayHasKey('groups', $response['result']);
309+
$this->assertNotEmpty($response['result']['groups']);
310+
}
311+
312+
public function testQueryGroupsWithFilter(): void
313+
{
314+
$groupsRequest = (new QueryGroupsRequest('category'))
315+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
316+
->setFilter(
317+
(new Filter())->addMust(
318+
new MatchString('image', 'sample image')
319+
)
320+
)
321+
->setGroupSize(2)
322+
->setLimit(5);
323+
324+
$response = $this->getCollections('sample-collection')
325+
->points()->query()->groups($groupsRequest);
326+
327+
$this->assertEquals('ok', $response['status']);
328+
$this->assertArrayHasKey('groups', $response['result']);
329+
}
330+
331+
public function testQueryGroupsWithQueryParams(): void
332+
{
333+
$groupsRequest = (new QueryGroupsRequest('category'))
334+
->setQuery(['nearest' => ['name' => 'image', 'vector' => [1, 2, 300]]])
335+
->setGroupSize(2)
336+
->setLimit(5);
337+
338+
$response = $this->getCollections('sample-collection')
339+
->points()->query()->groups($groupsRequest, ['timeout' => 1]);
340+
341+
$this->assertEquals('ok', $response['status']);
342+
}
343+
344+
protected function tearDown(): void
345+
{
346+
parent::tearDown();
347+
$collections = new Collections($this->client);
348+
349+
$collections->setCollectionName('sample-collection')->delete();
350+
}
351+
}

0 commit comments

Comments
 (0)