.gitignore000064400000000104151567466610006550 0ustar00/vendor/ composer.lock .phpunit.cache/ .phpcs.cache/ .DS_Store *.logREADME.md000064400000016303151567466610006047 0ustar00# Milvus PHP Client PHP客户端库,用于通过RESTful API与Milvus 2.6.x进行交互。 ## 要求 - PHP 8.1 或更高版本 - Guzzle HTTP客户端 - JSON扩展 ## 安装 使用Composer安装: ```bash composer require milvus/milvus-client-php ``` ## 快速开始 ### 基本连接 ```php use Milvus\Client\MilvusClient; $client = new MilvusClient('localhost', 19530); ``` ### 使用认证 ```php $client = new MilvusClient('localhost', 19530, 'your-token'); ``` ### 使用HTTPS ```php $client = new MilvusClient('your-milvus-host.com', 443, 'your-token', 'https'); ``` ## 创建Collection ```php use Milvus\Client\Constants\DataType; use Milvus\Client\Schema\CollectionSchema; use Milvus\Client\Schema\FieldSchema; $schema = CollectionSchema::create('products') ->setDescription('Product embeddings collection') ->setEnableDynamicField(true) ->addField(FieldSchema::primaryKey('id')) ->addField(FieldSchema::scalar('name', DataType::VARCHAR)->setMaxLength(255)) ->addField(FieldSchema::scalar('price', DataType::FLOAT)) ->addField(FieldSchema::vector('embedding', 128)); $result = $client->collections->create('products', $schema->toArray()); ``` ## 创建索引 ```php use Milvus\Client\Constants\IndexType; use Milvus\Client\Constants\MetricType; $indexParams = [ 'index_type' => IndexType::HNSW, 'metric_type' => MetricType::COSINE, 'params' => [ 'M' => 16, 'efConstruction' => 256, ], ]; $result = $client->indexes->create('products', 'embedding', $indexParams); ``` ## 加载Collection ```php $result = $client->collections->load('products'); ``` ## 插入数据 ```php $data = [ [ 'name' => 'Product 1', 'price' => 29.99, 'embedding' => array_fill(0, 128, 0.1), ], [ 'name' => 'Product 2', 'price' => 49.99, 'embedding' => array_fill(0, 128, 0.2), ], ]; $result = $client->data->insert('products', $data); ``` ## 向量搜索 ```php $queryVector = array_fill(0, 128, 0.15); $result = $client->data->search('products', [$queryVector], 'embedding', [ 'topK' => 5, 'params' => [ 'ef' => 64, ], ]); ``` ## 标量查询 ```php $result = $client->data->query('products', 'price > 30', [ 'outputFields' => ['name', 'price'], ]); ``` ## API参考 ### Collection管理 - `list(string $dbName = '_default')` - 列出所有collections - `create(string $collectionName, array $schema, array $options = [])` - 创建collection - `describe(string $collectionName, string $dbName = '_default')` - 描述collection - `drop(string $collectionName, string $dbName = '_default')` - 删除collection - `getStats(string $collectionName, string $dbName = '_default')` - 获取collection统计信息 - `load(string $collectionName, array $options = [])` - 加载collection - `release(string $collectionName, array $options = [])` - 释放collection - `has(string $collectionName, string $dbName = '_default')` - 检查collection是否存在 - `rename(string $oldCollectionName, string $newCollectionName, string $dbName = '_default')` - 重命名collection - `alter(string $collectionName, array $properties, string $dbName = '_default')` - 修改collection属性 ### 数据操作 - `insert(string $collectionName, array $data, array $options = [])` - 插入数据 - `upsert(string $collectionName, array $data, array $options = [])` - 更新或插入数据 - `delete(string $collectionName, array $filter, array $options = [])` - 删除数据 - `get(string $collectionName, array $ids, array $options = [])` - 根据ID获取数据 - `query(string $collectionName, string $filter, array $options = [])` - 查询数据 - `search(string $collectionName, array $vectors, string $vectorField, array $options = [])` - 向量搜索 - `hybridSearch(string $collectionName, array $searchRequests, array $options = [])` - 混合搜索 ### 索引管理 - `create(string $collectionName, string $fieldName, array $indexParams, array $options = [])` - 创建索引 - `list(string $collectionName, string $dbName = '_default')` - 列出索引 - `describe(string $collectionName, string $indexName, string $dbName = '_default')` - 描述索引 - `drop(string $collectionName, string $indexName, string $dbName = '_default')` - 删除索引 ### 分区管理 - `list(string $collectionName, string $dbName = '_default')` - 列出分区 - `create(string $collectionName, string $partitionName, array $options = [])` - 创建分区 - `has(string $collectionName, string $partitionName, string $dbName = '_default')` - 检查分区是否存在 - `drop(string $collectionName, string $partitionName, string $dbName = '_default')` - 删除分区 - `load(string $collectionName, array $partitionNames, array $options = [])` - 加载分区 - `release(string $collectionName, array $partitionNames, array $options = [])` - 释放分区 - `getStats(string $collectionName, string $partitionName, string $dbName = '_default')` - 获取分区统计信息 ## 数据类型 支持的数据类型常量: - `DataType::BOOL` - 布尔值 - `DataType::INT8` - 8位整数 - `DataType::INT16` - 16位整数 - `DataType::INT32` - 32位整数 - `DataType::INT64` - 64位整数 - `DataType::FLOAT` - 单精度浮点数 - `DataType::DOUBLE` - 双精度浮点数 - `DataType::STRING` - 字符串 - `DataType::VARCHAR` - 可变长度字符串 - `DataType::JSON` - JSON对象 - `DataType::ARRAY` - 数组 - `DataType::FLOAT_VECTOR` - 浮点向量 - `DataType::BINARY_VECTOR` - 二进制向量 - `DataType::FLOAT16_VECTOR` - 16位浮点向量 - `DataType::BFLOAT16_VECTOR` - 16位脑浮点向量 - `DataType::SPARSE_FLOAT_VECTOR` - 稀疏浮点向量 ## 索引类型 支持的索引类型: - `IndexType::FLAT` - FLAT索引 - `IndexType::IVF_FLAT` - IVF_FLAT索引 - `IndexType::IVF_SQ8` - IVF_SQ8索引 - `IndexType::IVF_PQ` - IVF_PQ索引 - `IndexType::HNSW` - HNSW索引 - `IndexType::ANNOY` - ANNOY索引 - `IndexType::DISKANN` - DISKANN索引 - `IndexType::AUTOINDEX` - 自动索引 - `IndexType::SCANN` - SCANN索引 - `IndexType::SPARSE_INVERTED_INDEX` - 稀疏倒排索引 - `IndexType::SPARSE_WAND` - 稀疏WAND索引 ## 距离度量类型 支持的距离度量: - `MetricType::L2` - 欧几里得距离 - `MetricType::IP` - 内积 - `MetricType::COSINE` - 余弦相似度 - `MetricType::HAMMING` - 汉明距离 - `MetricType::JACCARD` - Jaccard距离 - `MetricType::TANIMOTO` - Tanimoto系数 ## 错误处理 ```php use Milvus\Client\Exception\ApiException; use Milvus\Client\Exception\RequestException; try { $result = $client->collections->create('products', $schema->toArray()); } catch (ApiException $e) { echo "API Error: " . $e->getMessage() . "\n"; } catch (RequestException $e) { echo "Request Error: " . $e->getMessage() . "\n"; } ``` ## 测试 运行测试: ```bash composer test ``` 运行静态分析: ```bash composer analyze ``` ## 许可证 MIT License ## 贡献 欢迎贡献!请随时提交Pull Request。 ## 支持 如有问题或建议,请在GitHub上提交Issue。 ## API版本说明 本客户端使用Milvus v2 RESTful API,这是推荐的API版本。v1 API将在未来被弃用。 v2 API的主要特点: - 更完整的API覆盖(包括索引管理、分区管理、RBAC等) - 更一致的API设计 - 更好的错误处理 - 支持更多高级功能 composer.json000064400000001531151567466610007307 0ustar00{ "name": "milvus/milvus-client-php", "description": "PHP client for Milvus 2.6.x RESTful API", "type": "library", "license": "MIT", "authors": [ { "name": "Milvus PHP Client Contributors", "email": "dev@milvus.io" } ], "require": { "php": ">=8.1", "guzzlehttp/guzzle": "^7.0", "ext-json": "*" }, "require-dev": { "phpunit/phpunit": "^10.0", "phpstan/phpstan": "^1.10" }, "autoload": { "psr-4": { "Milvus\\Client\\": "src/" } }, "autoload-dev": { "psr-4": { "Milvus\\Client\\Tests\\": "tests/" } }, "scripts": { "test": "phpunit", "analyze": "phpstan analyse src --level=8" }, "minimum-stability": "stable", "prefer-stable": true } examples/basic_usage.php000064400000005174151567466610011370 0ustar00setDescription('Product embeddings collection') ->setEnableDynamicField(true) ->addField(FieldSchema::primaryKey('id')) ->addField(FieldSchema::scalar('name', DataType::VARCHAR)->setMaxLength(255)) ->addField(FieldSchema::scalar('price', DataType::FLOAT)) ->addField(FieldSchema::vector('embedding', 128)); try { $result = $client->collections->create('products', $schema->toArray()); echo "Collection created: " . json_encode($result, JSON_PRETTY_PRINT) . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } $indexParams = [ 'index_type' => IndexType::HNSW, 'metric_type' => MetricType::COSINE, 'params' => [ 'M' => 16, 'efConstruction' => 256, ], ]; try { $result = $client->indexes->create('products', 'embedding', $indexParams); echo "Index created: " . json_encode($result, JSON_PRETTY_PRINT) . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } try { $result = $client->collections->load('products'); echo "Collection loaded: " . json_encode($result, JSON_PRETTY_PRINT) . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } $data = [ [ 'name' => 'Product 1', 'price' => 29.99, 'embedding' => array_fill(0, 128, 0.1), ], [ 'name' => 'Product 2', 'price' => 49.99, 'embedding' => array_fill(0, 128, 0.2), ], ]; try { $result = $client->data->insert('products', $data); echo "Data inserted: " . json_encode($result, JSON_PRETTY_PRINT) . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } $queryVector = array_fill(0, 128, 0.15); try { $result = $client->data->search('products', [$queryVector], 'embedding', [ 'topK' => 5, 'params' => [ 'ef' => 64, ], ]); echo "Search results: " . json_encode($result, JSON_PRETTY_PRINT) . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } try { $result = $client->data->query('products', 'price > 30', [ 'outputFields' => ['name', 'price'], ]); echo "Query results: " . json_encode($result, JSON_PRETTY_PRINT) . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } phpunit.xml000064400000001041151567466610006772 0ustar00 tests src src/Api/Collection.php000064400000005724151567466610010701 0ustar00client = $client; } public function list(string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/list', [ 'dbName' => $dbName, ]); } public function create(string $collectionName, array $schema, array $options = []): array { $data = array_merge([ 'collectionName' => $collectionName, 'schema' => $schema, ], $options); return $this->client->post('/v2/vectordb/collections/create', $data); } public function describe(string $collectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/describe', [ 'collectionName' => $collectionName, 'dbName' => $dbName, ]); } public function drop(string $collectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/drop', [ 'collectionName' => $collectionName, 'dbName' => $dbName, ]); } public function getStats(string $collectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/get_stats', [ 'collectionName' => $collectionName, 'dbName' => $dbName, ]); } public function load(string $collectionName, array $options = []): array { $data = array_merge([ 'collectionName' => $collectionName, ], $options); return $this->client->post('/v2/vectordb/collections/load', $data); } public function release(string $collectionName, array $options = []): array { $data = array_merge([ 'collectionName' => $collectionName, ], $options); return $this->client->post('/v2/vectordb/collections/release', $data); } public function has(string $collectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/has', [ 'collectionName' => $collectionName, 'dbName' => $dbName, ]); } public function rename(string $oldCollectionName, string $newCollectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/rename', [ 'oldCollectionName' => $oldCollectionName, 'newCollectionName' => $newCollectionName, 'dbName' => $dbName, ]); } public function alter(string $collectionName, array $properties, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/collections/alter', [ 'collectionName' => $collectionName, 'properties' => $properties, 'dbName' => $dbName, ]); } } src/Api/Data.php000064400000004777151567466610007466 0ustar00client = $client; } public function insert(string $collectionName, array $data, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'data' => $data, ], $options); return $this->client->post('/v2/vectordb/entities/insert', $payload); } public function upsert(string $collectionName, array $data, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'data' => $data, ], $options); return $this->client->post('/v2/vectordb/entities/upsert', $payload); } public function delete(string $collectionName, array $filter, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'filter' => $filter, ], $options); return $this->client->post('/v2/vectordb/entities/delete', $payload); } public function get(string $collectionName, array $ids, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'ids' => $ids, ], $options); return $this->client->post('/v2/vectordb/entities/get', $payload); } public function query(string $collectionName, string $filter, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'filter' => $filter, ], $options); return $this->client->post('/v2/vectordb/entities/query', $payload); } public function search(string $collectionName, array $vectors, string $vectorField, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'data' => $vectors, 'annsField' => $vectorField, ], $options); return $this->client->post('/v2/vectordb/entities/search', $payload); } public function hybridSearch(string $collectionName, array $searchRequests, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'searchRequests' => $searchRequests, ], $options); return $this->client->post('/v2/vectordb/entities/hybrid_search', $payload); } } src/Api/Index.php000064400000002761151567466610007653 0ustar00client = $client; } public function create(string $collectionName, string $fieldName, array $indexParams, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'fieldName' => $fieldName, 'indexParams' => $indexParams, ], $options); return $this->client->post('/v2/vectordb/indexes/create', $payload); } public function list(string $collectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/indexes/list', [ 'collectionName' => $collectionName, 'dbName' => $dbName, ]); } public function describe(string $collectionName, string $indexName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/indexes/describe', [ 'collectionName' => $collectionName, 'indexName' => $indexName, 'dbName' => $dbName, ]); } public function drop(string $collectionName, string $indexName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/indexes/drop', [ 'collectionName' => $collectionName, 'indexName' => $indexName, 'dbName' => $dbName, ]); } } src/Api/Partition.php000064400000004750151567466610010555 0ustar00client = $client; } public function list(string $collectionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/partitions/list', [ 'collectionName' => $collectionName, 'dbName' => $dbName, ]); } public function create(string $collectionName, string $partitionName, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'partitionName' => $partitionName, ], $options); return $this->client->post('/v2/vectordb/partitions/create', $payload); } public function has(string $collectionName, string $partitionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/partitions/has', [ 'collectionName' => $collectionName, 'partitionName' => $partitionName, 'dbName' => $dbName, ]); } public function drop(string $collectionName, string $partitionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/partitions/drop', [ 'collectionName' => $collectionName, 'partitionName' => $partitionName, 'dbName' => $dbName, ]); } public function load(string $collectionName, array $partitionNames, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'partitionNames' => $partitionNames, ], $options); return $this->client->post('/v2/vectordb/partitions/load', $payload); } public function release(string $collectionName, array $partitionNames, array $options = []): array { $payload = array_merge([ 'collectionName' => $collectionName, 'partitionNames' => $partitionNames, ], $options); return $this->client->post('/v2/vectordb/partitions/release', $payload); } public function getStats(string $collectionName, string $partitionName, string $dbName = '_default'): array { return $this->client->post('/v2/vectordb/partitions/get_stats', [ 'collectionName' => $collectionName, 'partitionName' => $partitionName, 'dbName' => $dbName, ]); } } src/Client.php000064400000005354151567466610007312 0ustar00baseUrl = sprintf('%s://%s:%d', $scheme, $host, $port); $this->token = $token; $this->httpClient = new GuzzleClient([ 'base_uri' => $this->baseUrl, 'timeout' => $timeout, 'headers' => [ 'Accept' => 'application/json', 'Content-Type' => 'application/json', ], ]); } public function get(string $endpoint, array $params = []): array { return $this->request('GET', $endpoint, ['query' => $params]); } public function post(string $endpoint, array $data = []): array { return $this->request('POST', $endpoint, ['json' => $data]); } public function put(string $endpoint, array $data = []): array { return $this->request('PUT', $endpoint, ['json' => $data]); } public function delete(string $endpoint, array $params = []): array { return $this->request('DELETE', $endpoint, ['query' => $params]); } private function request(string $method, string $endpoint, array $options = []): array { $headers = []; if ($this->token !== null) { $headers['Authorization'] = 'Bearer ' . $this->token; } if (!empty($headers)) { $options['headers'] = array_merge($options['headers'] ?? [], $headers); } try { $response = $this->httpClient->request($method, $endpoint, $options); return $this->parseResponse($response); } catch (GuzzleException $e) { throw new Exception\RequestException( sprintf('Request failed: %s', $e->getMessage()), $e->getCode(), $e ); } } private function parseResponse(ResponseInterface $response): array { $body = (string) $response->getBody(); $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR); if ($response->getStatusCode() >= 400) { throw new Exception\ApiException( $data['message'] ?? 'API request failed', $response->getStatusCode() ); } return $data; } public function getBaseUrl(): string { return $this->baseUrl; } } src/Constants/DataType.php000064400000001324151567466610011554 0ustar00httpClient = new Client($host, $port, $token, $scheme, $timeout); $this->collections = new Collection($this->httpClient); $this->data = new Data($this->httpClient); $this->indexes = new Index($this->httpClient); $this->partitions = new Partition($this->httpClient); } public function getHttpClient(): Client { return $this->httpClient; } } src/Schema/CollectionSchema.php000064400000005604151567466610012506 0ustar00collectionName = $collectionName; } public static function create(string $collectionName): self { return new self($collectionName); } public function setDescription(string $description): self { $this->description = $description; return $this; } public function setEnableDynamicField(bool $enable): self { $this->enableDynamicField = $enable; return $this; } public function setDbName(?string $dbName): self { $this->dbName = $dbName; return $this; } public function setShardsNum(?int $shardsNum): self { $this->shardsNum = $shardsNum; return $this; } public function setConsistencyLevel(?int $consistencyLevel): self { $this->consistencyLevel = $consistencyLevel; return $this; } public function setPartitionsNum(?int $partitionsNum): self { $this->partitionsNum = $partitionsNum; return $this; } public function setTtlSeconds(?int $ttlSeconds): self { $this->ttlSeconds = $ttlSeconds; return $this; } public function addField(FieldSchema $field): self { $this->fields[] = $field; return $this; } public function addFields(array $fields): self { foreach ($fields as $field) { if ($field instanceof FieldSchema) { $this->fields[] = $field; } } return $this; } public function toArray(): array { $schema = [ 'description' => $this->description, 'enableDynamicField' => $this->enableDynamicField, 'fields' => array_map(fn($field) => $field->toArray(), $this->fields), ]; if ($this->shardsNum !== null) { $schema['shardsNum'] = $this->shardsNum; } if ($this->consistencyLevel !== null) { $schema['consistencyLevel'] = $this->consistencyLevel; } if ($this->partitionsNum !== null) { $schema['partitionsNum'] = $this->partitionsNum; } if ($this->ttlSeconds !== null) { $schema['ttlSeconds'] = $this->ttlSeconds; } return $schema; } public function getCollectionName(): string { return $this->collectionName; } public function getDbName(): ?string { return $this->dbName; } } src/Schema/FieldSchema.php000064400000007724151567466610011443 0ustar00fieldName = $fieldName; $this->dataType = $dataType; } public static function primaryKey(string $fieldName, string $dataType = 'Int64'): self { return (new self($fieldName, $dataType))->setPrimaryKey(true)->setAutoID(true); } public static function vector(string $fieldName, int $dim, string $dataType = 'FloatVector'): self { return (new self($fieldName, $dataType))->setDim($dim); } public static function scalar(string $fieldName, string $dataType): self { return new self($fieldName, $dataType); } public function setPrimaryKey(bool $isPrimary): self { $this->isPrimary = $isPrimary; return $this; } public function setAutoID(bool $autoID): self { $this->autoID = $autoID; return $this; } public function setDescription(?string $description): self { $this->description = $description; return $this; } public function setDim(?int $dim): self { $this->dim = $dim; return $this; } public function setMaxLength(?int $maxLength): self { $this->maxLength = $maxLength; return $this; } public function setMaxCapacity(?int $maxCapacity): self { $this->maxCapacity = $maxCapacity; return $this; } public function setElementDataType(?string $elementDataType): self { $this->elementDataType = $elementDataType; return $this; } public function setPartitionKey(bool $isPartitionKey): self { $this->isPartitionKey = $isPartitionKey; return $this; } public function setElementTypeParams(?array $elementTypeParams): self { $this->elementTypeParams = $elementTypeParams; return $this; } public function setIndexParams(?array $indexParams): self { $this->indexParams = $indexParams; return $this; } public function toArray(): array { $field = [ 'fieldName' => $this->fieldName, 'dataType' => $this->dataType, 'isPrimary' => $this->isPrimary, 'autoID' => $this->autoID, ]; if ($this->description !== null) { $field['description'] = $this->description; } if ($this->dim !== null) { $field['dim'] = $this->dim; } if ($this->maxLength !== null) { $field['elementTypeParams'] = array_merge( $field['elementTypeParams'] ?? [], ['max_length' => $this->maxLength] ); } if ($this->maxCapacity !== null) { $field['elementTypeParams'] = array_merge( $field['elementTypeParams'] ?? [], ['max_capacity' => $this->maxCapacity] ); } if ($this->elementDataType !== null) { $field['elementDataType'] = $this->elementDataType; } if ($this->isPartitionKey) { $field['isPartitionKey'] = true; } if ($this->elementTypeParams !== null) { $field['elementTypeParams'] = array_merge( $field['elementTypeParams'] ?? [], $this->elementTypeParams ); } if ($this->indexParams !== null) { $field['indexParams'] = $this->indexParams; } return $field; } } tests/ClientTest.php000064400000002347151567466610010524 0ustar00client = new Client('localhost', 19530); } public function testClientInstantiation(): void { $this->assertInstanceOf(Client::class, $this->client); } public function testClientWithToken(): void { $client = new Client('localhost', 19530, 'test-token'); $this->assertInstanceOf(Client::class, $client); } public function testGetBaseUrl(): void { $baseUrl = $this->client->getBaseUrl(); $this->assertStringContainsString('localhost', $baseUrl); } public function testClientWithCustomScheme(): void { $client = new Client('localhost', 19530, null, 'https'); $baseUrl = $client->getBaseUrl(); $this->assertStringContainsString('https', $baseUrl); } public function testClientWithCustomTimeout(): void { $client = new Client('localhost', 19530, null, 'http', 60.0); $this->assertInstanceOf(Client::class, $client); } } tests/Schema/CollectionSchemaTest.php000064400000005613151567466610013721 0ustar00toArray(); $this->assertEquals('test_collection', $array['collection_name']); $this->assertEquals('', $array['description']); $this->assertFalse($array['enable_dynamic_field']); $this->assertIsArray($array['fields']); } public function testCollectionSchemaWithDescription(): void { $schema = CollectionSchema::create('test_collection') ->setDescription('Test collection for unit testing'); $array = $schema->toArray(); $this->assertEquals('Test collection for unit testing', $array['description']); } public function testCollectionSchemaWithDynamicFields(): void { $schema = CollectionSchema::create('test_collection') ->setEnableDynamicField(true); $array = $schema->toArray(); $this->assertTrue($array['enable_dynamic_field']); } public function testCollectionSchemaWithFields(): void { $schema = CollectionSchema::create('test_collection') ->addField(FieldSchema::primaryKey('id')) ->addField(FieldSchema::scalar('title', DataType::STRING)) ->addField(FieldSchema::vector('embedding', 128)); $array = $schema->toArray(); $this->assertCount(3, $array['fields']); } public function testCollectionSchemaWithMultipleFields(): void { $fields = [ FieldSchema::primaryKey('id'), FieldSchema::scalar('title', DataType::STRING), FieldSchema::vector('embedding', 128), ]; $schema = CollectionSchema::create('test_collection') ->addFields($fields); $array = $schema->toArray(); $this->assertCount(3, $array['fields']); } public function testCompleteCollectionSchema(): void { $schema = CollectionSchema::create('products') ->setDescription('Product embeddings collection') ->setEnableDynamicField(true) ->addField(FieldSchema::primaryKey('id')) ->addField(FieldSchema::scalar('name', DataType::VARCHAR)->setMaxLength(255)) ->addField(FieldSchema::scalar('price', DataType::FLOAT)) ->addField(FieldSchema::vector('embedding', 128)); $array = $schema->toArray(); $this->assertEquals('products', $array['collection_name']); $this->assertEquals('Product embeddings collection', $array['description']); $this->assertTrue($array['enable_dynamic_field']); $this->assertCount(4, $array['fields']); } } tests/Schema/FieldSchemaTest.php000064400000004230151567466610012643 0ustar00toArray(); $this->assertEquals('title', $array['field_name']); $this->assertEquals(DataType::STRING, $array['data_type']); $this->assertFalse($array['is_primary_key']); $this->assertFalse($array['autoID']); } public function testCreateVectorField(): void { $field = FieldSchema::vector('embedding', 128, DataType::FLOAT_VECTOR); $array = $field->toArray(); $this->assertEquals('embedding', $array['field_name']); $this->assertEquals(DataType::FLOAT_VECTOR, $array['data_type']); $this->assertEquals(128, $array['dim']); } public function testCreatePrimaryKeyField(): void { $field = FieldSchema::primaryKey('id', DataType::INT64); $array = $field->toArray(); $this->assertEquals('id', $array['field_name']); $this->assertEquals(DataType::INT64, $array['data_type']); $this->assertTrue($array['is_primary_key']); $this->assertTrue($array['autoID']); } public function testFieldWithDescription(): void { $field = FieldSchema::scalar('description', DataType::VARCHAR) ->setDescription('Product description'); $array = $field->toArray(); $this->assertEquals('Product description', $array['description']); } public function testVarcharFieldWithMaxLength(): void { $field = FieldSchema::scalar('name', DataType::VARCHAR) ->setMaxLength(255); $array = $field->toArray(); $this->assertEquals(255, $array['max_length']); } public function testArrayFieldWithMaxCapacity(): void { $field = FieldSchema::scalar('tags', DataType::ARRAY) ->setMaxCapacity(100); $array = $field->toArray(); $this->assertEquals(100, $array['max_capacity']); } }