mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
1049 lines
45 KiB
PHP
1049 lines
45 KiB
PHP
<?php
|
|
|
|
namespace Tests\E2E\Services\Databases\VectorsDB;
|
|
|
|
use PHPUnit\Framework\Attributes\Depends;
|
|
use Tests\E2E\Client;
|
|
use Utopia\Database\Database;
|
|
use Utopia\Database\Helpers\ID;
|
|
use Utopia\Database\Helpers\Permission;
|
|
use Utopia\Database\Helpers\Role;
|
|
use Utopia\Database\Query;
|
|
|
|
trait DatabasesBase
|
|
{
|
|
public function testCreateDatabase(): array
|
|
{
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$database = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => 'Test Database'
|
|
]);
|
|
|
|
$this->assertNotEmpty($database['body']['$id']);
|
|
$this->assertEquals(201, $database['headers']['status-code']);
|
|
$this->assertEquals('Test Database', $database['body']['name']);
|
|
$this->assertEquals('vectorsdb', $database['body']['type']);
|
|
|
|
return ['databaseId' => $database['body']['$id']];
|
|
}
|
|
|
|
#[Depends('testCreateCollectionSample')]
|
|
public function testCreateDocument(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
|
|
// Build embedding vector matching collection dimensions (1536)
|
|
$vector = array_fill(0, 1536, 0.1);
|
|
$vector[0] = 1.0;
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'embeddings' => $vector,
|
|
'metadata' => ['type' => 'sample', 'rank' => 1]
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
Permission::update(Role::any()),
|
|
Permission::delete(Role::any()),
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals(201, $res['headers']['status-code']);
|
|
$this->assertNotEmpty($res['body']['$id']);
|
|
$documentId = $res['body']['$id'];
|
|
|
|
// createdAt/updatedAt should be present and equal on initial create
|
|
$this->assertArrayHasKey('$createdAt', $res['body']);
|
|
$this->assertArrayHasKey('$updatedAt', $res['body']);
|
|
$this->assertNotEmpty($res['body']['$createdAt']);
|
|
$this->assertNotEmpty($res['body']['$updatedAt']);
|
|
$this->assertEquals($res['body']['$createdAt'], $res['body']['$updatedAt']);
|
|
|
|
// Edge: invalid dimensions (vector too short) → expect 4xx
|
|
$badVec = [1.0, 0.0];
|
|
$bad = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'embeddings' => $badVec,
|
|
'metadata' => ['type' => 'bad']
|
|
],
|
|
]);
|
|
$this->assertGreaterThanOrEqual(400, $bad['headers']['status-code']);
|
|
$this->assertLessThan(500, $bad['headers']['status-code']);
|
|
|
|
// Edge: invalid type values (strings) → expect 4xx
|
|
$strVec = ['1.0', '0.0', '0.0'];
|
|
$bad2 = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'embeddings' => $strVec,
|
|
'metadata' => ['type' => 'bad-strings']
|
|
],
|
|
]);
|
|
$this->assertGreaterThanOrEqual(400, $bad2['headers']['status-code']);
|
|
$this->assertLessThan(500, $bad2['headers']['status-code']);
|
|
|
|
// Create another valid doc to verify list totals later
|
|
$res2 = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'embeddings' => $vector,
|
|
'metadata' => ['type' => 'sample', 'rank' => 99]
|
|
],
|
|
'permissions' => [Permission::read(Role::any())]
|
|
]);
|
|
$this->assertEquals(201, $res2['headers']['status-code']);
|
|
$documentId2 = $res2['body']['$id'];
|
|
|
|
return [
|
|
'databaseId' => $databaseId,
|
|
'collectionId' => $collectionId,
|
|
'documentId' => $documentId,
|
|
'documentId2' => $documentId2,
|
|
'createdAt' => $res['body']['$createdAt'],
|
|
'updatedAt' => $res['body']['$updatedAt'],
|
|
];
|
|
}
|
|
|
|
#[Depends('testCreateDocument')]
|
|
public function testGetDocument(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
$documentId = $data['documentId'];
|
|
|
|
$res = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
|
|
$this->assertEquals(200, $res['headers']['status-code']);
|
|
$this->assertEquals($documentId, $res['body']['$id']);
|
|
|
|
// Edge: missing document should return 404
|
|
$missing = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/" . ID::unique(), [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(404, $missing['headers']['status-code']);
|
|
|
|
return $data;
|
|
}
|
|
|
|
#[Depends('testCreateDocument')]
|
|
public function testListDocuments(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
|
|
$list = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [Query::limit(5)->toString()]
|
|
]);
|
|
|
|
$this->assertEquals(200, $list['headers']['status-code']);
|
|
$this->assertIsInt($list['body']['total']);
|
|
$this->assertGreaterThanOrEqual(1, $list['body']['total']);
|
|
|
|
// Pagination: limit 1, then offset 1
|
|
$page1 = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::limit(1)->toString(),
|
|
Query::orderAsc('$id')->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $page1['headers']['status-code']);
|
|
$this->assertEquals(1, \count($page1['body']['documents'] ?? []));
|
|
|
|
$page2 = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::limit(1)->toString(),
|
|
Query::offset(1)->toString(),
|
|
Query::orderAsc('$id')->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $page2['headers']['status-code']);
|
|
$this->assertEquals(1, \count($page2['body']['documents'] ?? []));
|
|
|
|
return $data;
|
|
}
|
|
|
|
#[Depends('testCreateDocument')]
|
|
public function testUpsertDocument(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
$documentId = $data['documentId'];
|
|
|
|
$vector = array_fill(0, 1536, 0.0);
|
|
// $vector[1] = 1.0;
|
|
|
|
$upd = $this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'data' => [
|
|
'embeddings' => $vector,
|
|
'metadata' => ['type' => 'sample', 'rank' => 2]
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals(200, $upd['headers']['status-code']);
|
|
|
|
// Verify update took effect
|
|
$get = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(200, $get['headers']['status-code']);
|
|
$this->assertEquals(2, $get['body']['metadata']['rank']);
|
|
// updatedAt should be greater or changed from earlier
|
|
$this->assertArrayHasKey('$updatedAt', $get['body']);
|
|
|
|
return $data;
|
|
}
|
|
|
|
#[Depends('testUpsertDocument')]
|
|
public function testUpdateDocument(array $data): array
|
|
{
|
|
// Upsert is used for update semantics
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
$documentId = $data['documentId'];
|
|
|
|
$vector = array_fill(0, 1536, 0.0);
|
|
$vector[2] = 1.0;
|
|
|
|
$upd = $this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'data' => [
|
|
'embeddings' => $vector,
|
|
'metadata' => ['type' => 'sample', 'rank' => 3]
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals(200, $upd['headers']['status-code']);
|
|
|
|
// Re-update to check idempotence and metadata replacement
|
|
$vector2 = array_fill(0, 1536, 0.0);
|
|
$vector2[3] = 1.0;
|
|
$upd2 = $this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'data' => [
|
|
'embeddings' => $vector2,
|
|
'metadata' => ['type' => 'sample', 'rank' => 4]
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $upd2['headers']['status-code']);
|
|
|
|
// Verify updatedAt changed again
|
|
$get2 = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(200, $get2['headers']['status-code']);
|
|
$this->assertArrayHasKey('$updatedAt', $get2['body']);
|
|
|
|
return $data;
|
|
}
|
|
|
|
#[Depends('testUpdateDocument')]
|
|
public function testDocumentsVectorQueries(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
|
|
// Create two more documents with distinct embeddings
|
|
$mk = function (array $vec, string $name) use ($databaseId, $collectionId) {
|
|
$this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'embeddings' => $vec,
|
|
'metadata' => ['name' => $name]
|
|
],
|
|
'permissions' => [Permission::read(Role::any())]
|
|
]);
|
|
};
|
|
|
|
$vA = array_fill(0, 1536, 0.0);
|
|
$vA[0] = 1.0; // close to [1,0,0,...]
|
|
$vB = array_fill(0, 1536, 0.0);
|
|
$vB[1] = 1.0; // close to [0,1,0,...]
|
|
|
|
$mk($vA, 'A');
|
|
$mk($vB, 'B');
|
|
|
|
// Dot product
|
|
$dot = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::vectorDot('embeddings', $vA)->toString(),
|
|
Query::limit(2)->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $dot['headers']['status-code']);
|
|
$this->assertGreaterThanOrEqual(1, $dot['body']['total']);
|
|
|
|
// Cosine
|
|
$cos = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::vectorCosine('embeddings', $vB)->toString(),
|
|
Query::limit(2)->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $cos['headers']['status-code']);
|
|
$this->assertGreaterThanOrEqual(1, $cos['body']['total']);
|
|
|
|
// Euclidean
|
|
$eu = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::vectorEuclidean('embeddings', $vA)->toString(),
|
|
Query::limit(2)->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $eu['headers']['status-code']);
|
|
$this->assertGreaterThanOrEqual(1, $eu['body']['total']);
|
|
|
|
// Combined vector + metadata filters
|
|
$combo = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::vectorCosine('embeddings', $vA)->toString(),
|
|
Query::notEqual('metadata', [['name' => 'B']])->toString(),
|
|
Query::limit(2)->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $combo['headers']['status-code']);
|
|
$this->assertGreaterThanOrEqual(1, $combo['body']['total']);
|
|
|
|
// Ordering with $id ascending combined with vector
|
|
$ordered = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::vectorDot('embeddings', $vA)->toString(),
|
|
Query::orderAsc('$id')->toString(),
|
|
Query::limit(3)->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals(200, $ordered['headers']['status-code']);
|
|
|
|
return $data;
|
|
}
|
|
|
|
#[Depends('testDocumentsVectorQueries')]
|
|
public function testDeleteDocument(array $data): void
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
$documentId = $data['documentId'];
|
|
|
|
$del = $this->client->call(Client::METHOD_DELETE, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(204, $del['headers']['status-code']);
|
|
|
|
// GET after delete should be 404
|
|
$getMissing = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$documentId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(404, $getMissing['headers']['status-code']);
|
|
|
|
// List should still work and reflect at least one less document compared to earlier pages (best-effort)
|
|
$list = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [Query::limit(5)->toString()]
|
|
]);
|
|
$this->assertEquals(200, $list['headers']['status-code']);
|
|
}
|
|
|
|
#[Depends('testCreateCollectionSample')]
|
|
public function testDocumentPermissions(array $data): void
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
|
|
// Create doc readable only by a specific user
|
|
$docId = ID::unique();
|
|
$vector = array_fill(0, 1536, 0.0);
|
|
$vector[0] = 1.0;
|
|
$create = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'documentId' => $docId,
|
|
'data' => [
|
|
'embeddings' => $vector,
|
|
'metadata' => ['scope' => 'private']
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::user($this->getUser()['$id']))
|
|
]
|
|
]);
|
|
$this->assertEquals(201, $create['headers']['status-code']);
|
|
|
|
$guest = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$docId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id']
|
|
]);
|
|
$this->assertEquals(404, $guest['headers']['status-code']);
|
|
|
|
// GET with key should succeed regardless of document user-level permission
|
|
$withKey = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$docId}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(200, $withKey['headers']['status-code']);
|
|
}
|
|
|
|
#[Depends('testCreateDatabase')]
|
|
public function testCreateCollection(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$movies = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'Movies',
|
|
'documentSecurity' => true,
|
|
'dimension' => 1536,
|
|
'permissions' => [
|
|
Permission::create(Role::user($this->getUser()['$id'])),
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals(201, $movies['headers']['status-code']);
|
|
$this->assertEquals($movies['body']['name'], 'Movies');
|
|
|
|
$actors = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'Actors',
|
|
'documentSecurity' => true,
|
|
'dimension' => 1536,
|
|
'permissions' => [
|
|
Permission::create(Role::user($this->getUser()['$id'])),
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals(201, $actors['headers']['status-code']);
|
|
$this->assertEquals($actors['body']['name'], 'Actors');
|
|
|
|
return [
|
|
'databaseId' => $databaseId,
|
|
'moviesId' => $movies['body']['$id'],
|
|
'actorsId' => $actors['body']['$id'],
|
|
];
|
|
}
|
|
|
|
public function testCreateDatabaseSample(): array
|
|
{
|
|
$database = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => 'Sample VectorsDB'
|
|
]);
|
|
|
|
$this->assertNotEmpty($database['body']['$id']);
|
|
$this->assertEquals(201, $database['headers']['status-code']);
|
|
$this->assertEquals('Sample VectorsDB', $database['body']['name']);
|
|
$this->assertEquals('vectorsdb', $database['body']['type']);
|
|
|
|
return ['databaseId' => $database['body']['$id']];
|
|
}
|
|
|
|
#[Depends('testCreateDatabaseSample')]
|
|
public function testCreateCollectionSample(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
|
|
$collection = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'Sample Collection',
|
|
'dimension' => 1536,
|
|
'documentSecurity' => true,
|
|
'permissions' => [
|
|
Permission::create(Role::user($this->getUser()['$id'])),
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals(201, $collection['headers']['status-code']);
|
|
$this->assertEquals('Sample Collection', $collection['body']['name']);
|
|
$this->assertEquals(1536, $collection['body']['dimension']);
|
|
|
|
return [
|
|
'databaseId' => $databaseId,
|
|
'collectionId' => $collection['body']['$id'],
|
|
];
|
|
}
|
|
|
|
public function testCreateMultipleDatabasesWithCollections(): array
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$apiKey = $this->getProject()['apiKey'];
|
|
$userId = $this->getUser()['$id'];
|
|
|
|
/**
|
|
* Helper to create a database
|
|
*/
|
|
$createDatabase = function (string $name) use ($projectId, $apiKey) {
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $apiKey
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => $name
|
|
]);
|
|
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$this->assertEquals('vectorsdb', $db['body']['type']);
|
|
$this->assertEquals($name, $db['body']['name']);
|
|
$this->assertNotEmpty($db['body']['$id']);
|
|
|
|
return $db['body']['$id'];
|
|
};
|
|
|
|
/**
|
|
* Helper to create a collection
|
|
*/
|
|
$createCollection = function (string $databaseId, string $name, int $dimensions = 1536) use ($projectId, $apiKey, $userId) {
|
|
$res = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $apiKey
|
|
], [
|
|
'collectionId' => ID::unique(),
|
|
'name' => $name,
|
|
'documentSecurity' => true,
|
|
'dimension' => $dimensions,
|
|
'permissions' => [
|
|
Permission::create(Role::user($userId)),
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals(201, $res['headers']['status-code']);
|
|
$this->assertEquals($name, $res['body']['name']);
|
|
|
|
return $res['body']['$id'];
|
|
};
|
|
|
|
/**
|
|
* === Database 1: MediaDB ===
|
|
*/
|
|
$mediaDbId = $createDatabase('MediaDB');
|
|
|
|
$mediaCollections = ['Movies', 'Actors', 'Directors'];
|
|
$mediaCollectionIds = [];
|
|
|
|
foreach ($mediaCollections as $col) {
|
|
$mediaCollectionIds[$col] = $createCollection($mediaDbId, $col);
|
|
}
|
|
|
|
/**
|
|
* === Database 2: ContentDB ===
|
|
*/
|
|
$contentDbId = $createDatabase('ContentDB');
|
|
|
|
$contentCollections = ['Articles', 'Authors'];
|
|
$contentCollectionIds = [];
|
|
|
|
foreach ($contentCollections as $col) {
|
|
$contentCollectionIds[$col] = $createCollection($contentDbId, $col);
|
|
}
|
|
|
|
// Create a tiny-dimension collection and insert a document to validate vector and object attributes
|
|
$tinyCollectionName = 'VectorsTiny';
|
|
$tinyDimensions = 8;
|
|
$tinyCollectionId = $createCollection($mediaDbId, $tinyCollectionName, $tinyDimensions);
|
|
|
|
return [
|
|
'databases' => [
|
|
'MediaDB' => [
|
|
'id' => $mediaDbId,
|
|
'collections' => $mediaCollectionIds + ['VectorsTiny' => $tinyCollectionId],
|
|
],
|
|
'ContentDB' => [
|
|
'id' => $contentDbId,
|
|
'collections' => $contentCollectionIds,
|
|
],
|
|
]
|
|
];
|
|
}
|
|
|
|
public function testInvalidCollectionDimensions(): void
|
|
{
|
|
// dimensions = 0 -> expect 4xx
|
|
$bad0 = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => 'BadDims0'
|
|
]);
|
|
$this->assertEquals(201, $bad0['headers']['status-code']);
|
|
$dbId = $bad0['body']['$id'];
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $dbId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'ZeroDims',
|
|
'documentSecurity' => true,
|
|
'dimension' => 0,
|
|
'permissions' => [Permission::create(Role::user($this->getUser()['$id']))],
|
|
]);
|
|
$this->assertGreaterThanOrEqual(400, $col['headers']['status-code']);
|
|
$this->assertLessThan(500, $col['headers']['status-code']);
|
|
|
|
// dimensions too large -> expect 4xx
|
|
$col2 = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $dbId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'HugeDims',
|
|
'documentSecurity' => true,
|
|
'dimension' => 16001,
|
|
'permissions' => [Permission::create(Role::user($this->getUser()['$id']))],
|
|
]);
|
|
$this->assertGreaterThanOrEqual(400, $col2['headers']['status-code']);
|
|
$this->assertLessThan(500, $col2['headers']['status-code']);
|
|
}
|
|
|
|
public function testSingleDimensionVectorCollection(): void
|
|
{
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => 'SingleDim'
|
|
]);
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$databaseId = $db['body']['$id'];
|
|
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'OneDim',
|
|
'documentSecurity' => true,
|
|
'dimension' => 1,
|
|
'permissions' => [Permission::create(Role::user($this->getUser()['$id']))],
|
|
]);
|
|
$this->assertEquals(201, $col['headers']['status-code']);
|
|
$collectionId = $col['body']['$id'];
|
|
|
|
// Create two docs with 1D embeddings
|
|
$id1 = ID::unique();
|
|
$this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$id1}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'data' => ['embeddings' => [1.0]]
|
|
]);
|
|
$id2 = ID::unique();
|
|
$this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/{$id2}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'data' => ['embeddings' => [0.5]]
|
|
]);
|
|
|
|
// Query with vectorCosine
|
|
$res = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [Query::vectorCosine('embeddings', [1.0])->toString(), Query::limit(2)->toString()]
|
|
]);
|
|
$this->assertEquals(200, $res['headers']['status-code']);
|
|
$this->assertGreaterThanOrEqual(1, $res['body']['total']);
|
|
}
|
|
|
|
public function testVectorInvalidValues(): void
|
|
{
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => 'InvalidVals'
|
|
]);
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$databaseId = $db['body']['$id'];
|
|
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'Docs',
|
|
'documentSecurity' => true,
|
|
'dimension' => 3,
|
|
'permissions' => [Permission::create(Role::user($this->getUser()['$id']))],
|
|
]);
|
|
$this->assertEquals(201, $col['headers']['status-code']);
|
|
$collectionId = $col['body']['$id'];
|
|
|
|
$badPayloads = [
|
|
['embeddings' => [INF, 0.0, 0.0]],
|
|
['embeddings' => [-INF, 0.0, 0.0]],
|
|
['embeddings' => [NAN, 0.0, 0.0]],
|
|
['embeddings' => ['x' => 1.0, 'y' => 0.0, 'z' => 0.0]],
|
|
['embeddings' => [1.0, null, 0.0]],
|
|
['embeddings' => [[1.0], [0.0], [0.0]]],
|
|
['embeddings' => [true, false, true]],
|
|
['embeddings' => [1.0, '2.0', 3.0]],
|
|
(function () {
|
|
$v = [];
|
|
$v[0] = 1.0;
|
|
$v[2] = 1.0;
|
|
return ['embeddings' => $v];
|
|
})(),
|
|
];
|
|
|
|
foreach ($badPayloads as $payload) {
|
|
$resp = $this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/" . ID::unique(), [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'data' => $payload
|
|
]);
|
|
$this->assertGreaterThanOrEqual(400, $resp['headers']['status-code']);
|
|
$this->assertLessThan(500, $resp['headers']['status-code']);
|
|
}
|
|
}
|
|
|
|
public function testVectorAllZerosAndQuery(): void
|
|
{
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'databaseId' => ID::unique(),
|
|
'name' => 'ZerosDB'
|
|
]);
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$databaseId = $db['body']['$id'];
|
|
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'collectionId' => ID::unique(),
|
|
'name' => 'Zeros',
|
|
'documentSecurity' => true,
|
|
'dimension' => 3,
|
|
'permissions' => [Permission::create(Role::user($this->getUser()['$id']))],
|
|
]);
|
|
$this->assertEquals(201, $col['headers']['status-code']);
|
|
$collectionId = $col['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/" . ID::unique(), [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'data' => ['embeddings' => [0.0, 0.0, 0.0]] ]);
|
|
|
|
$this->client->call(Client::METHOD_PUT, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents/" . ID::unique(), [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'data' => ['embeddings' => [1.0, 0.0, 0.0]] ]);
|
|
|
|
$results = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'queries' => [Query::vectorCosine('embeddings', [1.0, 0.0, 0.0])->toString()] ]);
|
|
$this->assertEquals(200, $results['headers']['status-code']);
|
|
$this->assertGreaterThan(0, $results['body']['total']);
|
|
}
|
|
|
|
public function testVectorMultipleQueriesRejection(): void
|
|
{
|
|
// Create a simple DB and collection
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'databaseId' => ID::unique(), 'name' => 'MultiQueryDB' ]);
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$databaseId = $db['body']['$id'];
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'collectionId' => ID::unique(), 'name' => 'Docs', 'documentSecurity' => true, 'dimension' => 3, 'permissions' => [Permission::create(Role::user($this->getUser()['$id']))] ]);
|
|
$this->assertEquals(201, $col['headers']['status-code']);
|
|
$collectionId = $col['body']['$id'];
|
|
|
|
// Two vector queries simultaneously should fail
|
|
$fail = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'queries' => [
|
|
Query::vectorCosine('embeddings', [1.0, 0.0, 0.0])->toString(),
|
|
Query::vectorEuclidean('embeddings', [1.0, 0.0, 0.0])->toString()
|
|
]
|
|
]);
|
|
$this->assertGreaterThanOrEqual(400, $fail['headers']['status-code']);
|
|
$this->assertLessThan(500, $fail['headers']['status-code']);
|
|
}
|
|
|
|
public function testVectorQueryOnNonVectorAttribute(): void
|
|
{
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'databaseId' => ID::unique(), 'name' => 'NonVec' ]);
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$databaseId = $db['body']['$id'];
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'collectionId' => ID::unique(), 'name' => 'Docs', 'documentSecurity' => true, 'dimension' => 3, 'permissions' => [Permission::create(Role::user($this->getUser()['$id']))] ]);
|
|
$this->assertEquals(201, $col['headers']['status-code']);
|
|
$collectionId = $col['body']['$id'];
|
|
|
|
// Query on non-vector attribute 'metadata' should fail
|
|
$fail = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'queries' => [Query::vectorCosine('metadata', [1.0, 0.0, 0.0])->toString()] ]);
|
|
$this->assertGreaterThanOrEqual(400, $fail['headers']['status-code']);
|
|
$this->assertLessThan(500, $fail['headers']['status-code']);
|
|
}
|
|
|
|
public function testVectorEmptyQueryCollection(): void
|
|
{
|
|
$db = $this->client->call(Client::METHOD_POST, '/vectorsdb', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'databaseId' => ID::unique(), 'name' => 'EmptyQ' ]);
|
|
$this->assertEquals(201, $db['headers']['status-code']);
|
|
$databaseId = $db['body']['$id'];
|
|
$col = $this->client->call(Client::METHOD_POST, '/vectorsdb/' . $databaseId . '/collections', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'collectionId' => ID::unique(), 'name' => 'Docs', 'documentSecurity' => true, 'dimension' => 3, 'permissions' => [Permission::create(Role::user($this->getUser()['$id']))] ]);
|
|
$this->assertEquals(201, $col['headers']['status-code']);
|
|
$collectionId = $col['body']['$id'];
|
|
|
|
$res = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/documents", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [ 'queries' => [Query::vectorCosine('embeddings', [1.0, 0.0, 0.0])->toString()] ]);
|
|
$this->assertEquals(200, $res['headers']['status-code']);
|
|
$this->assertEquals(0, $res['body']['total']);
|
|
}
|
|
|
|
#[Depends('testCreateCollection')]
|
|
public function testCreateIndexes(array $data): array
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['moviesId'];
|
|
|
|
// HNSW Euclidean
|
|
$idxEuclidean = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/indexes", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'key' => 'embedding_euclidean',
|
|
'type' => Database::INDEX_HNSW_EUCLIDEAN,
|
|
'attributes' => ['embeddings']
|
|
]);
|
|
$this->assertEquals(202, $idxEuclidean['headers']['status-code']);
|
|
|
|
// HNSW Dot (Inner Product)
|
|
$idxDot = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/indexes", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'key' => 'embedding_dot',
|
|
'type' => Database::INDEX_HNSW_DOT,
|
|
'attributes' => ['embeddings']
|
|
]);
|
|
$this->assertEquals(202, $idxDot['headers']['status-code']);
|
|
|
|
// HNSW Cosine
|
|
$idxCosine = $this->client->call(Client::METHOD_POST, "/vectorsdb/{$databaseId}/collections/{$collectionId}/indexes", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
], [
|
|
'key' => 'embedding_cosine',
|
|
'type' => Database::INDEX_HNSW_COSINE,
|
|
'attributes' => ['embeddings']
|
|
]);
|
|
$this->assertEquals(202, $idxCosine['headers']['status-code']);
|
|
|
|
return [
|
|
'databaseId' => $databaseId,
|
|
'collectionId' => $collectionId,
|
|
'indexes' => ['embedding_euclidean', 'embedding_dot', 'embedding_cosine']
|
|
];
|
|
}
|
|
|
|
#[Depends('testCreateIndexes')]
|
|
public function testListIndexes(array $data): void
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
|
|
$list = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/indexes", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
|
|
$this->assertEquals(200, $list['headers']['status-code']);
|
|
$keys = array_map(fn ($i) => $i['key'], $list['body']['indexes'] ?? []);
|
|
foreach ($data['indexes'] as $expectedKey) {
|
|
$this->assertContains($expectedKey, $keys);
|
|
}
|
|
}
|
|
|
|
#[Depends('testCreateIndexes')]
|
|
public function testGetIndexByKey(array $data): void
|
|
{
|
|
$databaseId = $data['databaseId'];
|
|
$collectionId = $data['collectionId'];
|
|
|
|
$keysToTypes = [
|
|
'embedding_euclidean' => Database::INDEX_HNSW_EUCLIDEAN,
|
|
'embedding_dot' => Database::INDEX_HNSW_DOT,
|
|
'embedding_cosine' => Database::INDEX_HNSW_COSINE,
|
|
];
|
|
|
|
foreach ($keysToTypes as $key => $type) {
|
|
$res = $this->client->call(Client::METHOD_GET, "/vectorsdb/{$databaseId}/collections/{$collectionId}/indexes/{$key}", [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]);
|
|
$this->assertEquals(200, $res['headers']['status-code']);
|
|
$this->assertEquals($key, $res['body']['key']);
|
|
$this->assertEquals($type, $res['body']['type']);
|
|
}
|
|
}
|
|
|
|
}
|