diff --git a/src/Database/Validator/Queries.php b/src/Database/Validator/Queries.php new file mode 100644 index 000000000..18e2ee727 --- /dev/null +++ b/src/Database/Validator/Queries.php @@ -0,0 +1,177 @@ +validator = $validator; + $this->indexes = $indexes; + $this->indexesInQueue = $indexesInQueue; + $this->strict = $strict; + } + + /** + * Get Description. + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return $this->message; + } + + /** + * Is valid. + * + * Returns true if all $queries are valid as a set. + * @param mixed $value as array of Query objects + * @return bool + */ + public function isValid($value) + { + /** + * Array of attributes from Query->getAttribute() + * + * @var string[] + */ + $queries = []; + + foreach ($value as $query) { + $queries[] = $query->getAttribute(); + + if (!$this->validator->isValid($query)) { + $this->message = 'Query not valid: ' . $this->validator->getDescription(); + return false; + } + } + + /** + * @var string + */ + $indexId = null; + + // Return false if attributes do not exactly match an index + if ($this->strict) { + // look for strict match among indexes + foreach ($this->indexes as $index) { + if ($this->arrayMatch($index['attributes'], $queries)) { + $indexId = $index['$id']; + } + } + + if (!$indexId) { + // check against the indexesInQueue + foreach ($this->indexesInQueue as $index) { + if ($this->arrayMatch($index['attributes'], $queries)) { + $this->message = 'Index still in creation queue: ' . implode(",", $queries); + return false; + } + } + + $this->message = 'Index not found: ' . implode(",", $queries); + return false; + } + } + + return true; + } + /** + * Is array + * + * Function will return true if object is array. + * + * @return bool + */ + public function isArray(): bool + { + return true; + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType(): string + { + return self::TYPE_OBJECT; + } + + /** + * Is Strict + * + * Returns true if strict validation is set + * + * @return bool + */ + public function isStrict(): bool + { + return $this->strict; + } + + /** + * Check if indexed array $indexes matches $queries + * + * @param array $indexes + * @param array $queries + * + * @return bool + */ + protected function arrayMatch($indexes, $queries): bool + { + // Check the count of indexes first for performance + if (count($indexes) !== count($queries)) { + return false; + } + + // 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; + } +} diff --git a/tests/Database/Validator/QueriesTest.php b/tests/Database/Validator/QueriesTest.php new file mode 100644 index 000000000..6e0d41c3e --- /dev/null +++ b/tests/Database/Validator/QueriesTest.php @@ -0,0 +1,180 @@ + Database::COLLECTIONS, + '$collection' => Database::COLLECTIONS, + 'name' => 'movies', + 'attributes' => [ + [ + '$id' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'description', + 'type' => Database::VAR_STRING, + 'size' => 1000000, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'rating', + 'type' => Database::VAR_INTEGER, + 'size' => 5, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'price', + 'type' => Database::VAR_FLOAT, + 'size' => 5, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'published', + 'type' => Database::VAR_BOOLEAN, + 'size' => 5, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'tags', + 'type' => Database::VAR_STRING, + 'size' => 55, + 'required' => true, + 'signed' => true, + 'array' => true, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => 'testindex', + 'type' => 'text', + 'attributes' => [ + 'title', + 'description' + ], + 'orders' => [ + 'ASC', + 'DESC' + ], + ], + [ + '$id' => 'testindex2', + 'type' => 'text', + 'attributes' => [ + 'title', + 'description', + 'price' + ], + 'orders' => [ + 'ASC', + 'DESC' + ], + ], + ], + 'indexesInQueue' => [ + [ + '$id' => 'testindex3', + 'type' => 'text', + 'attributes' => [ + 'price', + 'title' + ], + 'orders' => [ + 'ASC', + 'DESC' + ] + ] + ] + ]; + + + /** + * @var Query[] $queries + */ + protected $queries = []; + + /** + * @var QueryValidator + */ + protected $queryValidator = null; + + public function setUp(): void + { + $this->queryValidator = new QueryValidator($this->collection['attributes']); + + $query1 = Query::parse('title.notEqual("Iron Man", "Ant Man")'); + $query2 = Query::parse('description.equal("Best movie ever")'); + + array_push($this->queries, $query1, $query2); + } + + public function tearDown(): void + { + } + + public function testQueries() + { + // test for SUCCESS + $validator = new Queries($this->queryValidator, $this->collection['indexes'], $this->collection['indexesInQueue']); + + $this->assertEquals(true, $validator->isValid($this->queries)); + + $this->queries[] = Query::parse('price.lesserEqual(6.50)'); + $this->assertEquals(true, $validator->isValid($this->queries)); + + // test for FAILURE + $this->queries[] = Query::parse('rating.greater(4)'); + + $this->assertEquals(false, $validator->isValid($this->queries)); + $this->assertEquals("Index not found: title,description,price,rating", $validator->getDescription()); + + // test for queued index + $query1 = Query::parse('price.lesserEqual(6.50)'); + $query2 = Query::parse('title.notEqual("Iron Man", "Ant Man")'); + + $this->queries = [$query1, $query2]; + $this->assertEquals(false, $validator->isValid($this->queries)); + $this->assertEquals("Index still in creation queue: price,title", $validator->getDescription()); + + } + + public function testIsStrict() + { + $validator = new Queries($this->queryValidator, $this->collection['indexes'], $this->collection['indexesInQueue']); + + $this->assertEquals(true, $validator->isStrict()); + + $validator = new Queries($this->queryValidator, $this->collection['indexes'], $this->collection['indexesInQueue'], false); + + $this->assertEquals(false, $validator->isStrict()); + } +}