mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
updated migrations + index finding issue in vectordb
This commit is contained in:
+2
-2
@@ -54,8 +54,8 @@
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "dev-var_object as 1.4.5",
|
||||
"utopia-php/agents": "dev-feat-embeddings",
|
||||
"utopia-php/database": "3.*",
|
||||
"utopia-php/agents": "0.*",
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "0.9.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
|
||||
Generated
+78
-82
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f9b66abe46119ae12f0cf2becc8bc299",
|
||||
"content-hash": "a2f0965b1d70d23bbfc5a948dafabd1e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -161,16 +161,16 @@
|
||||
},
|
||||
{
|
||||
"name": "appwrite/php-runtimes",
|
||||
"version": "0.19.1",
|
||||
"version": "0.19.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae"
|
||||
"reference": "e5c142519df5aced37de9c302971c29c079ce3d9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/7bd0cc3cb97de625d7b07230bd91b121f88e72ae",
|
||||
"reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae",
|
||||
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/e5c142519df5aced37de9c302971c29c079ce3d9",
|
||||
"reference": "e5c142519df5aced37de9c302971c29c079ce3d9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -210,9 +210,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/runtimes/issues",
|
||||
"source": "https://github.com/appwrite/runtimes/tree/0.19.1"
|
||||
"source": "https://github.com/appwrite/runtimes/tree/0.19.2"
|
||||
},
|
||||
"time": "2025-05-27T07:12:56+00:00"
|
||||
"time": "2025-11-11T13:44:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "beberlei/assert",
|
||||
@@ -754,16 +754,16 @@
|
||||
},
|
||||
{
|
||||
"name": "google/protobuf",
|
||||
"version": "v4.33.0",
|
||||
"version": "v4.33.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/protocolbuffers/protobuf-php.git",
|
||||
"reference": "b50269e23204e5ae859a326ec3d90f09efe3047d"
|
||||
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/b50269e23204e5ae859a326ec3d90f09efe3047d",
|
||||
"reference": "b50269e23204e5ae859a326ec3d90f09efe3047d",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
|
||||
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -792,9 +792,9 @@
|
||||
"proto"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.0"
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.1"
|
||||
},
|
||||
"time": "2025-10-15T20:10:28+00:00"
|
||||
"time": "2025-11-12T21:58:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
@@ -2668,16 +2668,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.3.4",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62"
|
||||
"reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62",
|
||||
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de",
|
||||
"reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2744,7 +2744,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.4"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2764,7 +2764,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T10:12:26+00:00"
|
||||
"time": "2025-11-05T17:41:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@@ -3171,16 +3171,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.6.0",
|
||||
"version": "v3.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
|
||||
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
|
||||
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
|
||||
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3234,7 +3234,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3245,12 +3245,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-25T09:37:31+00:00"
|
||||
"time": "2025-07-15T11:30:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tbachert/spi",
|
||||
@@ -3496,16 +3500,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/agents",
|
||||
"version": "dev-feat-embeddings",
|
||||
"version": "0.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/agents.git",
|
||||
"reference": "b448a4d06374c7a538c9495ac69a1a8f8e526823"
|
||||
"reference": "7bbc77d4936ee7e87394a239690c9059b5cdaf1b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/agents/zipball/b448a4d06374c7a538c9495ac69a1a8f8e526823",
|
||||
"reference": "b448a4d06374c7a538c9495ac69a1a8f8e526823",
|
||||
"url": "https://api.github.com/repos/utopia-php/agents/zipball/7bbc77d4936ee7e87394a239690c9059b5cdaf1b",
|
||||
"reference": "7bbc77d4936ee7e87394a239690c9059b5cdaf1b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3544,9 +3548,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/agents/issues",
|
||||
"source": "https://github.com/utopia-php/agents/tree/feat-embeddings"
|
||||
"source": "https://github.com/utopia-php/agents/tree/0.5.0"
|
||||
},
|
||||
"time": "2025-11-06T07:53:14+00:00"
|
||||
"time": "2025-11-12T06:06:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
@@ -3889,16 +3893,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "dev-var_object",
|
||||
"version": "3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "9a0cea65abbfb325506cb1d619db60dac7ee7e55"
|
||||
"reference": "e10b4faa4f3a3ef30a5f6d76acdb605469924aec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/9a0cea65abbfb325506cb1d619db60dac7ee7e55",
|
||||
"reference": "9a0cea65abbfb325506cb1d619db60dac7ee7e55",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/e10b4faa4f3a3ef30a5f6d76acdb605469924aec",
|
||||
"reference": "e10b4faa4f3a3ef30a5f6d76acdb605469924aec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3941,9 +3945,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/var_object"
|
||||
"source": "https://github.com/utopia-php/database/tree/3.4.0"
|
||||
},
|
||||
"time": "2025-11-06T09:02:12+00:00"
|
||||
"time": "2025-11-13T06:34:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -4508,12 +4512,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "573aae2840e0727844b6614e57fd0fffbfc51518"
|
||||
"reference": "f5e175fa8da4bee6211dbeb4fe693a3eedb87672"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/573aae2840e0727844b6614e57fd0fffbfc51518",
|
||||
"reference": "573aae2840e0727844b6614e57fd0fffbfc51518",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f5e175fa8da4bee6211dbeb4fe693a3eedb87672",
|
||||
"reference": "f5e175fa8da4bee6211dbeb4fe693a3eedb87672",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4522,7 +4526,7 @@
|
||||
"ext-openssl": "*",
|
||||
"php": ">=8.1",
|
||||
"utopia-php/console": "0.0.*",
|
||||
"utopia-php/database": "dev-var_object as 1.4.5",
|
||||
"utopia-php/database": "3.*",
|
||||
"utopia-php/dsn": "0.2.*",
|
||||
"utopia-php/storage": "0.18.*"
|
||||
},
|
||||
@@ -4555,7 +4559,7 @@
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/feat-documents-db"
|
||||
},
|
||||
"time": "2025-10-28T15:06:08+00:00"
|
||||
"time": "2025-11-13T11:11:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
@@ -5427,16 +5431,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.5.1",
|
||||
"version": "1.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "cd712674e34136f706e9170641ed6f4ce160e772"
|
||||
"reference": "958947b6483a79e11c3812f23bb3056199fa4105"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/cd712674e34136f706e9170641ed6f4ce160e772",
|
||||
"reference": "cd712674e34136f706e9170641ed6f4ce160e772",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/958947b6483a79e11c3812f23bb3056199fa4105",
|
||||
"reference": "958947b6483a79e11c3812f23bb3056199fa4105",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5472,9 +5476,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.1"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.4"
|
||||
},
|
||||
"time": "2025-11-04T09:55:47+00:00"
|
||||
"time": "2025-11-12T12:43:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
@@ -6178,16 +6182,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpbench/phpbench",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpbench/phpbench.git",
|
||||
"reference": "bb61ae6c54b3d58642be154eb09f4e73c3511018"
|
||||
"reference": "b641dde59d969ea42eed70a39f9b51950bc96878"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpbench/phpbench/zipball/bb61ae6c54b3d58642be154eb09f4e73c3511018",
|
||||
"reference": "bb61ae6c54b3d58642be154eb09f4e73c3511018",
|
||||
"url": "https://api.github.com/repos/phpbench/phpbench/zipball/b641dde59d969ea42eed70a39f9b51950bc96878",
|
||||
"reference": "b641dde59d969ea42eed70a39f9b51950bc96878",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6202,26 +6206,26 @@
|
||||
"phpbench/container": "^2.2",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
"seld/jsonlint": "^1.1",
|
||||
"symfony/console": "^6.1 || ^7.0",
|
||||
"symfony/filesystem": "^6.1 || ^7.0",
|
||||
"symfony/finder": "^6.1 || ^7.0",
|
||||
"symfony/options-resolver": "^6.1 || ^7.0",
|
||||
"symfony/process": "^6.1 || ^7.0",
|
||||
"symfony/console": "^6.1 || ^7.0 || ^8.0",
|
||||
"symfony/filesystem": "^6.1 || ^7.0 || ^8.0",
|
||||
"symfony/finder": "^6.1 || ^7.0 || ^8.0",
|
||||
"symfony/options-resolver": "^6.1 || ^7.0 || ^8.0",
|
||||
"symfony/process": "^6.1 || ^7.0 || ^8.0",
|
||||
"webmozart/glob": "^4.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"dantleech/invoke": "^2.0",
|
||||
"ergebnis/composer-normalize": "^2.39",
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"jangregor/phpstan-prophecy": "^1.0",
|
||||
"php-cs-fixer/shim": "^3.9",
|
||||
"phpspec/prophecy": "^1.22",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^10.4 || ^11.0",
|
||||
"rector/rector": "^1.2",
|
||||
"symfony/error-handler": "^6.1 || ^7.0",
|
||||
"symfony/var-dumper": "^6.1 || ^7.0"
|
||||
"symfony/error-handler": "^6.1 || ^7.0 || ^8.0",
|
||||
"symfony/var-dumper": "^6.1 || ^7.0 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-xdebug": "For Xdebug profiling extension."
|
||||
@@ -6264,7 +6268,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpbench/phpbench/issues",
|
||||
"source": "https://github.com/phpbench/phpbench/tree/1.4.2"
|
||||
"source": "https://github.com/phpbench/phpbench/tree/1.4.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6272,7 +6276,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-26T14:21:59+00:00"
|
||||
"time": "2025-11-06T19:07:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
@@ -7921,16 +7925,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.3.5",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7"
|
||||
"reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7",
|
||||
"reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a",
|
||||
"reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7995,7 +7999,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.5"
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8015,20 +8019,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-14T15:46:26+00:00"
|
||||
"time": "2025-11-04T01:21:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd"
|
||||
"reference": "e9bcfd7837928ab656276fe00464092cc9e1826a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd",
|
||||
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e9bcfd7837928ab656276fe00464092cc9e1826a",
|
||||
"reference": "e9bcfd7837928ab656276fe00464092cc9e1826a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -8065,7 +8069,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8085,7 +8089,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-07T08:17:47+00:00"
|
||||
"time": "2025-11-05T09:52:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
@@ -8940,12 +8944,6 @@
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
{
|
||||
"package": "utopia-php/database",
|
||||
"version": "dev-var_object",
|
||||
"alias": "1.4.5",
|
||||
"alias_normalized": "1.4.5.0"
|
||||
},
|
||||
{
|
||||
"package": "utopia-php/migration",
|
||||
"version": "dev-feat-documents-db",
|
||||
@@ -8955,8 +8953,6 @@
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"utopia-php/agents": 20,
|
||||
"utopia-php/database": 20,
|
||||
"utopia-php/migration": 20
|
||||
},
|
||||
"prefer-stable": true,
|
||||
|
||||
@@ -28,4 +28,4 @@ const DOCUMENTSDB = 'documentsdb';
|
||||
const VECTORDB = 'vectordb';
|
||||
|
||||
const MIN_VECTOR_DIMENSION = 1;
|
||||
const MAX_VECTOR_DIMENSION = 16000;
|
||||
const MAX_VECTOR_DIMENSION = 16000;
|
||||
|
||||
@@ -140,8 +140,8 @@ class Create extends CollectionAction
|
||||
attributes:$attributes,
|
||||
indexes:$indexes
|
||||
);
|
||||
// Create attribute metadata documents in the attributes table
|
||||
// This is necessary so that indexes can find the attributes when they're created
|
||||
// Create attribute and indexes metadata documents in the attributes and indexes collections
|
||||
// needed for the get and list calls
|
||||
$attributeDocs = array_map(function ($attributeConfig) use ($database, $collection, $databaseId, $collectionId, $dimensions) {
|
||||
$key = \is_string($attributeConfig['$id']) ? $attributeConfig['$id'] : (string) $attributeConfig['$id'];
|
||||
return new Document([
|
||||
@@ -165,6 +165,28 @@ class Create extends CollectionAction
|
||||
]);
|
||||
}, $collections['defaultAttributes']);
|
||||
$dbForProject->createDocuments('attributes', $attributeDocs);
|
||||
|
||||
$indexDocs = array_map(function ($indexConfig) use ($database, $collection, $databaseId, $collectionId) {
|
||||
$key = \is_string($indexConfig['$id']) ? $indexConfig['$id'] : (string) $indexConfig['$id'];
|
||||
|
||||
return new Document([
|
||||
'$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $key),
|
||||
'key' => $key,
|
||||
'status' => 'available',
|
||||
'databaseInternalId' => $database->getSequence(),
|
||||
'databaseId' => $databaseId,
|
||||
'collectionInternalId' => $collection->getSequence(),
|
||||
'collectionId' => $collectionId,
|
||||
'type' => $indexConfig['type'],
|
||||
'attributes' => $indexConfig['attributes'] ?? [],
|
||||
'lengths' => $indexConfig['lengths'] ?? [],
|
||||
'orders' => $indexConfig['orders'] ?? [],
|
||||
]);
|
||||
}, $collections['defaultIndexes']);
|
||||
|
||||
if (!empty($indexDocs)) {
|
||||
$dbForProject->createDocuments('indexes', $indexDocs);
|
||||
}
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception($this->getDuplicateException());
|
||||
} catch (IndexException) {
|
||||
|
||||
@@ -450,6 +450,7 @@ class Migrations extends Action
|
||||
{
|
||||
return match ($databaseType) {
|
||||
'documentsdb' => $this->project->getAttribute('documentsDatabase'),
|
||||
'vectordb' => $this->project->getAttribute('vectorDatabase'),
|
||||
default => $this->project->getAttribute('database'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use Tests\E2E\Client;
|
||||
use Tests\E2E\General\UsageTest;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Services\Functions\FunctionsBase;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -82,6 +83,26 @@ trait MigrationsBase
|
||||
return $migrationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get migration status by ID (without creating a new migration)
|
||||
*
|
||||
* @param string $migrationId
|
||||
* @return array
|
||||
*/
|
||||
public function getMigrationStatus(string $migrationId): array
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_GET, '/migrations/' . $migrationId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
return $response['body'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Appwrite E2E Migration Tests
|
||||
*/
|
||||
@@ -1494,6 +1515,200 @@ trait MigrationsBase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* VectorDB (embeddings collections)
|
||||
*/
|
||||
public function testAppwriteMigrationVectorDBDatabase(): array
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_POST, '/vectordb', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'VDB - Migration DB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
|
||||
$databaseId = $response['body']['$id'];
|
||||
|
||||
$result = $this->performMigrationSync([
|
||||
'resources' => [
|
||||
Resource::TYPE_DATABASE_VECTORDB,
|
||||
],
|
||||
'endpoint' => 'http://localhost/v1',
|
||||
'projectId' => $this->getProject()['$id'],
|
||||
'apiKey' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals('completed', $result['status']);
|
||||
$this->assertEquals([Resource::TYPE_DATABASE_VECTORDB], $result['resources']);
|
||||
$this->assertArrayHasKey(Resource::TYPE_DATABASE_VECTORDB, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_VECTORDB]['error'] ?? 0);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertEquals($databaseId, $response['body']['$id']);
|
||||
$this->assertEquals('VDB - Migration DB', $response['body']['name']);
|
||||
|
||||
// Cleanup on destination
|
||||
$this->client->call(Client::METHOD_DELETE, '/vectordb/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAppwriteMigrationVectorDBDatabase
|
||||
*/
|
||||
public function testAppwriteMigrationVectorDBCollection(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/vectordb/' . $databaseId . '/collections', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'VDB - Movies',
|
||||
'dimensions' => 3,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
$result = $this->performMigrationSync([
|
||||
'resources' => [
|
||||
Resource::TYPE_DATABASE_VECTORDB,
|
||||
Resource::TYPE_COLLECTION,
|
||||
Resource::TYPE_ATTRIBUTE,
|
||||
],
|
||||
'endpoint' => 'http://localhost/v1',
|
||||
'projectId' => $this->getProject()['$id'],
|
||||
'apiKey' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
$this->assertEquals('completed', $result['status']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $databaseId . '/collections/' . $collectionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEquals($collectionId, $response['body']['$id']);
|
||||
$this->assertEquals('VDB - Movies', $response['body']['name']);
|
||||
// Verify attributes are present (embeddings and metadata are default attributes)
|
||||
$this->assertArrayHasKey('attributes', $response['body']);
|
||||
$this->assertIsArray($response['body']['attributes']);
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/vectordb/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAppwriteMigrationVectorDBCollection
|
||||
*/
|
||||
public function testAppwriteMigrationVectorDBDocument(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
$collectionId = $data['collectionId'];
|
||||
|
||||
$document = $this->client->call(Client::METHOD_POST, '/vectordb/' . $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' => [1.0, 0.0, 0.0],
|
||||
'metadata' => ['title' => 'Migration Test Movie'],
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document['headers']['status-code']);
|
||||
$documentId = $document['body']['$id'];
|
||||
|
||||
// Ensure attributes are exported before documents
|
||||
$result = $this->performMigrationSync([
|
||||
'resources' => [
|
||||
Resource::TYPE_DATABASE_VECTORDB,
|
||||
Resource::TYPE_COLLECTION,
|
||||
Resource::TYPE_ATTRIBUTE,
|
||||
Resource::TYPE_DOCUMENT,
|
||||
],
|
||||
'endpoint' => 'http://localhost/v1',
|
||||
'projectId' => $this->getProject()['$id'],
|
||||
'apiKey' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals('completed', $result['status']);
|
||||
// Verify that TYPE_ATTRIBUTE appears in the resources array for VectorDB
|
||||
$this->assertContains(Resource::TYPE_ATTRIBUTE, $result['resources'], 'TYPE_ATTRIBUTE should be in resources array for VectorDB');
|
||||
|
||||
// Verify attributes exist on destination before checking document
|
||||
$collectionResponse = $this->client->call(Client::METHOD_GET, '/vectordb/' . $databaseId . '/collections/' . $collectionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $collectionResponse['headers']['status-code']);
|
||||
$this->assertArrayHasKey('attributes', $collectionResponse['body']);
|
||||
$this->assertIsArray($collectionResponse['body']['attributes']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEquals($documentId, $response['body']['$id']);
|
||||
$this->assertEquals('Migration Test Movie', $response['body']['metadata']['title']);
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/vectordb/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_DELETE, '/vectordb/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAppwriteMigrationDocumentsDBDatabase
|
||||
*/
|
||||
@@ -1688,6 +1903,31 @@ trait MigrationsBase
|
||||
$this->assertEquals('available', $response['body']['status']);
|
||||
}, 5000, 500);
|
||||
|
||||
$sqlIndexKey = 'product_unique';
|
||||
|
||||
$sqlIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $sqlDatabaseId . '/tables/' . $tableId . '/indexes', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
], [
|
||||
'key' => $sqlIndexKey,
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'columns' => ['productName'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $sqlIndex['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($sqlDatabaseId, $tableId, $sqlIndexKey, $sourceProject) {
|
||||
$index = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $sqlDatabaseId . '/tables/' . $tableId . '/indexes/' . $sqlIndexKey, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $index['headers']['status-code']);
|
||||
$this->assertEquals('available', $index['body']['status']);
|
||||
}, 30000, 500);
|
||||
|
||||
// Create Row in Table
|
||||
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $sqlDatabaseId . '/tables/' . $tableId . '/rows', [
|
||||
'content-type' => 'application/json',
|
||||
@@ -1730,6 +1970,31 @@ trait MigrationsBase
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
$documentsIndexKey = 'email_unique';
|
||||
|
||||
$documentsIndex = $this->client->call(Client::METHOD_POST, '/documentsdb/' . $docsDatabaseId . '/collections/' . $collectionId . '/indexes', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
], [
|
||||
'key' => $documentsIndexKey,
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['email'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $documentsIndex['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($docsDatabaseId, $collectionId, $documentsIndexKey, $sourceProject) {
|
||||
$index = $this->client->call(Client::METHOD_GET, '/documentsdb/' . $docsDatabaseId . '/collections/' . $collectionId . '/indexes/' . $documentsIndexKey, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $index['headers']['status-code']);
|
||||
$this->assertEquals('available', $index['body']['status']);
|
||||
}, 30000, 500);
|
||||
|
||||
// Create Document in Collection
|
||||
$document = $this->client->call(Client::METHOD_POST, '/documentsdb/' . $docsDatabaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
@@ -1746,8 +2011,119 @@ trait MigrationsBase
|
||||
$this->assertEquals(201, $document['headers']['status-code']);
|
||||
$documentId = $document['body']['$id'];
|
||||
|
||||
// ====== Perform migration including both database kinds with all child resources ======
|
||||
$result = $this->performMigrationSync([
|
||||
// ====== Create VectorDB (/vectordb) with collection and document ======
|
||||
$vector = $this->client->call(Client::METHOD_POST, '/vectordb', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Mixed VectorDB',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $vector['headers']['status-code']);
|
||||
$this->assertNotEmpty($vector['body']['$id']);
|
||||
$vectorDatabaseId = $vector['body']['$id'];
|
||||
|
||||
// Create Collection in VectorDB
|
||||
$vectorCollection = $this->client->call(Client::METHOD_POST, '/vectordb/' . $vectorDatabaseId . '/collections', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
], [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Products',
|
||||
'dimensions' => 3,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $vectorCollection['headers']['status-code']);
|
||||
$vectorCollectionId = $vectorCollection['body']['$id'];
|
||||
|
||||
// Wait for VectorDB collection attributes to be ready
|
||||
$this->assertEventually(function () use ($vectorDatabaseId, $vectorCollectionId, $sourceProject) {
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertArrayHasKey('attributes', $response['body']);
|
||||
$this->assertIsArray($response['body']['attributes']);
|
||||
// Check that default attributes (embeddings and metadata) are present and ready
|
||||
$attributeKeys = array_column($response['body']['attributes'], 'key');
|
||||
$this->assertContains('embeddings', $attributeKeys);
|
||||
$this->assertContains('metadata', $attributeKeys);
|
||||
// Check that attributes are available (if status field exists)
|
||||
foreach ($response['body']['attributes'] as $attribute) {
|
||||
if (isset($attribute['status']) && $attribute['status'] !== 'available') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, 10000, 500);
|
||||
|
||||
$metadataIndexKey = '_key_metadata';
|
||||
$vectorIndexes = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId . '/indexes', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
$this->assertEquals(200, $vectorIndexes['headers']['status-code']);
|
||||
$metadataIndex = null;
|
||||
foreach ($vectorIndexes['body']['indexes'] ?? [] as $index) {
|
||||
if (($index['key'] ?? '') === $metadataIndexKey) {
|
||||
$metadataIndex = $index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertNotNull($metadataIndex, 'Default metadata index should exist on source collection');
|
||||
$this->assertEquals(Database::INDEX_OBJECT, $metadataIndex['type']);
|
||||
|
||||
$vectorEmbeddingIndexKey = 'embedding_euclidean';
|
||||
$vectorEmbeddingIndex = $this->client->call(Client::METHOD_POST, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId . '/indexes', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
], [
|
||||
'key' => $vectorEmbeddingIndexKey,
|
||||
'type' => Database::INDEX_HNSW_EUCLIDEAN,
|
||||
'attributes' => ['embeddings'],
|
||||
]);
|
||||
$this->assertEquals(202, $vectorEmbeddingIndex['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($vectorDatabaseId, $vectorCollectionId, $vectorEmbeddingIndexKey, $sourceProject) {
|
||||
$index = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId . '/indexes/' . $vectorEmbeddingIndexKey, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $index['headers']['status-code']);
|
||||
$this->assertEquals(Database::INDEX_HNSW_EUCLIDEAN, $index['body']['type']);
|
||||
if (isset($index['body']['status'])) {
|
||||
$this->assertEquals('available', $index['body']['status']);
|
||||
}
|
||||
}, 30000, 500);
|
||||
|
||||
// Create Document in VectorDB Collection
|
||||
$vectorDocument = $this->client->call(Client::METHOD_POST, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
], [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'embeddings' => [0.5, 0.3, 0.2],
|
||||
'metadata' => ['name' => 'Product Vector'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $vectorDocument['headers']['status-code']);
|
||||
$vectorDocumentId = $vectorDocument['body']['$id'];
|
||||
|
||||
// ====== Perform migration including all three database kinds with all child resources ======
|
||||
$migrationConfig = [
|
||||
'resources' => [
|
||||
Resource::TYPE_DATABASE,
|
||||
Resource::TYPE_TABLE,
|
||||
@@ -1756,13 +2132,18 @@ trait MigrationsBase
|
||||
Resource::TYPE_DATABASE_DOCUMENTSDB,
|
||||
Resource::TYPE_COLLECTION,
|
||||
Resource::TYPE_DOCUMENT,
|
||||
Resource::TYPE_DATABASE_VECTORDB,
|
||||
Resource::TYPE_ATTRIBUTE,
|
||||
Resource::TYPE_INDEX,
|
||||
],
|
||||
'endpoint' => 'http://localhost/v1',
|
||||
'projectId' => $sourceProject['$id'],
|
||||
'apiKey' => $sourceProject['apiKey'],
|
||||
]);
|
||||
];
|
||||
|
||||
// ====== Assert migration result ======
|
||||
// Perform migration sync once and get migration ID
|
||||
$result = $this->performMigrationSync($migrationConfig);
|
||||
$migrationId = $result['$id'];
|
||||
$this->assertEquals('completed', $result['status']);
|
||||
$this->assertEquals('Appwrite', $result['source']);
|
||||
$this->assertEquals('Appwrite', $result['destination']);
|
||||
@@ -1774,8 +2155,13 @@ trait MigrationsBase
|
||||
Resource::TYPE_DATABASE_DOCUMENTSDB,
|
||||
Resource::TYPE_COLLECTION,
|
||||
Resource::TYPE_DOCUMENT,
|
||||
Resource::TYPE_DATABASE_VECTORDB,
|
||||
Resource::TYPE_ATTRIBUTE,
|
||||
Resource::TYPE_INDEX,
|
||||
], $result['resources']);
|
||||
|
||||
// Get migration status before asserting SQL Database counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert SQL Database counters
|
||||
$this->assertArrayHasKey(Resource::TYPE_DATABASE, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE]['error']);
|
||||
@@ -1784,6 +2170,8 @@ trait MigrationsBase
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE]['warning']);
|
||||
|
||||
// Get migration status before asserting Table counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert Table counters
|
||||
$this->assertArrayHasKey(Resource::TYPE_TABLE, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TABLE]['error']);
|
||||
@@ -1792,6 +2180,8 @@ trait MigrationsBase
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TABLE]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TABLE]['warning']);
|
||||
|
||||
// Get migration status before asserting Column counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert Column counters
|
||||
$this->assertArrayHasKey(Resource::TYPE_COLUMN, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLUMN]['error']);
|
||||
@@ -1800,6 +2190,8 @@ trait MigrationsBase
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLUMN]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLUMN]['warning']);
|
||||
|
||||
// Get migration status before asserting Row counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert Row counters
|
||||
$this->assertArrayHasKey(Resource::TYPE_ROW, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ROW]['error']);
|
||||
@@ -1808,6 +2200,8 @@ trait MigrationsBase
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ROW]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ROW]['warning']);
|
||||
|
||||
// Get migration status before asserting DocumentsDB counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert DocumentsDB counters
|
||||
$this->assertArrayHasKey(Resource::TYPE_DATABASE_DOCUMENTSDB, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_DOCUMENTSDB]['error']);
|
||||
@@ -1816,24 +2210,75 @@ trait MigrationsBase
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_DOCUMENTSDB]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_DOCUMENTSDB]['warning']);
|
||||
|
||||
// Assert Collection counters
|
||||
// Wait for all collections to be fully processed and status counters to be updated
|
||||
// Note: Collections are being transferred but status counters may not be updated immediately
|
||||
// This wait ensures the migration worker has finished processing all collections
|
||||
$result = null;
|
||||
$this->assertEventually(function () use ($migrationId, &$result) {
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
|
||||
// Check if collections status counters exist
|
||||
if (!isset($result['statusCounters'][Resource::TYPE_COLLECTION])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$pendingCount = $result['statusCounters'][Resource::TYPE_COLLECTION]['pending'] ?? 0;
|
||||
|
||||
// Return true only when pending count is 0
|
||||
return $pendingCount === 0;
|
||||
}, 30000, 1000); // 30 second timeout, check every 1 second
|
||||
|
||||
// Assert Collection counters (covers both DocumentsDB and VectorDB collections)
|
||||
$this->assertArrayHasKey(Resource::TYPE_COLLECTION, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLLECTION]['error']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLLECTION]['pending']);
|
||||
$this->assertEquals(1, $result['statusCounters'][Resource::TYPE_COLLECTION]['success']);
|
||||
$this->assertGreaterThanOrEqual(1, $result['statusCounters'][Resource::TYPE_COLLECTION]['success']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLLECTION]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_COLLECTION]['warning']);
|
||||
|
||||
// Assert Document counters
|
||||
// Get migration status before asserting Document counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert Document counters (covers both DocumentsDB and VectorDB documents)
|
||||
$this->assertArrayHasKey(Resource::TYPE_DOCUMENT, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DOCUMENT]['error']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DOCUMENT]['pending']);
|
||||
$this->assertEquals(1, $result['statusCounters'][Resource::TYPE_DOCUMENT]['success']);
|
||||
$this->assertGreaterThanOrEqual(1, $result['statusCounters'][Resource::TYPE_DOCUMENT]['success']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DOCUMENT]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DOCUMENT]['warning']);
|
||||
|
||||
// Ensure only expected counters exist (7 total)
|
||||
$this->assertCount(7, $result['statusCounters']);
|
||||
// Get migration status before asserting VectorDB counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert VectorDB counters
|
||||
$this->assertArrayHasKey(Resource::TYPE_DATABASE_VECTORDB, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_VECTORDB]['error']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_VECTORDB]['pending']);
|
||||
$this->assertEquals(1, $result['statusCounters'][Resource::TYPE_DATABASE_VECTORDB]['success']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_VECTORDB]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_DATABASE_VECTORDB]['warning']);
|
||||
|
||||
// Get migration status before asserting Attribute counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Assert Attribute counters (for VectorDB)
|
||||
$this->assertArrayHasKey(Resource::TYPE_ATTRIBUTE, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ATTRIBUTE]['error']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ATTRIBUTE]['pending']);
|
||||
$this->assertGreaterThanOrEqual(1, $result['statusCounters'][Resource::TYPE_ATTRIBUTE]['success']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ATTRIBUTE]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_ATTRIBUTE]['warning']);
|
||||
|
||||
// Get migration status before asserting Index counters
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
$this->assertArrayHasKey(Resource::TYPE_INDEX, $result['statusCounters']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_INDEX]['error']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_INDEX]['pending']);
|
||||
$this->assertGreaterThanOrEqual(4, $result['statusCounters'][Resource::TYPE_INDEX]['success']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_INDEX]['processing']);
|
||||
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_INDEX]['warning']);
|
||||
|
||||
// Get migration status before asserting counter count
|
||||
$result = $this->getMigrationStatus($migrationId);
|
||||
// Ensure only expected counters exist (10 total)
|
||||
$this->assertCount(10, $result['statusCounters']);
|
||||
|
||||
// ====== Validate on destination: SQL Database resources ======
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $sqlDatabaseId, [
|
||||
@@ -1880,6 +2325,18 @@ trait MigrationsBase
|
||||
$this->assertEquals($rowId, $response['body']['$id']);
|
||||
$this->assertEquals('Laptop', $response['body']['productName']);
|
||||
|
||||
$sqlIndexDestination = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $sqlDatabaseId . '/tables/' . $tableId . '/indexes/' . $sqlIndexKey, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
$this->assertEquals(200, $sqlIndexDestination['headers']['status-code']);
|
||||
$this->assertEquals($sqlIndexKey, $sqlIndexDestination['body']['key']);
|
||||
$this->assertEquals(Database::INDEX_UNIQUE, $sqlIndexDestination['body']['type']);
|
||||
if (isset($sqlIndexDestination['body']['columns'])) {
|
||||
$this->assertEquals(['productName'], $sqlIndexDestination['body']['columns']);
|
||||
}
|
||||
|
||||
// ====== Validate on destination: DocumentsDB resources ======
|
||||
$response = $this->client->call(Client::METHOD_GET, '/documentsdb/' . $docsDatabaseId, [
|
||||
'content-type' => 'application/json',
|
||||
@@ -1914,7 +2371,72 @@ trait MigrationsBase
|
||||
$this->assertEquals('John Doe', $response['body']['name']);
|
||||
$this->assertEquals('john@example.com', $response['body']['email']);
|
||||
|
||||
// ====== Cleanup both destinations ======
|
||||
$documentsIndexDestination = $this->client->call(Client::METHOD_GET, '/documentsdb/' . $docsDatabaseId . '/collections/' . $collectionId . '/indexes/' . $documentsIndexKey, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
$this->assertEquals(200, $documentsIndexDestination['headers']['status-code']);
|
||||
$this->assertEquals($documentsIndexKey, $documentsIndexDestination['body']['key']);
|
||||
$this->assertEquals(Database::INDEX_UNIQUE, $documentsIndexDestination['body']['type']);
|
||||
if (isset($documentsIndexDestination['body']['attributes'])) {
|
||||
$this->assertEquals(['email'], $documentsIndexDestination['body']['attributes']);
|
||||
}
|
||||
|
||||
// ====== Validate on destination: VectorDB resources ======
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($vectorDatabaseId, $response['body']['$id']);
|
||||
$this->assertEquals('Mixed VectorDB', $response['body']['name']);
|
||||
|
||||
// Validate VectorDB Collection
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($vectorCollectionId, $response['body']['$id']);
|
||||
$this->assertEquals('Products', $response['body']['name']);
|
||||
// Verify attributes are present (embeddings and metadata are default attributes)
|
||||
$this->assertArrayHasKey('attributes', $response['body']);
|
||||
$this->assertIsArray($response['body']['attributes']);
|
||||
|
||||
$vectorIndexesDestination = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId . '/indexes', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
$this->assertEquals(200, $vectorIndexesDestination['headers']['status-code']);
|
||||
$indexByKey = [];
|
||||
foreach ($vectorIndexesDestination['body']['indexes'] ?? [] as $index) {
|
||||
if (isset($index['key'])) {
|
||||
$indexByKey[$index['key']] = $index;
|
||||
}
|
||||
}
|
||||
$this->assertArrayHasKey($metadataIndexKey, $indexByKey, 'Metadata index should exist on destination');
|
||||
$this->assertEquals(Database::INDEX_OBJECT, $indexByKey[$metadataIndexKey]['type']);
|
||||
$this->assertArrayHasKey($vectorEmbeddingIndexKey, $indexByKey, 'Embeddings HNSW index should exist on destination');
|
||||
$this->assertEquals(Database::INDEX_HNSW_EUCLIDEAN, $indexByKey[$vectorEmbeddingIndexKey]['type']);
|
||||
|
||||
// Validate VectorDB Document
|
||||
$response = $this->client->call(Client::METHOD_GET, '/vectordb/' . $vectorDatabaseId . '/collections/' . $vectorCollectionId . '/documents/' . $vectorDocumentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($vectorDocumentId, $response['body']['$id']);
|
||||
$this->assertEquals('Product Vector', $response['body']['metadata']['name']);
|
||||
|
||||
// ====== Cleanup all destinations ======
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $sqlDatabaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
@@ -1927,6 +2449,12 @@ trait MigrationsBase
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_DELETE, '/vectordb/' . $vectorDatabaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
// ====== Cleanup sources ======
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $sqlDatabaseId, [
|
||||
'content-type' => 'application/json',
|
||||
@@ -1939,5 +2467,11 @@ trait MigrationsBase
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_DELETE, '/vectordb/' . $vectorDatabaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $sourceProject['$id'],
|
||||
'x-appwrite-key' => $sourceProject['apiKey'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user