diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 3449896ad6..4b51a05087 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2817,7 +2817,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') } // Validate queries - $queriesValidator = new Documents($collection->getAttribute('attributes')); + $queriesValidator = new Documents($collection->getAttribute('attributes'), $collection->getAttribute('indexes')); $validQueries = $queriesValidator->isValid($queries); if (!$validQueries) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $queriesValidator->getDescription()); @@ -2848,6 +2848,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') unset($filterQueries[$key]); } } + $documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries)); $documentSecurity = $collection->getAttribute('documentSecurity', false); diff --git a/src/Appwrite/Utopia/Database/Validator/IndexedQueries.php b/src/Appwrite/Utopia/Database/Validator/IndexedQueries.php index e36ba45928..f247bb7e4f 100644 --- a/src/Appwrite/Utopia/Database/Validator/IndexedQueries.php +++ b/src/Appwrite/Utopia/Database/Validator/IndexedQueries.php @@ -10,26 +10,26 @@ use Utopia\Database\Query; class IndexedQueries extends Queries { /** - * @var Document[] + * @var array */ - protected $attributes = []; + protected array $attributes = []; /** - * @var Document[] + * @var array */ - protected $indexes = []; + protected array $indexes = []; /** * Expression constructor * * This Queries Validator filters indexes for only available indexes * - * @param Document[] $attributes - * @param Document[] $indexes + * @param array $attributes + * @param array $indexes * @param Base ...$validators * @throws \Exception */ - public function __construct($attributes = [], $indexes = [], Base ...$validators) + public function __construct(array $attributes = [], array $indexes = [], Base ...$validators) { $this->attributes = $attributes; @@ -55,33 +55,6 @@ class IndexedQueries extends Queries parent::__construct(...$validators); } - /** - * Check if indexed array $indexes matches $queries - * - * @param array $indexes - * @param array $queries - * - * @return bool - */ - protected function arrayMatch(array $indexes, array $queries): bool - { - // Check the count of indexes first for performance - if (count($queries) !== count($indexes)) { - return false; - } - - // Sort them for comparison, the order is not important here anymore. - sort($indexes, SORT_STRING); - sort($queries, SORT_STRING); - - // Only matching arrays will have equal diffs in both directions - if (array_diff_assoc($indexes, $queries) !== array_diff_assoc($queries, $indexes)) { - return false; - } - - return true; - } - /** * Is valid. * @@ -111,41 +84,26 @@ class IndexedQueries extends Queries } $grouped = Query::groupByType($queries); - /** @var Query[] */ $filters = $grouped['filters']; - /** @var string[] */ $orderAttributes = $grouped['orderAttributes']; + $filters = $grouped['filters']; - // Check filter queries for exact index match - if (count($filters) > 0) { - $filtersByAttribute = []; - foreach ($filters as $filter) { - $filtersByAttribute[$filter->getAttribute()] = $filter->getMethod(); - } + foreach ($filters as $filter) { + if ($filter->getMethod() === Query::TYPE_SEARCH) { + $matched = false; - $found = null; + foreach ($this->indexes as $index) { + if ( + $index->getAttribute('type') === Database::INDEX_FULLTEXT + && $index->getAttribute('attributes') === [$filter->getAttribute()] + ) { + $matched = true; + } + } - foreach ($this->indexes as $index) { - if ($this->arrayMatch($index->getAttribute('attributes'), array_keys($filtersByAttribute))) { - $found = $index; + if (!$matched) { + $this->message = "Searching by attribute \"{$filter->getAttribute()}\" requires a fulltext index."; + return false; } } - - if (!$found) { - $this->message = 'Index not found: ' . implode(",", array_keys($filtersByAttribute)); - return false; - } - - // search method requires fulltext index - if (in_array(Query::TYPE_SEARCH, array_values($filtersByAttribute)) && $found['type'] !== Database::INDEX_FULLTEXT) { - $this->message = 'Search method requires fulltext index: ' . implode(",", array_keys($filtersByAttribute)); - return false; - } - } - - // Check order attributes for exact index match - $validator = new OrderAttributes($this->attributes, $this->indexes, true); - if (count($orderAttributes) > 0 && !$validator->isValid($orderAttributes)) { - $this->message = $validator->getDescription(); - return false; } return true; diff --git a/src/Appwrite/Utopia/Database/Validator/Queries.php b/src/Appwrite/Utopia/Database/Validator/Queries.php index c43c15eedf..825b24e2bc 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries.php @@ -11,12 +11,12 @@ class Queries extends Validator /** * @var string */ - protected $message = 'Invalid queries'; + protected string $message = 'Invalid queries'; /** - * @var Base[] + * @var array */ - protected $validators; + protected array $validators; /** * Queries constructor diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Documents.php b/src/Appwrite/Utopia/Database/Validator/Queries/Documents.php index 57ca4981e5..7955ad35cc 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Documents.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Documents.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; -use Appwrite\Utopia\Database\Validator\Queries; +use Appwrite\Utopia\Database\Validator\IndexedQueries; use Appwrite\Utopia\Database\Validator\Query\Cursor; use Appwrite\Utopia\Database\Validator\Query\Filter; use Appwrite\Utopia\Database\Validator\Query\Limit; @@ -12,7 +12,7 @@ use Appwrite\Utopia\Database\Validator\Query\Select; use Utopia\Database\Database; use Utopia\Database\Document; -class Documents extends Queries +class Documents extends IndexedQueries { /** * Expression constructor @@ -20,7 +20,7 @@ class Documents extends Queries * @param Document[] $attributes * @throws \Exception */ - public function __construct(array $attributes) + public function __construct(array $attributes, array $indexes) { $attributes[] = new Document([ 'key' => '$id', @@ -47,6 +47,6 @@ class Documents extends Queries new Select($attributes), ]; - parent::__construct(...$validators); + parent::__construct($attributes, $indexes, ...$validators); } } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 701eebb777..d0b5199253 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1577,6 +1577,18 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => ['greaterThan("birthDay", "1960-01-01 10:10:10+02:30")'], + ]); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals('1975-06-12T12:12:55.000+00:00', $documents['body']['documents'][0]['birthDay']); + $this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['documents'][1]['birthDay']); + $this->assertCount(2, $documents['body']['documents']); + /** * Test for Failure */ @@ -1610,18 +1622,14 @@ trait DatabasesBase $this->assertEquals(400, $documents['headers']['status-code']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'queries' => ['greaterThan("birthDay", "1960-01-01 10:10:10+02:30")'], + 'queries' => ['search("actors", "Tom")'], ]); - - $this->assertEquals($documents['headers']['status-code'], 200); - $this->assertEquals('1975-06-12T12:12:55.000+00:00', $documents['body']['documents'][0]['birthDay']); - $this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['documents'][1]['birthDay']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals(400, $documents['headers']['status-code']); + $this->assertEquals('Searching by attribute "actors" requires a fulltext index.', $documents['body']['message']); return []; }