diff --git a/src/Endpoints/HttpFactoryTrait.php b/src/Endpoints/HttpFactoryTrait.php index fe5edbb..5ca08b9 100644 --- a/src/Endpoints/HttpFactoryTrait.php +++ b/src/Endpoints/HttpFactoryTrait.php @@ -52,6 +52,7 @@ protected function createRequest(string $method, string $uri, array $body = []): protected function queryBuild(array $params): string { - return '?' . http_build_query($params); + $paramStr = http_build_query($params); + return $paramStr ? '?' . $paramStr : ''; } } \ No newline at end of file diff --git a/src/Models/Request/CreateIndex.php b/src/Models/Request/CreateIndex.php index 54ef0a5..1aa1432 100644 --- a/src/Models/Request/CreateIndex.php +++ b/src/Models/Request/CreateIndex.php @@ -8,16 +8,27 @@ class CreateIndex implements RequestModel { - public function __construct(protected string $fieldName, protected string $fieldSchema, protected array $schemaParams = []) + protected string $fieldName; + protected ?array $fieldSchema; + + public function __construct(string $fieldName, array|string $fieldSchema = null) { + if (is_string($fieldSchema)) { + $this->fieldSchema = [ + 'type' => $fieldSchema + ]; + } else { + $this->fieldSchema = $fieldSchema; + } + + $this->fieldName = $fieldName; } public function toArray(): array { - // TODO: schema params is missing - return [ + return array_filter([ 'field_name' => $this->fieldName, 'field_schema' => $this->fieldSchema - ]; + ]); } } \ No newline at end of file diff --git a/src/Models/Request/ScrollRequest.php b/src/Models/Request/ScrollRequest.php index 93aabde..e55dd5d 100644 --- a/src/Models/Request/ScrollRequest.php +++ b/src/Models/Request/ScrollRequest.php @@ -20,6 +20,8 @@ class ScrollRequest implements RequestModel protected bool|array|null $withPayload = null; + protected string|array|null $orderBy = null; + public function setFilter(Filter $filter): static { $this->filter = $filter; @@ -41,6 +43,13 @@ public function setOffset(int|string $offset): static return $this; } + public function setOrderBy(array|string $orderBy): static + { + $this->orderBy = $orderBy; + + return $this; + } + public function setWithPayload($withPayload): static { $this->withPayload = $withPayload; @@ -74,6 +83,9 @@ public function toArray(): array if ($this->withPayload) { $body['with_payload'] = $this->withPayload; } + if ($this->orderBy) { + $body['order_by'] = $this->orderBy; + } return $body; } diff --git a/tests/Integration/Endpoints/Collections/SearchSortTest.php b/tests/Integration/Endpoints/Collections/SearchSortTest.php new file mode 100644 index 0000000..33423b1 --- /dev/null +++ b/tests/Integration/Endpoints/Collections/SearchSortTest.php @@ -0,0 +1,130 @@ + + */ + +namespace Integration\Endpoints\Collections; + +use Qdrant\Endpoints\Collections; +use Qdrant\Exception\InvalidArgumentException; +use Qdrant\Models\Filter\Condition\MatchString; +use Qdrant\Models\Filter\Filter; +use Qdrant\Models\PointsStruct; +use Qdrant\Models\Request\CreateIndex; +use Qdrant\Models\Request\ScrollRequest; +use Qdrant\Models\Request\SearchRequest; +use Qdrant\Models\VectorStruct; +use Qdrant\Tests\Integration\AbstractIntegration; + +class SearchSortTest extends AbstractIntegration +{ + /** + * @throws InvalidArgumentException + */ + public function setUp(): void + { + parent::setUp(); + + $this->createCollections('sample-collection'); + + $response = $this->getCollections('sample-collection')->index()->create( + (new CreateIndex('sort', [ + 'type' => 'integer', + 'range' => true, + 'lookup' => false, + 'is_principal' => true + ])), + [ + 'wait' => 'true' + ] + ); + + $this->assertEquals('ok', $response['status']); + $this->assertEquals('completed', $response['result']['status']); + + $response = $this->getCollections('sample-collection')->points() + ->upsert( + PointsStruct::createFromArray(self::basicPointDataProvider()[0][0]), + [ + 'wait' => 'true' + ] + ); + + $this->assertEquals('ok', $response['status']); + $this->assertEquals('completed', $response['result']['status']); + } + + public static function basicPointDataProvider(): array + { + return [ + [ + [ + [ + 'id' => 1, + 'vector' => new VectorStruct([1, 3, 400], 'image'), + 'payload' => [ + 'sort' => 1, + 'color' => 'red' + ] + ], + [ + 'id' => 2, + 'vector' => new VectorStruct([1, 3, 300], 'image'), + 'payload' => [ + 'sort' => 2, + 'color' => 'red' + ] + ], + [ + 'id' => 3, + 'vector' => new VectorStruct([1, 3, 300], 'image'), + 'payload' => [ + 'sort' => 3, + 'color' => 'green' + ] + ], + ] + ] + ]; + } + + public function testScrollAscPoint(): void + { + $filter = (new Filter())->addMust( + new MatchString('color', 'red') + ); + + $scroll = (new ScrollRequest())->setFilter($filter)->setOrderBy('sort'); + $response = $this->getCollections('sample-collection')->points()->scroll($scroll); + + $this->assertEquals('ok', $response['status']); + $this->assertCount(2, $response['result']); + $this->assertEquals(1, $response['result']['points'][0]['id']); + } + + public function testScrollDescPoint(): void + { + $filter = (new Filter())->addMust( + new MatchString('color', 'red') + ); + + $scroll = (new ScrollRequest())->setFilter($filter)->setOrderBy([ + 'key' => 'sort', + 'direction' => 'desc' + ]); + $response = $this->getCollections('sample-collection')->points()->scroll($scroll); + + $this->assertEquals('ok', $response['status']); + $this->assertCount(2, $response['result']); + $this->assertEquals(2, $response['result']['points'][0]['id']); + } + + protected function tearDown(): void + { + parent::tearDown(); + $collections = new Collections($this->client); + + $collections->setCollectionName('sample-collection')->delete(); + } +} \ No newline at end of file