Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5f637cb
feat: add support for TTL indexes in database adapters and validation
ArnabChatterjee20k Jan 9, 2026
321a4c5
refactor: standardize method formatting and improve TTL index validat…
ArnabChatterjee20k Jan 12, 2026
151703f
updated adapters with the support method for the ttl index support
ArnabChatterjee20k Jan 12, 2026
cc63ade
fix: update TTL index validation messages and conditions for shared t…
ArnabChatterjee20k Jan 12, 2026
ed322e6
typo
ArnabChatterjee20k Jan 12, 2026
8fa2454
added ttl index in the missing index message
ArnabChatterjee20k Jan 12, 2026
07ccad8
Refactor SchemalessTests to improve datetime handling and add new tests
ArnabChatterjee20k Jan 13, 2026
9a21ce2
Update src/Database/Validator/Index.php
ArnabChatterjee20k Jan 13, 2026
6fd965d
fix: update TTL index default value to 1 and adjust related methods f…
ArnabChatterjee20k Jan 13, 2026
8e4e208
Merge remote-tracking branch 'upstream/mongo-ttl' into mongo-ttl
ArnabChatterjee20k Jan 13, 2026
affc6a1
test: implement retry logic for TTL expiration checks in SchemalessTests
ArnabChatterjee20k Jan 13, 2026
3cbc6f6
linting
ArnabChatterjee20k Jan 13, 2026
41ea5ca
updated waiting in ttl test
ArnabChatterjee20k Jan 13, 2026
06e8d08
updated expiry tests
ArnabChatterjee20k Jan 13, 2026
4cb6863
enforced one ttl index per collection
ArnabChatterjee20k Jan 14, 2026
cc7fca3
removed empty orders from the ttl index validator
ArnabChatterjee20k Jan 14, 2026
3b388c8
changed ttl check
ArnabChatterjee20k Jan 14, 2026
e90b89d
updated ttl attribute support in the get document
ArnabChatterjee20k Jan 22, 2026
d641084
Merge remote-tracking branch 'upstream/main' into mongo-ttl
ArnabChatterjee20k Jan 22, 2026
92aeef9
linting
ArnabChatterjee20k Jan 22, 2026
68beab1
updated composer
ArnabChatterjee20k Jan 22, 2026
7aa241c
Merge remote-tracking branch 'origin/main' into mongo-ttl
ArnabChatterjee20k Feb 10, 2026
b020b31
applied ttl check for both cached and non cached doc
ArnabChatterjee20k Feb 10, 2026
073165e
Refactor SchemalessTests to assert document expiration behavior. Upda…
ArnabChatterjee20k Feb 10, 2026
502d0df
updated tests
ArnabChatterjee20k Feb 10, 2026
3fca7d7
updated tests
ArnabChatterjee20k Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/Database/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -666,10 +666,12 @@ abstract public function renameIndex(string $collection, string $old, string $ne
* @param array<int> $lengths
* @param array<string> $orders
* @param array<string,string> $indexAttributeTypes
* @param array<string, mixed> $collation
* @param int $ttl
*
* @return bool
*/
abstract public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = []): bool;
abstract public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = [], int $ttl = 0): bool;

