diff --git a/composer.json b/composer.json index c80d3acc9..1824795de 100755 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "utopia-php/framework": "0.33.*", "utopia-php/cache": "0.13.*", "utopia-php/pools": "0.8.*", - "utopia-php/mongo": "dev-feat-partial-filter-expression as 0.5.3" + "utopia-php/mongo": "dev-feat-create-UUID as 0.5.3" }, "require-dev": { "fakerphp/faker": "1.23.*", diff --git a/composer.lock b/composer.lock index 046911aec..017f29b9b 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "0f4d1b2ebd46917ced104adabbe9638b", + "content-hash": "94af8bc26007c57d8f0ad1a76f38c16e", "packages": [ { "name": "brick/math", @@ -149,23 +149,26 @@ }, { "name": "google/protobuf", - "version": "v4.31.1", + "version": "v4.32.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "2b028ce8876254e2acbeceea7d9b573faad41864" + "reference": "9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/2b028ce8876254e2acbeceea7d9b573faad41864", - "reference": "2b028ce8876254e2acbeceea7d9b573faad41864", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646", + "reference": "9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=8.1.0" + }, + "provide": { + "ext-protobuf": "*" }, "require-dev": { - "phpunit/phpunit": ">=5.0.0" + "phpunit/phpunit": ">=5.0.0 <8.5.27" }, "suggest": { "ext-bcmath": "Need to support JSON deserialization" @@ -187,9 +190,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.31.1" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.32.0" }, - "time": "2025-05-28T18:52:35+00:00" + "time": "2025-08-14T20:00:33+00:00" }, { "name": "mongodb/mongodb", @@ -484,16 +487,16 @@ }, { "name": "open-telemetry/context", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc" + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc", - "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6", + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6", "shasum": "" }, "require": { @@ -539,7 +542,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-04T03:25:06+00:00" + "time": "2025-08-13T01:12:00+00:00" }, { "name": "open-telemetry/exporter-otlp", @@ -2077,22 +2080,23 @@ }, { "name": "utopia-php/mongo", - "version": "dev-feat-partial-filter-expression", + "version": "dev-feat-create-UUID", "source": { "type": "git", "url": "https://github.com/utopia-php/mongo.git", - "reference": "b2c89d4c21b77cfa3cb1b740bd0245c971040e7f" + "reference": "f25c14e4e3037093ad5679398da4805abb3dfec1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/b2c89d4c21b77cfa3cb1b740bd0245c971040e7f", - "reference": "b2c89d4c21b77cfa3cb1b740bd0245c971040e7f", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/f25c14e4e3037093ad5679398da4805abb3dfec1", + "reference": "f25c14e4e3037093ad5679398da4805abb3dfec1", "shasum": "" }, "require": { "ext-mongodb": "2.1.1", "mongodb/mongodb": "2.1.0", - "php": ">=8.0" + "php": ">=8.0", + "ramsey/uuid": "^4.9.0" }, "require-dev": { "fakerphp/faker": "^1.14", @@ -2131,9 +2135,9 @@ ], "support": { "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/feat-partial-filter-expression" + "source": "https://github.com/utopia-php/mongo/tree/feat-create-UUID" }, - "time": "2025-08-10T12:40:53+00:00" + "time": "2025-08-18T14:00:43+00:00" }, { "name": "utopia-php/pools", @@ -4381,7 +4385,7 @@ "aliases": [ { "package": "utopia-php/mongo", - "version": "dev-feat-partial-filter-expression", + "version": "dev-feat-create-UUID", "alias": "0.5.3", "alias_normalized": "0.5.3.0" } diff --git a/docker-compose.yml b/docker-compose.yml index 09b3aa026..33a984521 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - ./dev/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini - /var/run/docker.sock:/var/run/docker.sock - ./docker-compose.yml:/usr/src/code/docker-compose.yml - #- ./vendor/utopia-php/mongo/src/Client.php:/usr/src/code/vendor/utopia-php/mongo/src/Client.php + - ./vendor/utopia-php/mongo/src/Client.php:/usr/src/code/vendor/utopia-php/mongo/src/Client.php environment: PHP_IDE_CONFIG: serverName=tests diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index e4c4f7340..81c8a14c3 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -652,7 +652,6 @@ public function createIndex(string $collection, string $id, string $type, array $indexes = []; $options = []; - $indexes['name'] = $id; // If sharedTables, always add _tenant as the first key @@ -661,15 +660,15 @@ public function createIndex(string $collection, string $id, string $type, array } foreach ($attributes as $i => $attribute) { - $attribute = $this->filter($this->getInternalKeyForAttribute($attribute)); + $attributes[$i] = $this->filter($this->getInternalKeyForAttribute($attribute)); $orderType = $this->getOrder($this->filter($orders[$i] ?? Database::ORDER_ASC)); - $indexes['key'][$attribute] = $orderType; + $indexes['key'][$attributes[$i] ] = $orderType; switch ($type) { case Database::INDEX_KEY: break; case Database::INDEX_FULLTEXT: - $indexes['key'][$attribute] = 'text'; + $indexes['key'][$attributes[$i]] = 'text'; break; case Database::INDEX_UNIQUE: $indexes['unique'] = true; @@ -1168,7 +1167,7 @@ public function createOrUpdateDocuments(string $collection, string $attribute, a $attributes['_permissions'] = $document->getPermissions(); if (!empty($document->getSequence())) { - $attributes['_id'] = new ObjectId($document->getSequence()); + $attributes['_id'] = $document->getSequence(); } if ($this->sharedTables) { @@ -1204,6 +1203,13 @@ public function createOrUpdateDocuments(string $collection, string $attribute, a $update = [ '$set' => $record ]; + + // Add UUID7 _id for new documents in upsert operations + if (empty($document->getSequence())) { + $update['$setOnInsert'] = [ + '_id' => $this->client->createUuid() + ]; + } } $operations[] = [ @@ -1358,7 +1364,7 @@ public function deleteDocuments(string $collection, array $sequences, array $per $name = $this->getNamespace() . '_' . $this->filter($collection); foreach ($sequences as $index => $sequence) { - $sequences[$index] = new ObjectId($sequence); + $sequences[$index] = $sequence; } $filters = $this->buildFilters([new Query(Query::TYPE_EQUAL, '_id', $sequences)]); @@ -1512,7 +1518,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $tmp = $cursor[$originalPrev]; if ($originalPrev === '$sequence') { - $tmp = new ObjectId($tmp); + $tmp = $tmp; } $andConditions[] = [ @@ -1523,8 +1529,6 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $tmp = $cursor[$originalAttribute]; if ($originalAttribute === '$sequence') { - $tmp = new ObjectId($tmp); - /** If there is only $sequence attribute in $orderAttributes skip Or And operators **/ if (count($orderAttributes) === 1) { $filters[$attribute] = [ @@ -1594,7 +1598,7 @@ private function getMongoTypeCode(string $appwriteType): string Database::VAR_BOOLEAN => 'bool', Database::VAR_DATETIME => 'date', Database::VAR_ID => 'string', - Database::VAR_OBJECT_ID => 'objectId', + Database::VAR_UUID7 => 'string', default => 'string' }; } @@ -1792,7 +1796,7 @@ protected function replaceChars(string $from, string $to, array $array): array unset($result['$id']); } if (array_key_exists('$sequence', $array)) { - $result['_id'] = new ObjectId($array['$sequence']); + $result['_id'] = $array['$sequence']; unset($result['$sequence']); } if (array_key_exists('$tenant', $array)) { @@ -1842,7 +1846,7 @@ protected function buildFilter(Query $query): array $query->setAttribute('_id'); $values = $query->getValues(); foreach ($values as $k => $v) { - $values[$k] = new ObjectId($v); + $values[$k] = $v; } $query->setValues($values); } elseif ($query->getAttribute() === '$createdAt') { @@ -2400,7 +2404,7 @@ protected function execute(mixed $stmt): bool */ public function getIdAttributeType(): string { - return Database::VAR_OBJECT_ID; + return Database::VAR_UUID7; } /** diff --git a/src/Database/Database.php b/src/Database/Database.php index 75c144dd7..7b4fe3188 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -42,7 +42,7 @@ class Database public const VAR_BOOLEAN = 'boolean'; public const VAR_DATETIME = 'datetime'; public const VAR_ID = 'id'; - public const VAR_OBJECT_ID = 'objectId'; + public const VAR_UUID7 = 'uuid7'; public const INT_MAX = 2147483647; public const BIG_INT_MAX = PHP_INT_MAX; diff --git a/src/Database/Validator/Sequence.php b/src/Database/Validator/Sequence.php index 305632727..e97042aa0 100644 --- a/src/Database/Validator/Sequence.php +++ b/src/Database/Validator/Sequence.php @@ -46,9 +46,8 @@ public function isValid($value): bool } switch ($this->idAttributeType) { - case Database::VAR_OBJECT_ID: - return preg_match('/^[a-f0-9]{24}$/i', $value) === 1; - + case Database::VAR_UUID7: //UUID7 + return preg_match('/^[a-f0-9]{8}-[a-f0-9]{4}-7[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$/i', $value) === 1; case Database::VAR_INTEGER: $start = ($this->primary) ? 1 : 0; $validator = new Range($start, Database::BIG_INT_MAX, Database::VAR_INTEGER); diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index be49ac681..a05a2b915 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -43,8 +43,8 @@ public function testCreateDocument(): Document $this->assertEquals(true, $database->createAttribute('documents', 'id', Database::VAR_ID, 0, false, null)); $sequence = '1000000'; - if ($database->getAdapter()->getIdAttributeType() == Database::VAR_OBJECT_ID) { - $sequence = '6890c1e3c00288c2470de7a0' ; + if ($database->getAdapter()->getIdAttributeType() == Database::VAR_UUID7) { + $sequence = '01890dd5-7331-7f3a-9c1b-123456789abc' ; } $document = $database->createDocument('documents', new Document([ @@ -102,8 +102,8 @@ public function testCreateDocument(): Document $sequence = '56000'; - if ($database->getAdapter()->getIdAttributeType() == Database::VAR_OBJECT_ID) { - $sequence = '6890c1e3c00288c2470de7b3' ; + if ($database->getAdapter()->getIdAttributeType() == Database::VAR_UUID7) { + $sequence = '01890dd5-7331-7f3a-9c1b-123456789def' ; } // Test create document with manual internal id @@ -279,8 +279,8 @@ public function testCreateDocument(): Document $this->assertNull($documentIdNull->getAttribute('id')); $sequence = '0'; - if ($database->getAdapter()->getIdAttributeType() == Database::VAR_OBJECT_ID) { - $sequence = '6890c1e3c00288c0000de7b3'; + if ($database->getAdapter()->getIdAttributeType() == Database::VAR_UUID7) { + $sequence = '01890dd5-7331-7f3a-9c1b-123456789abc'; } /** @@ -402,8 +402,10 @@ public function testCreateDocumentsWithAutoIncrement(): void $offset = 1000000; for ($i = $offset; $i <= ($offset + 10); $i++) { $sequence = (string)$i; - if ($database->getAdapter()->getIdAttributeType() == Database::VAR_OBJECT_ID) { - $sequence = '689000288c0000de7'.$i; + if ($database->getAdapter()->getIdAttributeType() == Database::VAR_UUID7) { + // Replace last 6 digits with $i to make it unique + $suffix = str_pad(substr((string)$i, -6), 6, '0', STR_PAD_LEFT); + $sequence = '01890dd5-7331-7f3a-9c1b-123456' . $suffix; } $hash[$i] = $sequence; @@ -4712,8 +4714,8 @@ public function testExceptionCaseInsensitiveDuplicate(Document $document): Docum $database = static::getDatabase(); $sequence = '200'; - if ($database->getAdapter()->getIdAttributeType() == Database::VAR_OBJECT_ID) { - $sequence = '6890c1e3c00288c2470de7a0' ; + if ($database->getAdapter()->getIdAttributeType() == Database::VAR_UUID7) { + $sequence = '01890dd5-7331-7f3a-9c1b-123456789abc' ; } $document->setAttribute('$id', 'caseSensitive');