From 3cf58ce52a9e6cc3160186becddd3c6650a227d9 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 22 Apr 2021 14:00:04 -0400 Subject: [PATCH 01/45] Add function to parse query expressions --- src/Database/Database.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Database/Database.php b/src/Database/Database.php index fd0720ce6..848fecd67 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -785,4 +785,34 @@ public function getId(): string { return \uniqid(); } + + /** + * Get attribute key-value from query expression + * + * @param string $expression + * + * @return array + */ + public function parseExpression(string $expression): array + { + //find location of parentheses in expression + $start = mb_strpos($expression, '('); + $end = mb_strpos($expression, ')'); + + //extract the query method + $method = mb_substr($expression, 0, $start); + + //grab everything inside parentheses + $query = mb_substr($expression, + ($start + 1), /* exclude open paren*/ + ($end - $start - 1) /* exclude closed paren*/ + ); + + //strip quotes from queries of type string + $query = str_replace('"', "", $query); + $query = str_replace("'", "", $query); + + return [$method, $query]; + } + } From 514bf70932b81bde15769363aa3ba1e583fbe506 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 22 Apr 2021 14:01:02 -0400 Subject: [PATCH 02/45] Add method to parse custom filter queries --- src/Database/Database.php | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/Database/Database.php b/src/Database/Database.php index 848fecd67..b070b425a 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -815,4 +815,68 @@ public function parseExpression(string $expression): array return [$method, $query]; } + /** + * Parse query filters + * + * @param string $collection Collection name + * @param array $filters Array of document filters + * + * @return array + * */ + public function parseQueries(string $collection, array $filters): array + { + $collection = $this->getCollection($collection); + $schema = $collection->getAttribute('attributes', []); + + $output = []; + foreach ($filters as &$filter) + { + $stanzas = mb_substr_count($filter, ".") + 1; + + switch ($stanzas): + case 2: + $input = explode('.', $filter); + + $attribute = $input[0]; + $expression = $input[1]; + [$method, $query] = $this->parseExpression($expression); + // $attributeType = $schema[\array_search($attribute, $schema)]['type']; + $attributeType = $schema[array_search($attribute, array_column($schema, '$id'))]['type']; + array_push($output, [ + 'collection' => $collection->getId(), + 'attribute' => $attribute, + 'method' => $method, + 'query' => $query, + 'queryType' => $attributeType, + ]); + break; + + // Copypasta code - remove/refactor when collection relations work + // + // case 3: + // $input = explode('.', $filter); + + // $attribute = $input[0]; + // $childAttribute = $input[1]; + // $expression = $input[2]; + // [$method, $query] = pickAttribute($expression); + // $attributeType = checkType($schema, $collection, $attribute, $childAttribute); + // array_push($output, [ + // 'input' => $filter, + // 'output' => [ + // [ + // 'collection' => $collection, + // 'attribute' => $attribute, + // 'childAttribute' => $childAttribute, + // 'method' => $method, + // 'query' => $query, + // 'queryType' => $attributeType, + // ] + // ], + // ]); + // break; + endswitch; + } + return $output; + } } From 80eec648f9edf5e1b91ca3794a3a39c84cdb3fb2 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 22 Apr 2021 14:01:21 -0400 Subject: [PATCH 03/45] Add tests for parsing queries --- tests/Database/Base.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index b56a8feff..bd786f166 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -1558,4 +1558,32 @@ public function decodeTest() { $this->assertEquals('1', '1'); } + + public function testParseQueries() + { + // Set up mock collections + $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('Movie')); + // $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('Director')); + // $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('Actor')); + $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'title', Database::VAR_STRING, 256)); + $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'year', Database::VAR_INTEGER, 0)); + $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'published', Database::VAR_BOOLEAN, 0)); + $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'director', Database::VAR_STRING, 256)); + $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'actors', Database::VAR_STRING, 256, true, true)); + + // test parser + $query = static::getDatabase()->parseQueries('Movie', ['title.equal("Iron Man")']); + $this->assertEquals('Movie', $query[0]['collection']); + $this->assertEquals('title', $query[0]['attribute']); + $this->assertEquals('equal', $query[0]['method']); + $this->assertEquals('Iron Man', $query[0]['query']); + $this->assertEquals(Database::VAR_STRING, $query[0]['queryType']); + + $query = static::getDatabase()->parseQueries('Movie', ['year.lesser(2001)']); + $this->assertEquals('Movie', $query[0]['collection']); + $this->assertEquals('year', $query[0]['attribute']); + $this->assertEquals('lesser', $query[0]['method']); + $this->assertEquals(2001, $query[0]['query']); + $this->assertEquals(Database::VAR_INTEGER, $query[0]['queryType']); + } } \ No newline at end of file From 69e6bc766be2e8f9045c1e6e1343563a6b7786fe Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 22 Apr 2021 15:55:20 -0400 Subject: [PATCH 04/45] Add test for parsing query expressions --- tests/Database/Base.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index bd786f166..20cb9bc5a 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -1559,6 +1559,7 @@ public function decodeTest() $this->assertEquals('1', '1'); } + public function testParseQueries() { // Set up mock collections @@ -1586,4 +1587,16 @@ public function testParseQueries() $this->assertEquals(2001, $query[0]['query']); $this->assertEquals(Database::VAR_INTEGER, $query[0]['queryType']); } + + public function testParseExpression() + { + [$method, $query] = static::getDatabase()->parseExpression('equal("Spiderman")'); + $this->assertEquals('equal', $method); + $this->assertEquals('Spiderman', $query); + + + [$method, $query] = static::getDatabase()->parseExpression('lesser(2001)'); + $this->assertEquals('lesser', $method); + $this->assertEquals(2001, $query); + } } \ No newline at end of file From cdf52673341f4be5d3be85b59ff0b8b03b1236d9 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 11:21:28 -0400 Subject: [PATCH 05/45] Refactor query into standalone class --- src/Database/Query.php | 103 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/Database/Query.php diff --git a/src/Database/Query.php b/src/Database/Query.php new file mode 100644 index 000000000..f8b7e2545 --- /dev/null +++ b/src/Database/Query.php @@ -0,0 +1,103 @@ +attribute = $attribute; + $this->operator = $operator; + $this->operand = $operand; + } + + /** + * Parse query filter + * + * @param string $filter + * + * @return Query + * */ + public static function parse(string $filter) + { + // TODO@kodumbeats handle '.' in expression value + $stanzas = mb_substr_count(mb_substr($filter, ".") + 1; + + // TODO@kodumbeats handle relations between collections, e.g. if($stanzas > 2) + switch ($stanzas): + case 2: + $input = explode('.', $filter); + + $attribute = $input[0]; + [$operator, $operand] = $this->parseExpression($input[1]); + break; + endswitch; + + return new Query($attribute, $operator, $operand); + } + + /** + * Get attribute key-value from query expression + * $expression: string with format 'operator(operand)' + * + * @param string $expression + * + * @return array + */ + protected function parseExpression(string $expression): array + { + //find location of parentheses in expression + $start = mb_strpos($expression, '('); + $end = mb_strpos($expression, ')'); + + //extract the query method + $operator = mb_substr($expression, 0, $start); + + //grab everything inside parentheses + $operand = mb_substr($expression, + ($start + 1), /* exclude open paren*/ + ($end - $start - 1) /* exclude closed paren*/ + ); + + //strip quotes from queries of type string + $operand = str_replace('"', "", $operand); + $operand = str_replace("'", "", $operand); + + return [$operator, $operand]; + } + + // /** + // * Validate query against collection schema + // * + // * @param array $schema Structured array of collection attributes + // * + // * @return bool + // */ + // protected function isValid($schema): bool + // { + // $attributeType = $schema[array_search($attribute, array_column($schema, '$id'))]['type']; + // } +} From 3f75885d914315b7862e98b2da5d4fb61d0a0ad5 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 11:23:32 -0400 Subject: [PATCH 06/45] Add getters for object properties --- src/Database/Query.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Database/Query.php b/src/Database/Query.php index f8b7e2545..6cd909af9 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -34,6 +34,36 @@ public function __construct($attribute, $operator, $operand) $this->operand = $operand; } + /** + * Get attribute + * + * @return string + */ + public function getAttribute() + { + return $this->attribute; + } + + /** + * Get operator + * + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * Get operand + * + * @return string + */ + public function getOperand() + { + return $this->operand; + } + /** * Parse query filter * From 2a8a5b36a8355e4e4d7f4333f5a7852cbfd533cb Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 11:28:30 -0400 Subject: [PATCH 07/45] Add method to get all query details --- src/Database/Query.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 6cd909af9..9794e4f6d 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -4,7 +4,6 @@ use Utopia\Database\Database; - class Query { /** @@ -64,6 +63,20 @@ public function getOperand() return $this->operand; } + /** + * Get all query details as array + * + * @return array + */ + public function getQuery() + { + return [ + 'attribute' => $this->attribute, + 'operator' => $this->operator, + 'operand' => $this->operand, + ]; + } + /** * Parse query filter * @@ -80,7 +93,6 @@ public static function parse(string $filter) switch ($stanzas): case 2: $input = explode('.', $filter); - $attribute = $input[0]; [$operator, $operand] = $this->parseExpression($input[1]); break; From f763ba4950263754a6ac7b265632415bed16197e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:03:44 -0400 Subject: [PATCH 08/45] Add return typing --- src/Database/Query.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 9794e4f6d..41d38157e 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -38,7 +38,7 @@ public function __construct($attribute, $operator, $operand) * * @return string */ - public function getAttribute() + public function getAttribute(): string { return $this->attribute; } @@ -48,7 +48,7 @@ public function getAttribute() * * @return string */ - public function getOperator() + public function getOperator(): string { return $this->operator; } @@ -56,7 +56,7 @@ public function getOperator() /** * Get operand * - * @return string + * @return mixed */ public function getOperand() { @@ -68,7 +68,7 @@ public function getOperand() * * @return array */ - public function getQuery() + public function getQuery(): array { return [ 'attribute' => $this->attribute, @@ -84,7 +84,7 @@ public function getQuery() * * @return Query * */ - public static function parse(string $filter) + public static function parse(string $filter): Query { // TODO@kodumbeats handle '.' in expression value $stanzas = mb_substr_count(mb_substr($filter, ".") + 1; From 06f02f74985424dc81a9ff1990c5eb6b88e345b7 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:04:10 -0400 Subject: [PATCH 09/45] Clean up code --- src/Database/Query.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 41d38157e..2a25a1e81 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -2,8 +2,6 @@ namespace Utopia\Database; -use Utopia\Database\Database; - class Query { /** @@ -87,7 +85,7 @@ public function getQuery(): array public static function parse(string $filter): Query { // TODO@kodumbeats handle '.' in expression value - $stanzas = mb_substr_count(mb_substr($filter, ".") + 1; + $stanzas = mb_substr_count($filter, ".") + 1; // TODO@kodumbeats handle relations between collections, e.g. if($stanzas > 2) switch ($stanzas): From e01fcc403f96a8be4a2f4a1abd66f52a936c443e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:04:44 -0400 Subject: [PATCH 10/45] Call parseExpression as a static method --- src/Database/Query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 2a25a1e81..2008925c2 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -92,7 +92,7 @@ public static function parse(string $filter): Query case 2: $input = explode('.', $filter); $attribute = $input[0]; - [$operator, $operand] = $this->parseExpression($input[1]); + [$operator, $operand] = Query::parseExpression($input[1]); break; endswitch; @@ -107,7 +107,7 @@ public static function parse(string $filter): Query * * @return array */ - protected function parseExpression(string $expression): array + public static function parseExpression(string $expression): array { //find location of parentheses in expression $start = mb_strpos($expression, '('); From 65a2166889461e146039e5a672709b61f2fec04e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:05:05 -0400 Subject: [PATCH 11/45] Add tests for query class --- tests/Database/QueryTest.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/Database/QueryTest.php diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php new file mode 100644 index 000000000..1e12e9b83 --- /dev/null +++ b/tests/Database/QueryTest.php @@ -0,0 +1,33 @@ +assertEquals('title', $query->getAttribute()); + $this->assertEquals('equal', $query->getOperator()); + $this->assertEquals('Iron Man', $query->getOperand()); + + $query = Query::parse('year.lesser(2001)'); + $this->assertEquals('year', $query->getAttribute()); + $this->assertEquals('lesser', $query->getOperator()); + $this->assertEquals(2001, $query->getOperand()); + } + +} \ No newline at end of file From 1bb848d04d6d2de6b9e399dd5fdbaef4b2d218e5 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:09:48 -0400 Subject: [PATCH 12/45] Refactor parsing code into query class and tests --- src/Database/Database.php | 94 ------------------------------------ tests/Database/Base.php | 40 --------------- tests/Database/QueryTest.php | 14 +++++- 3 files changed, 13 insertions(+), 135 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index b070b425a..fd0720ce6 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -785,98 +785,4 @@ public function getId(): string { return \uniqid(); } - - /** - * Get attribute key-value from query expression - * - * @param string $expression - * - * @return array - */ - public function parseExpression(string $expression): array - { - //find location of parentheses in expression - $start = mb_strpos($expression, '('); - $end = mb_strpos($expression, ')'); - - //extract the query method - $method = mb_substr($expression, 0, $start); - - //grab everything inside parentheses - $query = mb_substr($expression, - ($start + 1), /* exclude open paren*/ - ($end - $start - 1) /* exclude closed paren*/ - ); - - //strip quotes from queries of type string - $query = str_replace('"', "", $query); - $query = str_replace("'", "", $query); - - return [$method, $query]; - } - - /** - * Parse query filters - * - * @param string $collection Collection name - * @param array $filters Array of document filters - * - * @return array - * */ - public function parseQueries(string $collection, array $filters): array - { - $collection = $this->getCollection($collection); - $schema = $collection->getAttribute('attributes', []); - - $output = []; - foreach ($filters as &$filter) - { - $stanzas = mb_substr_count($filter, ".") + 1; - - switch ($stanzas): - case 2: - $input = explode('.', $filter); - - $attribute = $input[0]; - $expression = $input[1]; - [$method, $query] = $this->parseExpression($expression); - // $attributeType = $schema[\array_search($attribute, $schema)]['type']; - $attributeType = $schema[array_search($attribute, array_column($schema, '$id'))]['type']; - array_push($output, [ - 'collection' => $collection->getId(), - 'attribute' => $attribute, - 'method' => $method, - 'query' => $query, - 'queryType' => $attributeType, - ]); - break; - - // Copypasta code - remove/refactor when collection relations work - // - // case 3: - // $input = explode('.', $filter); - - // $attribute = $input[0]; - // $childAttribute = $input[1]; - // $expression = $input[2]; - // [$method, $query] = pickAttribute($expression); - // $attributeType = checkType($schema, $collection, $attribute, $childAttribute); - // array_push($output, [ - // 'input' => $filter, - // 'output' => [ - // [ - // 'collection' => $collection, - // 'attribute' => $attribute, - // 'childAttribute' => $childAttribute, - // 'method' => $method, - // 'query' => $query, - // 'queryType' => $attributeType, - // ] - // ], - // ]); - // break; - endswitch; - } - return $output; - } } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 20cb9bc5a..5fc3e0993 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -1559,44 +1559,4 @@ public function decodeTest() $this->assertEquals('1', '1'); } - - public function testParseQueries() - { - // Set up mock collections - $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('Movie')); - // $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('Director')); - // $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('Actor')); - $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'title', Database::VAR_STRING, 256)); - $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'year', Database::VAR_INTEGER, 0)); - $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'published', Database::VAR_BOOLEAN, 0)); - $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'director', Database::VAR_STRING, 256)); - $this->assertEquals(true, static::getDatabase()->createAttribute('Movie', 'actors', Database::VAR_STRING, 256, true, true)); - - // test parser - $query = static::getDatabase()->parseQueries('Movie', ['title.equal("Iron Man")']); - $this->assertEquals('Movie', $query[0]['collection']); - $this->assertEquals('title', $query[0]['attribute']); - $this->assertEquals('equal', $query[0]['method']); - $this->assertEquals('Iron Man', $query[0]['query']); - $this->assertEquals(Database::VAR_STRING, $query[0]['queryType']); - - $query = static::getDatabase()->parseQueries('Movie', ['year.lesser(2001)']); - $this->assertEquals('Movie', $query[0]['collection']); - $this->assertEquals('year', $query[0]['attribute']); - $this->assertEquals('lesser', $query[0]['method']); - $this->assertEquals(2001, $query[0]['query']); - $this->assertEquals(Database::VAR_INTEGER, $query[0]['queryType']); - } - - public function testParseExpression() - { - [$method, $query] = static::getDatabase()->parseExpression('equal("Spiderman")'); - $this->assertEquals('equal', $method); - $this->assertEquals('Spiderman', $query); - - - [$method, $query] = static::getDatabase()->parseExpression('lesser(2001)'); - $this->assertEquals('lesser', $method); - $this->assertEquals(2001, $query); - } } \ No newline at end of file diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 1e12e9b83..d74726bb4 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -16,7 +16,7 @@ public function tearDown(): void { } - public function testCreate() + public function testParse() { $query = Query::parse('title.equal("Iron Man")'); @@ -30,4 +30,16 @@ public function testCreate() $this->assertEquals(2001, $query->getOperand()); } + public function testParseExpression() + { + [$operator, $operand] = Query::parseExpression('equal("Spiderman")'); + $this->assertEquals('equal', $operator); + $this->assertEquals('Spiderman', $operand); + + + [$operator, $operand] = Query::parseExpression('lesser(2001)'); + $this->assertEquals('lesser', $operator); + $this->assertEquals(2001, $operand); + } + } \ No newline at end of file From e8021531558efb209428aa9be1b49a3c304ce802 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:26:07 -0400 Subject: [PATCH 13/45] Add type hinting --- src/Database/Query.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 2008925c2..b0924c8d8 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -23,8 +23,12 @@ class Query * Construct. * * Construct a new query object + * + * @param string $attribute + * @param string $operator + * @param mixed $operand */ - public function __construct($attribute, $operator, $operand) + public function __construct($attribute, $operator, $operand) { $this->attribute = $attribute; $this->operator = $operator; @@ -105,18 +109,25 @@ public static function parse(string $filter): Query * * @param string $expression * - * @return array + * @return (string|mixed)[] */ public static function parseExpression(string $expression): array { //find location of parentheses in expression + + /** @var int */ $start = mb_strpos($expression, '('); + /** @var int */ $end = mb_strpos($expression, ')'); //extract the query method + + /** @var string */ $operator = mb_substr($expression, 0, $start); //grab everything inside parentheses + + /** @var mixed */ $operand = mb_substr($expression, ($start + 1), /* exclude open paren*/ ($end - $start - 1) /* exclude closed paren*/ From c18b485e540e6ac7476e42e44084ea2d74080fa6 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:48:50 -0400 Subject: [PATCH 14/45] Add types to constructor params --- src/Database/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index b0924c8d8..ab51a691d 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -28,7 +28,7 @@ class Query * @param string $operator * @param mixed $operand */ - public function __construct($attribute, $operator, $operand) + public function __construct(string $attribute, string $operator, $operand) { $this->attribute = $attribute; $this->operator = $operator; From b2f6603dd7f099a7e54af60f52ab516a05abf077 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:54:14 -0400 Subject: [PATCH 15/45] Rename operand to value --- src/Database/Query.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index ab51a691d..3bb7f0b5d 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -17,7 +17,7 @@ class Query /** * @var mixed */ - protected $operand; + protected $value; /** * Construct. @@ -26,13 +26,13 @@ class Query * * @param string $attribute * @param string $operator - * @param mixed $operand + * @param mixed $value */ - public function __construct(string $attribute, string $operator, $operand) + public function __construct(string $attribute, string $operator, $value) { $this->attribute = $attribute; $this->operator = $operator; - $this->operand = $operand; + $this->value = $value; } /** @@ -96,11 +96,11 @@ public static function parse(string $filter): Query case 2: $input = explode('.', $filter); $attribute = $input[0]; - [$operator, $operand] = Query::parseExpression($input[1]); + [$operator, $value] = Query::parseExpression($input[1]); break; endswitch; - return new Query($attribute, $operator, $operand); + return new Query($attribute, $operator, $value); } /** @@ -128,16 +128,16 @@ public static function parseExpression(string $expression): array //grab everything inside parentheses /** @var mixed */ - $operand = mb_substr($expression, + $value = mb_substr($expression, ($start + 1), /* exclude open paren*/ ($end - $start - 1) /* exclude closed paren*/ ); //strip quotes from queries of type string - $operand = str_replace('"', "", $operand); - $operand = str_replace("'", "", $operand); + $value = str_replace('"', "", $value); + $value = str_replace("'", "", $value); - return [$operator, $operand]; + return [$operator, $value]; } // /** From f581824aed527bd2214eed598d60a944515fe3f1 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:55:09 -0400 Subject: [PATCH 16/45] Change to protected method --- src/Database/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 3bb7f0b5d..2487f438a 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -111,7 +111,7 @@ public static function parse(string $filter): Query * * @return (string|mixed)[] */ - public static function parseExpression(string $expression): array + protected static function parseExpression(string $expression): array { //find location of parentheses in expression From fe6becbf4389902dfb0e202c14b4672124b858d6 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 12:57:39 -0400 Subject: [PATCH 17/45] Use bracket syntax for switch --- src/Database/Query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 2487f438a..ca7017279 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -92,13 +92,13 @@ public static function parse(string $filter): Query $stanzas = mb_substr_count($filter, ".") + 1; // TODO@kodumbeats handle relations between collections, e.g. if($stanzas > 2) - switch ($stanzas): + switch ($stanzas) { case 2: $input = explode('.', $filter); $attribute = $input[0]; [$operator, $value] = Query::parseExpression($input[1]); break; - endswitch; + } return new Query($attribute, $operator, $value); } From 1c6e60c20f7afcd8639a55b8fb56c4acfb0cdf7d Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 13:08:46 -0400 Subject: [PATCH 18/45] Treat query expression as kv pair --- src/Database/Query.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index ca7017279..9e04034d0 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -25,14 +25,13 @@ class Query * Construct a new query object * * @param string $attribute - * @param string $operator + * @param array $expression * @param mixed $value */ - public function __construct(string $attribute, string $operator, $value) + public function __construct(string $attribute, array $expression) { $this->attribute = $attribute; - $this->operator = $operator; - $this->value = $value; + [$this->operator, $this->value] = $expression; } /** @@ -60,9 +59,9 @@ public function getOperator(): string * * @return mixed */ - public function getOperand() + public function getValue() { - return $this->operand; + return $this->value; } /** @@ -96,11 +95,11 @@ public static function parse(string $filter): Query case 2: $input = explode('.', $filter); $attribute = $input[0]; - [$operator, $value] = Query::parseExpression($input[1]); + $expression = Query::parseExpression($input[1]); break; } - return new Query($attribute, $operator, $value); + return new Query($attribute, $expression); } /** @@ -137,7 +136,7 @@ protected static function parseExpression(string $expression): array $value = str_replace('"', "", $value); $value = str_replace("'", "", $value); - return [$operator, $value]; + return [$operator => $value]; } // /** From 179a0d51da3db2313a9c7662ebcc593b68d0c1f0 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 14:51:23 -0400 Subject: [PATCH 19/45] Create query validator --- src/Database/Query.php | 13 +---- src/Database/Validator/Query.php | 88 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 src/Database/Validator/Query.php diff --git a/src/Database/Query.php b/src/Database/Query.php index 9e04034d0..6b188ee6e 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -105,6 +105,7 @@ public static function parse(string $filter): Query /** * Get attribute key-value from query expression * $expression: string with format 'operator(operand)' + * $expression: string with format 'operator(value)' * * @param string $expression * @@ -138,16 +139,4 @@ protected static function parseExpression(string $expression): array return [$operator => $value]; } - - // /** - // * Validate query against collection schema - // * - // * @param array $schema Structured array of collection attributes - // * - // * @return bool - // */ - // protected function isValid($schema): bool - // { - // $attributeType = $schema[array_search($attribute, array_column($schema, '$id'))]['type']; - // } } diff --git a/src/Database/Validator/Query.php b/src/Database/Validator/Query.php new file mode 100644 index 000000000..6bc38b909 --- /dev/null +++ b/src/Database/Validator/Query.php @@ -0,0 +1,88 @@ +schema = $schema; + } + + /** + * Get Description. + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return $this->message; + } + + /** + * Is valid. + * + * Returns true if query typed according to schema. + * + * @param Query $query + * + * @return bool + */ + public function isValid(Query $query) + { + // Extract the type of desired attribute from collection $schema + $attributeType = $this->schema[array_search($query->getAttribute(), array_column($this->schema, '$id'))]['type']; + + if ($attributeType === gettype($query->getValue())) { + return true; + } + + return false; + + } + /** + * 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; + } +} From 8ada10e25b6d2045262796f785f6539e41598bfe Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 14:53:45 -0400 Subject: [PATCH 20/45] Remove references to old var --- src/Database/Query.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 6b188ee6e..b6bfaf90b 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -74,7 +74,7 @@ public function getQuery(): array return [ 'attribute' => $this->attribute, 'operator' => $this->operator, - 'operand' => $this->operand, + 'operand' => $this->value, ]; } @@ -104,7 +104,6 @@ public static function parse(string $filter): Query /** * Get attribute key-value from query expression - * $expression: string with format 'operator(operand)' * $expression: string with format 'operator(value)' * * @param string $expression From 8934ce72169d79b4071d24d0bcbf97d401edc219 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 14:54:18 -0400 Subject: [PATCH 21/45] Revert "Change to protected method" This reverts commit f581824aed527bd2214eed598d60a944515fe3f1. --- src/Database/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index b6bfaf90b..b7665d25b 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -110,7 +110,7 @@ public static function parse(string $filter): Query * * @return (string|mixed)[] */ - protected static function parseExpression(string $expression): array + public static function parseExpression(string $expression): array { //find location of parentheses in expression From 38b02cf39c1d8cdc934edb34749f7c6db6758580 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 15:15:52 -0400 Subject: [PATCH 22/45] Revert "Treat query expression as kv pair" This reverts commit 1c6e60c20f7afcd8639a55b8fb56c4acfb0cdf7d. --- src/Database/Query.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index b7665d25b..2ade8464a 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -25,13 +25,14 @@ class Query * Construct a new query object * * @param string $attribute - * @param array $expression + * @param string $operator * @param mixed $value */ - public function __construct(string $attribute, array $expression) + public function __construct(string $attribute, string $operator, $value) { $this->attribute = $attribute; - [$this->operator, $this->value] = $expression; + $this->operator = $operator; + $this->value = $value; } /** @@ -59,9 +60,9 @@ public function getOperator(): string * * @return mixed */ - public function getValue() + public function getOperand() { - return $this->value; + return $this->operand; } /** @@ -95,11 +96,11 @@ public static function parse(string $filter): Query case 2: $input = explode('.', $filter); $attribute = $input[0]; - $expression = Query::parseExpression($input[1]); + [$operator, $value] = Query::parseExpression($input[1]); break; } - return new Query($attribute, $expression); + return new Query($attribute, $operator, $value); } /** @@ -136,6 +137,6 @@ public static function parseExpression(string $expression): array $value = str_replace('"', "", $value); $value = str_replace("'", "", $value); - return [$operator => $value]; + return [$operator, $value]; } } From c2b9dc268a3034a3055404a5ad31c736192f00bd Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 15:18:52 -0400 Subject: [PATCH 23/45] Use value instead of operand --- src/Database/Query.php | 4 ++-- tests/Database/QueryTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 2ade8464a..5cfb46b6e 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -60,9 +60,9 @@ public function getOperator(): string * * @return mixed */ - public function getOperand() + public function getValue() { - return $this->operand; + return $this->value; } /** diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index d74726bb4..e326c146f 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -22,12 +22,12 @@ public function testParse() $this->assertEquals('title', $query->getAttribute()); $this->assertEquals('equal', $query->getOperator()); - $this->assertEquals('Iron Man', $query->getOperand()); + $this->assertEquals('Iron Man', $query->getValue()); $query = Query::parse('year.lesser(2001)'); $this->assertEquals('year', $query->getAttribute()); $this->assertEquals('lesser', $query->getOperator()); - $this->assertEquals(2001, $query->getOperand()); + $this->assertEquals(2001, $query->getValue()); } public function testParseExpression() @@ -37,9 +37,9 @@ public function testParseExpression() $this->assertEquals('Spiderman', $operand); - [$operator, $operand] = Query::parseExpression('lesser(2001)'); + [$operator, $value] = Query::parseExpression('lesser(2001)'); $this->assertEquals('lesser', $operator); - $this->assertEquals(2001, $operand); + $this->assertEquals(2001, $value); } } \ No newline at end of file From 70719234c8757775bcdc8ffc1fa0e1d84ab88d88 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 15:57:28 -0400 Subject: [PATCH 24/45] Clarify intended behavior for parseExpression() --- src/Database/Query.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 5cfb46b6e..d6ba49437 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -96,7 +96,8 @@ public static function parse(string $filter): Query case 2: $input = explode('.', $filter); $attribute = $input[0]; - [$operator, $value] = Query::parseExpression($input[1]); + $expression = $input[1]; + [$operator, $value] = self::parseExpression($expression); break; } @@ -111,7 +112,7 @@ public static function parse(string $filter): Query * * @return (string|mixed)[] */ - public static function parseExpression(string $expression): array + protected static function parseExpression(string $expression): array { //find location of parentheses in expression From 0ec83e0529716be2f8e80c50e3b5ef1e19380724 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 16:37:23 -0400 Subject: [PATCH 25/45] Add and fix query tests --- src/Database/Query.php | 4 ++-- tests/Database/QueryTest.php | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index d6ba49437..16ebaca83 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -75,7 +75,7 @@ public function getQuery(): array return [ 'attribute' => $this->attribute, 'operator' => $this->operator, - 'operand' => $this->value, + 'value' => $this->value, ]; } @@ -112,7 +112,7 @@ public static function parse(string $filter): Query * * @return (string|mixed)[] */ - protected static function parseExpression(string $expression): array + public static function parseExpression(string $expression): array { //find location of parentheses in expression diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index e326c146f..c03c8aa12 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -32,9 +32,9 @@ public function testParse() public function testParseExpression() { - [$operator, $operand] = Query::parseExpression('equal("Spiderman")'); + [$operator, $value] = Query::parseExpression('equal("Spiderman")'); $this->assertEquals('equal', $operator); - $this->assertEquals('Spiderman', $operand); + $this->assertEquals('Spiderman', $value); [$operator, $value] = Query::parseExpression('lesser(2001)'); @@ -42,4 +42,35 @@ public function testParseExpression() $this->assertEquals(2001, $value); } + public function testGetAttribute() + { + $query = Query::parse('title.equal("Iron Man")'); + + $this->assertEquals('title', $query->getAttribute()); + } + + public function testGetOperator() + { + $query = Query::parse('title.equal("Iron Man")'); + + $this->assertEquals('equal', $query->getOperator()); + } + + public function testGetValue() + { + $query = Query::parse('title.equal("Iron Man")'); + + $this->assertEquals('Iron Man', $query->getValue()); + } + + public function testGetQuery() + { + $parsed = Query::parse('title.equal("Iron Man")'); + $query = $parsed->getQuery(); + + $this->assertEquals('title', $query['attribute']); + $this->assertEquals('equal', $query['operator']); + $this->assertEquals('Iron Man', $query['value']); + } + } \ No newline at end of file From e3f1efb43942518b2d8a3eb59a3c41e7ca0411cc Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 17:11:39 -0400 Subject: [PATCH 26/45] Constructor expects array of $values by default --- src/Database/Query.php | 28 +++++++++++++++++----------- tests/Database/QueryTest.php | 16 ++++++++-------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 16ebaca83..78e930bab 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -15,9 +15,9 @@ class Query protected $operator = ''; /** - * @var mixed + * @var (mixed)[] */ - protected $value; + protected $values; /** * Construct. @@ -26,13 +26,13 @@ class Query * * @param string $attribute * @param string $operator - * @param mixed $value + * @param array $values */ - public function __construct(string $attribute, string $operator, $value) + public function __construct(string $attribute, string $operator, array $values) { $this->attribute = $attribute; $this->operator = $operator; - $this->value = $value; + $this->values = $values; } /** @@ -60,9 +60,9 @@ public function getOperator(): string * * @return mixed */ - public function getValue() + public function getValues() { - return $this->value; + return $this->values; } /** @@ -75,7 +75,7 @@ public function getQuery(): array return [ 'attribute' => $this->attribute, 'operator' => $this->operator, - 'value' => $this->value, + 'values' => $this->values, ]; } @@ -97,11 +97,11 @@ public static function parse(string $filter): Query $input = explode('.', $filter); $attribute = $input[0]; $expression = $input[1]; - [$operator, $value] = self::parseExpression($expression); + [$operator, $values] = self::parseExpression($expression); break; } - return new Query($attribute, $operator, $value); + return new Query($attribute, $operator, $values); } /** @@ -110,7 +110,7 @@ public static function parse(string $filter): Query * * @param string $expression * - * @return (string|mixed)[] + * @return (string|array)[] */ public static function parseExpression(string $expression): array { @@ -138,6 +138,12 @@ public static function parseExpression(string $expression): array $value = str_replace('"', "", $value); $value = str_replace("'", "", $value); + // if $value is not array, return array with single $value + // TODO@kodumbeats appropriately cast type of ints, floats, bools + if (gettype($value) !== 'array') { + $value = [$value]; + } + return [$operator, $value]; } } diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index c03c8aa12..f4f5dc1f3 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -22,24 +22,24 @@ public function testParse() $this->assertEquals('title', $query->getAttribute()); $this->assertEquals('equal', $query->getOperator()); - $this->assertEquals('Iron Man', $query->getValue()); + $this->assertContains('Iron Man', $query->getValues()); $query = Query::parse('year.lesser(2001)'); $this->assertEquals('year', $query->getAttribute()); $this->assertEquals('lesser', $query->getOperator()); - $this->assertEquals(2001, $query->getValue()); + $this->assertContains('2001', $query->getValues()); } public function testParseExpression() { - [$operator, $value] = Query::parseExpression('equal("Spiderman")'); + [$operator, $values] = Query::parseExpression('equal("Spiderman")'); $this->assertEquals('equal', $operator); - $this->assertEquals('Spiderman', $value); + $this->assertContains('Spiderman', $values); - [$operator, $value] = Query::parseExpression('lesser(2001)'); + [$operator, $values] = Query::parseExpression('lesser(2001)'); $this->assertEquals('lesser', $operator); - $this->assertEquals(2001, $value); + $this->assertContains('2001', $values); } public function testGetAttribute() @@ -60,7 +60,7 @@ public function testGetValue() { $query = Query::parse('title.equal("Iron Man")'); - $this->assertEquals('Iron Man', $query->getValue()); + $this->assertContains('Iron Man', $query->getValues()); } public function testGetQuery() @@ -70,7 +70,7 @@ public function testGetQuery() $this->assertEquals('title', $query['attribute']); $this->assertEquals('equal', $query['operator']); - $this->assertEquals('Iron Man', $query['value']); + $this->assertContains('Iron Man', $query['values']); } } \ No newline at end of file From 0659fe8787a8e12b46a62a1da56584b5ba4de524 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 23 Apr 2021 17:20:47 -0400 Subject: [PATCH 27/45] Properly scope parseExpression --- src/Database/Query.php | 2 +- tests/Database/QueryTest.php | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 78e930bab..3b9403de3 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -112,7 +112,7 @@ public static function parse(string $filter): Query * * @return (string|array)[] */ - public static function parseExpression(string $expression): array + protected static function parseExpression(string $expression): array { //find location of parentheses in expression diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index f4f5dc1f3..1b97e4847 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -30,18 +30,6 @@ public function testParse() $this->assertContains('2001', $query->getValues()); } - public function testParseExpression() - { - [$operator, $values] = Query::parseExpression('equal("Spiderman")'); - $this->assertEquals('equal', $operator); - $this->assertContains('Spiderman', $values); - - - [$operator, $values] = Query::parseExpression('lesser(2001)'); - $this->assertEquals('lesser', $operator); - $this->assertContains('2001', $values); - } - public function testGetAttribute() { $query = Query::parse('title.equal("Iron Man")'); From 6b3bcd2cdf1918f16ecdfea788946c75986da079 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 11:41:47 -0400 Subject: [PATCH 28/45] Typecast expression values and handle comma-separated values --- src/Database/Query.php | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 3b9403de3..11605af19 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -134,16 +134,32 @@ protected static function parseExpression(string $expression): array ($end - $start - 1) /* exclude closed paren*/ ); - //strip quotes from queries of type string - $value = str_replace('"', "", $value); - $value = str_replace("'", "", $value); - - // if $value is not array, return array with single $value - // TODO@kodumbeats appropriately cast type of ints, floats, bools - if (gettype($value) !== 'array') { - $value = [$value]; - } + // Explode comma-separated values and trim whitespace + + $values = array_map('trim', explode(',', $value)); + + // strip quotes from queries of type string + + $values = array_map(function ($str) { + return str_replace(['"',"'"], "", $str); + }, $values); + + // type cast ints, floats, and bools + + $values = array_map(function ($value) { + // type casted to int or float by "+" operator + if (is_numeric($value)) { + return $value + 0; + // since (bool)"false" returns true, check bools manually + } elseif ($value === 'true') { + return true; + } elseif ($value === 'false') { + return false; + } else { + return $value; + } + }, $values); - return [$operator, $value]; + return [$operator, $values]; } } From a1b472d39271c1175af307d7ada6d607d289d9ed Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 11:42:52 -0400 Subject: [PATCH 29/45] Test with integer param --- tests/Database/QueryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 1b97e4847..02757a84b 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -27,7 +27,7 @@ public function testParse() $query = Query::parse('year.lesser(2001)'); $this->assertEquals('year', $query->getAttribute()); $this->assertEquals('lesser', $query->getOperator()); - $this->assertContains('2001', $query->getValues()); + $this->assertContains(2001, $query->getValues()); } public function testGetAttribute() From e74da85bf7fb0776af289f004a2c6e125849f0c2 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 11:45:35 -0400 Subject: [PATCH 30/45] Test for typecasted bool expressions --- tests/Database/QueryTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 02757a84b..81beb1ea8 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -28,6 +28,16 @@ public function testParse() $this->assertEquals('year', $query->getAttribute()); $this->assertEquals('lesser', $query->getOperator()); $this->assertContains(2001, $query->getValues()); + + $query = Query::parse('published.equal(true)'); + $this->assertEquals('published', $query->getAttribute()); + $this->assertEquals('equal', $query->getOperator()); + $this->assertContains(true, $query->getValues()); + + $query = Query::parse('published.equal(false)'); + $this->assertEquals('published', $query->getAttribute()); + $this->assertEquals('equal', $query->getOperator()); + $this->assertContains(false, $query->getValues()); } public function testGetAttribute() From f7e86b6b6f3a0d6a070e663fa85fdc661a122d72 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 11:46:50 -0400 Subject: [PATCH 31/45] Test multiple query values --- tests/Database/QueryTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 81beb1ea8..010767b88 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -38,6 +38,13 @@ public function testParse() $this->assertEquals('published', $query->getAttribute()); $this->assertEquals('equal', $query->getOperator()); $this->assertContains(false, $query->getValues()); + + $query = Query::parse('actors.equal("Brad Pitt", "Johnny Depp")'); + + $this->assertEquals('actors', $query->getAttribute()); + $this->assertEquals('equal', $query->getOperator()); + $this->assertContains('Brad Pitt', $query->getValues()); + $this->assertContains('Johnny Depp', $query->getValues()); } public function testGetAttribute() From a14a73046080fbe9d96fe290fa90667648b437fb Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 11:47:20 -0400 Subject: [PATCH 32/45] Cleanup for readability --- tests/Database/QueryTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 010767b88..24c6b903c 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -25,16 +25,19 @@ public function testParse() $this->assertContains('Iron Man', $query->getValues()); $query = Query::parse('year.lesser(2001)'); + $this->assertEquals('year', $query->getAttribute()); $this->assertEquals('lesser', $query->getOperator()); $this->assertContains(2001, $query->getValues()); $query = Query::parse('published.equal(true)'); + $this->assertEquals('published', $query->getAttribute()); $this->assertEquals('equal', $query->getOperator()); $this->assertContains(true, $query->getValues()); $query = Query::parse('published.equal(false)'); + $this->assertEquals('published', $query->getAttribute()); $this->assertEquals('equal', $query->getOperator()); $this->assertContains(false, $query->getValues()); @@ -70,8 +73,7 @@ public function testGetValue() public function testGetQuery() { - $parsed = Query::parse('title.equal("Iron Man")'); - $query = $parsed->getQuery(); + $query = Query::parse('title.equal("Iron Man")')->getQuery(); $this->assertEquals('title', $query['attribute']); $this->assertEquals('equal', $query['operator']); From 631ecfd0a0447ae148185300100a644b9ce327ee Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 11:51:36 -0400 Subject: [PATCH 33/45] Reserve query validator for separate PR --- src/Database/Validator/Query.php | 88 -------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 src/Database/Validator/Query.php diff --git a/src/Database/Validator/Query.php b/src/Database/Validator/Query.php deleted file mode 100644 index 6bc38b909..000000000 --- a/src/Database/Validator/Query.php +++ /dev/null @@ -1,88 +0,0 @@ -schema = $schema; - } - - /** - * Get Description. - * - * Returns validator description - * - * @return string - */ - public function getDescription() - { - return $this->message; - } - - /** - * Is valid. - * - * Returns true if query typed according to schema. - * - * @param Query $query - * - * @return bool - */ - public function isValid(Query $query) - { - // Extract the type of desired attribute from collection $schema - $attributeType = $this->schema[array_search($query->getAttribute(), array_column($this->schema, '$id'))]['type']; - - if ($attributeType === gettype($query->getValue())) { - return true; - } - - return false; - - } - /** - * 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; - } -} From 4d6189be45acf4a356b9054b1ac404c2bdc8f85c Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 13:53:50 -0400 Subject: [PATCH 34/45] Only consider periods oudside parentheses --- src/Database/Query.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 11605af19..260c34b0d 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -88,13 +88,16 @@ public function getQuery(): array * */ public static function parse(string $filter): Query { - // TODO@kodumbeats handle '.' in expression value - $stanzas = mb_substr_count($filter, ".") + 1; + // get index of open parentheses + $end = mb_strpos($filter, '('); + // count stanzas by only counting '.' that come before open parentheses + $stanzas = mb_substr_count(mb_substr($filter, 0, $end), ".") + 1; // TODO@kodumbeats handle relations between collections, e.g. if($stanzas > 2) switch ($stanzas) { case 2: - $input = explode('.', $filter); + // use limit param to ignore '.' in $expression + $input = explode('.', $filter, $stanzas); $attribute = $input[0]; $expression = $input[1]; [$operator, $values] = self::parseExpression($expression); From 1f4c64f322ce98f7bc27ad492adf5f71d40afb3e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 14:53:26 -0400 Subject: [PATCH 35/45] Test for float values --- tests/Database/QueryTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 24c6b903c..28f47d25a 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -48,6 +48,12 @@ public function testParse() $this->assertEquals('equal', $query->getOperator()); $this->assertContains('Brad Pitt', $query->getValues()); $this->assertContains('Johnny Depp', $query->getValues()); + + $query = Query::parse('score.greater(8.5)'); + + $this->assertEquals('score', $query->getAttribute()); + $this->assertEquals('greater', $query->getOperator()); + $this->assertContains(8.5, $query->getValues()); } public function testGetAttribute() From fa2828610c2313fda94cc92c1a5a2bf459fd733a Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 14:53:54 -0400 Subject: [PATCH 36/45] Handle null as value --- src/Database/Query.php | 20 ++++++++++---------- tests/Database/QueryTest.php | 12 ++++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 260c34b0d..12d3faecc 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -141,25 +141,25 @@ protected static function parseExpression(string $expression): array $values = array_map('trim', explode(',', $value)); - // strip quotes from queries of type string - - $values = array_map(function ($str) { - return str_replace(['"',"'"], "", $str); - }, $values); - - // type cast ints, floats, and bools + // type cast ints, floats, bools, and null $values = array_map(function ($value) { - // type casted to int or float by "+" operator if (is_numeric($value)) { + // type casted to int or float by "+" operator return $value + 0; - // since (bool)"false" returns true, check bools manually } elseif ($value === 'true') { + // since (bool)"false" returns true, check bools manually return true; } elseif ($value === 'false') { return false; + } elseif ($value === '"null"') { + // need special case to handle null with or without quotes + return 'null'; + } elseif ($value === 'null') { + return null; } else { - return $value; + // strip quotes from queries of type string + return str_replace(['"',"'"], "", $value); } }, $values); diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 28f47d25a..c258dc356 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -54,6 +54,18 @@ public function testParse() $this->assertEquals('score', $query->getAttribute()); $this->assertEquals('greater', $query->getOperator()); $this->assertContains(8.5, $query->getValues()); + + $query = Query::parse('director.notEqual("null")'); + + $this->assertEquals('director', $query->getAttribute()); + $this->assertEquals('notEqual', $query->getOperator()); + $this->assertContains('null', $query->getValues()); + + $query = Query::parse('director.notEqual(null)'); + + $this->assertEquals('director', $query->getAttribute()); + $this->assertEquals('notEqual', $query->getOperator()); + $this->assertContains(null, $query->getValues()); } public function testGetAttribute() From 2ec2fc83b35c26b840b94b9f175a0525f17a6c64 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 15:21:57 -0400 Subject: [PATCH 37/45] Refactor elseifs into switch --- src/Database/Query.php | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 12d3faecc..ecf092462 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -141,25 +141,31 @@ protected static function parseExpression(string $expression): array $values = array_map('trim', explode(',', $value)); - // type cast ints, floats, bools, and null + // Cast $value type $values = array_map(function ($value) { - if (is_numeric($value)) { + switch (true) { // type casted to int or float by "+" operator - return $value + 0; - } elseif ($value === 'true') { + case is_numeric($value): + return $value + 0; + // since (bool)"false" returns true, check bools manually - return true; - } elseif ($value === 'false') { - return false; - } elseif ($value === '"null"') { + case $value === 'true': + return true; + + case $value === 'false': + return false; + // need special case to handle null with or without quotes - return 'null'; - } elseif ($value === 'null') { - return null; - } else { + case $value === '"null"': + return 'null'; + + case $value === 'null': + return null; + // strip quotes from queries of type string - return str_replace(['"',"'"], "", $value); + default: + return str_replace(['"',"'"], "", $value); } }, $values); From ab2a6b7d55fe1c3deaafd8affa52064877890a8f Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 15:25:12 -0400 Subject: [PATCH 38/45] Use single array_map to trim whitespace and typecast --- src/Database/Query.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index ecf092462..1964de374 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -137,13 +137,16 @@ protected static function parseExpression(string $expression): array ($end - $start - 1) /* exclude closed paren*/ ); - // Explode comma-separated values and trim whitespace + // Explode comma-separated values - $values = array_map('trim', explode(',', $value)); + $values = explode(',', $value); // Cast $value type $values = array_map(function ($value) { + // trim whitespace + $value = trim($value); + switch (true) { // type casted to int or float by "+" operator case is_numeric($value): @@ -156,10 +159,10 @@ protected static function parseExpression(string $expression): array case $value === 'false': return false; - // need special case to handle null with or without quotes case $value === '"null"': return 'null'; + // need special case to handle null with or without quotes case $value === 'null': return null; @@ -167,6 +170,7 @@ protected static function parseExpression(string $expression): array default: return str_replace(['"',"'"], "", $value); } + }, $values); return [$operator, $values]; From b01d9dddcf32be6cdc4211a89426d005165573bf Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 15:29:46 -0400 Subject: [PATCH 39/45] Remove unneeded switch case --- src/Database/Query.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 1964de374..c9eee8a32 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -159,9 +159,6 @@ protected static function parseExpression(string $expression): array case $value === 'false': return false; - case $value === '"null"': - return 'null'; - // need special case to handle null with or without quotes case $value === 'null': return null; From 7db5c8fc28ee5c16d06e508cee98c5d446cd56e6 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 15:52:25 -0400 Subject: [PATCH 40/45] Trim whitespace and remove escape slashes --- src/Database/Query.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index c9eee8a32..3ee13974f 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -144,8 +144,6 @@ protected static function parseExpression(string $expression): array // Cast $value type $values = array_map(function ($value) { - // trim whitespace - $value = trim($value); switch (true) { // type casted to int or float by "+" operator @@ -163,9 +161,11 @@ protected static function parseExpression(string $expression): array case $value === 'null': return null; - // strip quotes from queries of type string default: - return str_replace(['"',"'"], "", $value); + // strip escape characters + $value = stripslashes($value); + // trim leading and tailing quotes and whitespace + return trim($value, '\'" '); } }, $values); From 40cd42c84f6340a3bf13f6761d3f28a0cfe4c6e3 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 15:52:42 -0400 Subject: [PATCH 41/45] Test for whitespace and escaped character --- tests/Database/QueryTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index c258dc356..89230b56a 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -42,6 +42,12 @@ public function testParse() $this->assertEquals('equal', $query->getOperator()); $this->assertContains(false, $query->getValues()); + $query = Query::parse('actors.notContains(" Johnny Depp ")'); + + $this->assertEquals('actors', $query->getAttribute()); + $this->assertEquals('notContains', $query->getOperator()); + $this->assertContains('Johnny Depp', $query->getValues()); + $query = Query::parse('actors.equal("Brad Pitt", "Johnny Depp")'); $this->assertEquals('actors', $query->getAttribute()); @@ -49,6 +55,12 @@ public function testParse() $this->assertContains('Brad Pitt', $query->getValues()); $this->assertContains('Johnny Depp', $query->getValues()); + $query = Query::parse('writers.contains("Tim O\'Reilly")'); + + $this->assertEquals('writers', $query->getAttribute()); + $this->assertEquals('contains', $query->getOperator()); + $this->assertContains("Tim O'Reilly", $query->getValues()); + $query = Query::parse('score.greater(8.5)'); $this->assertEquals('score', $query->getAttribute()); From 53cf8dc9d29dbdac1c3c72bec5e074f44464f00e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 16:29:48 -0400 Subject: [PATCH 42/45] Use strrpos to find last occurrence of parenthesis --- src/Database/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 3ee13974f..e976e71ea 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -122,7 +122,7 @@ protected static function parseExpression(string $expression): array /** @var int */ $start = mb_strpos($expression, '('); /** @var int */ - $end = mb_strpos($expression, ')'); + $end = mb_strrpos($expression, ')'); //extract the query method From ba852c2263494037e545fb4b4abc61d8149d1c12 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 16:30:02 -0400 Subject: [PATCH 43/45] Correct comments --- src/Database/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index e976e71ea..e73f56ae6 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -157,7 +157,7 @@ protected static function parseExpression(string $expression): array case $value === 'false': return false; - // need special case to handle null with or without quotes + // need special case to cast (null) as null, not string case $value === 'null': return null; From 87dd5b30ba2f8fd38917ee82cd206ac3af4d7b99 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 16:36:22 -0400 Subject: [PATCH 44/45] Test constructor function --- tests/Database/QueryTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 89230b56a..402925341 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -16,6 +16,15 @@ public function tearDown(): void { } + public function testCreate(): void + { + $query = new Query('title', 'equal', ['Iron Man']); + + $this->assertEquals('title', $query->getAttribute()); + $this->assertEquals('equal', $query->getOperator()); + $this->assertContains('Iron Man', $query->getValues()); + } + public function testParse() { $query = Query::parse('title.equal("Iron Man")'); From 1522284cf0fcc6c73031ee25547030ed33c8c890 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Apr 2021 17:39:59 -0400 Subject: [PATCH 45/45] Properly handle whitespace between values --- src/Database/Query.php | 8 ++++++-- tests/Database/QueryTest.php | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index e73f56ae6..00a56e8f7 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -145,6 +145,10 @@ protected static function parseExpression(string $expression): array $values = array_map(function ($value) { + // Trim whitespace from around $value + + $value = trim($value); + switch (true) { // type casted to int or float by "+" operator case is_numeric($value): @@ -164,8 +168,8 @@ protected static function parseExpression(string $expression): array default: // strip escape characters $value = stripslashes($value); - // trim leading and tailing quotes and whitespace - return trim($value, '\'" '); + // trim leading and tailing quotes + return trim($value, '\'"'); } }, $values); diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 402925341..f4811e92d 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -51,11 +51,13 @@ public function testParse() $this->assertEquals('equal', $query->getOperator()); $this->assertContains(false, $query->getValues()); - $query = Query::parse('actors.notContains(" Johnny Depp ")'); + $query = Query::parse('actors.notContains( " Johnny Depp ", " Brad Pitt" , "Al Pacino")'); $this->assertEquals('actors', $query->getAttribute()); $this->assertEquals('notContains', $query->getOperator()); - $this->assertContains('Johnny Depp', $query->getValues()); + $this->assertContains(' Johnny Depp ', $query->getValues()); + $this->assertContains(' Brad Pitt', $query->getValues()); + $this->assertContains('Al Pacino', $query->getValues()); $query = Query::parse('actors.equal("Brad Pitt", "Johnny Depp")');