/**
* Delete Index
Expand Down Expand Up @@ -1483,4 +1485,14 @@ public function getSupportForRegex(): bool
{
return $this->getSupportForPCRERegex() || $this->getSupportForPOSIXRegex();
}

/**
* Are ttl indexes supported?
*
* @return bool
*/
public function getSupportTTLIndexes(): bool
{
return false;
}
}
7 changes: 6 additions & 1 deletion src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ public function renameIndex(string $collection, string $old, string $new): bool
* @return bool
* @throws DatabaseException
*/
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = [], int $ttl = 0): bool
{
$metadataCollection = new Document(['$id' => Database::METADATA]);
$collection = $this->getDocument($metadataCollection, $collection);
Expand Down Expand Up @@ -2255,4 +2255,9 @@ public function getSupportForPOSIXRegex(): bool
{
return false;
}

public function getSupportTTLIndexes(): bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make sure method names are always consistent: getSupportForTTLIndexes

{
return false;
}
}
28 changes: 26 additions & 2 deletions src/Database/Adapter/Mongo.php
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,9 @@ public function createCollection(string $name, array $attributes = [], array $in
$order = $this->getOrder($this->filter($orders[$j] ?? Database::ORDER_ASC));
$unique = true;
break;
case Database::INDEX_TTL:
$order = $this->getOrder($this->filter($orders[$j] ?? Database::ORDER_ASC));
break;
default:
// index not supported
return false;
Expand All @@ -526,6 +529,14 @@ public function createCollection(string $name, array $attributes = [], array $in
$newIndexes[$i]['default_language'] = 'none';
}

// Handle TTL indexes
if ($index->getAttribute('type') === Database::INDEX_TTL) {
$ttl = $index->getAttribute('ttl', 0);
if ($ttl > 0) {
$newIndexes[$i]['expireAfterSeconds'] = $ttl;
}
}

// Add partial filter for indexes to avoid indexing null values
if (in_array($index->getAttribute('type'), [
Database::INDEX_UNIQUE,
Expand Down Expand Up @@ -901,10 +912,11 @@ public function deleteRelationship(
* @param array<string> $orders
* @param array<string, string> $indexAttributeTypes
* @param array<string, mixed> $collation
* @param int $ttl
* @return bool
* @throws Exception
*/
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = [], int $ttl = 0): bool
{
$name = $this->getNamespace() . '_' . $this->filter($collection);
$id = $this->filter($id);
Expand Down Expand Up @@ -933,6 +945,8 @@ public function createIndex(string $collection, string $id, string $type, array
case Database::INDEX_UNIQUE:
$indexes['unique'] = true;
break;
case Database::INDEX_TTL:
break;
default:
return false;
}
Expand Down Expand Up @@ -961,6 +975,11 @@ public function createIndex(string $collection, string $id, string $type, array
$indexes['default_language'] = 'none';
}

// Handle TTL indexes
if ($type === Database::INDEX_TTL && $ttl > 0) {
$indexes['expireAfterSeconds'] = $ttl;
}

// Add partial filter for indexes to avoid indexing null values
if (in_array($type, [Database::INDEX_UNIQUE, Database::INDEX_KEY])) {
$partialFilter = [];
Expand Down Expand Up @@ -1073,7 +1092,7 @@ public function renameIndex(string $collection, string $old, string $new): bool

try {
$deletedindex = $this->deleteIndex($collection, $old);
$createdindex = $this->createIndex($collection, $new, $index['type'], $index['attributes'], $index['lengths'] ?? [], $index['orders'] ?? [], $indexAttributeTypes);
$createdindex = $this->createIndex($collection, $new, $index['type'], $index['attributes'], $index['lengths'] ?? [], $index['orders'] ?? [], $indexAttributeTypes, [], $index['ttl'] ?? 0);
} catch (\Exception $e) {
throw $this->processException($e);
}
Expand Down Expand Up @@ -3395,4 +3414,9 @@ public function getSupportForTrigramIndex(): bool
{
return false;
}

public function getSupportTTLIndexes(): bool
{
return true;
}
}
5 changes: 5 additions & 0 deletions src/Database/Adapter/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,4 +317,9 @@ protected function getOperatorSQL(string $column, \Utopia\Database\Operator $ope
// For all other operators, use parent implementation
return parent::getOperatorSQL($column, $operator, $bindIndex);
}

public function getSupportTTLIndexes(): bool
{
return false;
}
}
7 changes: 6 additions & 1 deletion src/Database/Adapter/Pool.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public function renameIndex(string $collection, string $old, string $new): bool
return $this->delegate(__FUNCTION__, \func_get_args());
}

public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = [], int $ttl = 0): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
}
Expand Down Expand Up @@ -649,4 +649,9 @@ public function getSupportForAlterLocks(): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
}

public function getSupportTTLIndexes(): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
}
}
12 changes: 10 additions & 2 deletions src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ public function createCollection(string $name, array $attributes = [], array $in
}
}
$indexOrders = $index->getAttribute('orders', []);
$indexTtl = $index->getAttribute('ttl', 0);
if ($indexType === Database::INDEX_SPATIAL && count($indexOrders)) {
throw new DatabaseException('Spatial indexes with explicit orders are not supported. Remove the orders to create this index.');
}
Expand All @@ -329,7 +330,9 @@ public function createCollection(string $name, array $attributes = [], array $in
$indexAttributes,
[],
$indexOrders,
$indexAttributesWithType
$indexAttributesWithType,
[],
$indexTtl
);
}
} catch (PDOException $e) {
Expand Down Expand Up @@ -876,7 +879,7 @@ public function deleteRelationship(

* @return bool
*/
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = [], int $ttl = 0): bool
{
$collection = $this->filter($collection);
$id = $this->filter($id);
Expand Down Expand Up @@ -2829,4 +2832,9 @@ protected function getSQLTable(string $name): string

return "{$this->quote($this->getDatabase())}.{$this->quote($table)}";
}

public function getSupportTTLIndexes(): bool
{
return false;
}
}
10 changes: 8 additions & 2 deletions src/Database/Adapter/SQLite.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,9 @@ public function createCollection(string $name, array $attributes = [], array $in
$indexAttributes = $index->getAttribute('attributes', []);
$indexLengths = $index->getAttribute('lengths', []);
$indexOrders = $index->getAttribute('orders', []);
$indexTtl = $index->getAttribute('ttl', 0);

$this->createIndex($id, $indexId, $indexType, $indexAttributes, $indexLengths, $indexOrders);
$this->createIndex($id, $indexId, $indexType, $indexAttributes, $indexLengths, $indexOrders, [], [], $indexTtl);
}

$this->createIndex("{$id}_perms", '_index_1', Database::INDEX_UNIQUE, ['_document', '_type', '_permission'], [], []);
Expand Down Expand Up @@ -455,7 +456,7 @@ public function renameIndex(string $collection, string $old, string $new): bool
* @throws Exception
* @throws PDOException
*/
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders, array $indexAttributeTypes = [], array $collation = [], int $ttl = 0): bool
{
$name = $this->filter($collection);
$id = $this->filter($id);
Expand Down Expand Up @@ -1908,4 +1909,9 @@ public function getSupportForPOSIXRegex(): bool
{
return false;
}

public function getSupportTTLIndexes(): bool
{
return false;
}
}
11 changes: 8 additions & 3 deletions src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Database
public const INDEX_HNSW_COSINE = 'hnsw_cosine';
public const INDEX_HNSW_DOT = 'hnsw_dot';
public const INDEX_TRIGRAM = 'trigram';
public const INDEX_TTL = 'ttl';

// Max limits
public const MAX_INT = 2147483647;
Expand Down Expand Up @@ -1648,6 +1649,7 @@ public function createCollection(string $id, array $attributes = [], array $inde
$this->adapter->getSupportForIndex(),
$this->adapter->getSupportForUniqueIndex(),
$this->adapter->getSupportForFulltextIndex(),
$this->adapter->getSupportTTLIndexes()
);
foreach ($indexes as $index) {
if (!$validator->isValid($index)) {
Expand Down Expand Up @@ -2798,6 +2800,7 @@ public function updateAttribute(string $collection, string $id, ?string $type =
$this->adapter->getSupportForIndex(),
$this->adapter->getSupportForUniqueIndex(),
$this->adapter->getSupportForFulltextIndex(),
$this->adapter->getSupportTTLIndexes()
);

foreach ($indexes as $index) {
Expand Down Expand Up @@ -3603,6 +3606,7 @@ public function renameIndex(string $collection, string $old, string $new): bool
* @param array<string> $attributes
* @param array<int> $lengths
* @param array<string> $orders
* @param int $ttl
*
* @return bool
* @throws AuthorizationException
Expand All @@ -3613,14 +3617,13 @@ public function renameIndex(string $collection, string $old, string $new): bool
* @throws StructureException
* @throws Exception
*/
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = [], int $ttl = 0): bool
{
if (empty($attributes)) {
throw new DatabaseException('Missing attributes');
}

$collection = $this->silent(fn () => $this->getCollection($collection));

// index IDs are case-insensitive
$indexes = $collection->getAttribute('indexes', []);

Expand Down Expand Up @@ -3671,6 +3674,7 @@ public function createIndex(string $collection, string $id, string $type, array
'attributes' => $attributes,
'lengths' => $lengths,
'orders' => $orders,
'ttl' => $ttl
]);

if ($this->validate) {
Expand All @@ -3693,6 +3697,7 @@ public function createIndex(string $collection, string $id, string $type, array
$this->adapter->getSupportForIndex(),
$this->adapter->getSupportForUniqueIndex(),
$this->adapter->getSupportForFulltextIndex(),
$this->adapter->getSupportTTLIndexes()
);
if (!$validator->isValid($index)) {
throw new IndexException($validator->getDescription());
Expand All @@ -3702,7 +3707,7 @@ public function createIndex(string $collection, string $id, string $type, array
$collection->setAttribute('indexes', $index, Document::SET_TYPE_APPEND);

try {
$created = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders, $indexAttributesWithTypes);
$created = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders, $indexAttributesWithTypes, [], $ttl);

if (!$created) {
throw new DatabaseException('Failed to create index');
Expand Down
7 changes: 4 additions & 3 deletions src/Database/Mirror.php
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,9 @@ public function deleteAttribute(string $collection, string $id): bool
return $result;
}

public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = []): bool
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = [], int $ttl = 0): bool
{
$result = $this->source->createIndex($collection, $id, $type, $attributes, $lengths, $orders);
$result = $this->source->createIndex($collection, $id, $type, $attributes, $lengths, $orders, $ttl);

if ($this->destination === null) {
return $result;
Expand Down Expand Up @@ -502,7 +502,8 @@ public function createIndex(string $collection, string $id, string $type, array
$document->getAttribute('type'),
$document->getAttribute('attributes'),
$document->getAttribute('lengths'),
$document->getAttribute('orders')
$document->getAttribute('orders'),
$document->getAttribute('ttl', 0)
);
} catch (\Throwable $err) {
$this->logError('createIndex', $err);
Expand Down
Loading
Loading