diff --git a/grammar/query.pp b/grammar/query.pp index 7bf7341d2a..098ae65816 100644 --- a/grammar/query.pp +++ b/grammar/query.pp @@ -73,11 +73,11 @@ key_value_pair: | ::flag_prefix:: flag() ::colon:: ::space::? boolean() #flag_statement | ::field_prefix:: field() ::colon:: ::space::? term() #field_statement | field() ::colon:: ::space::? term() #field_statement - | key() ::space::? ::lt:: ::space::? value() #less_than - | key() ::space::? ::gt:: ::space::? value() #greater_than - | key() ::space::? ::lte:: ::space::? value() #less_than_or_equal_to - | key() ::space::? ::gte:: ::space::? value() #greater_than_or_equal_to - | field() ::space::? ::equal:: ::space::? value() #equal_to + | key() ::space::? ::lt:: ::space::? value() #less_than + | key() ::space::? ::gt:: ::space::? value() #greater_than + | key() ::space::? ::lte:: ::space::? value() #less_than_or_equal_to + | key() ::space::? ::gte:: ::space::? value() #greater_than_or_equal_to + | key() ::space::? ::equal:: ::space::? value() #equal_to #native_key: diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/FieldEqualsExpression.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/FieldEqualsExpression.php index 8a4b93ca0c..e514fcf312 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/FieldEqualsExpression.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/FieldEqualsExpression.php @@ -2,38 +2,41 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\AST; +use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\FieldKey; +use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Key; use Alchemy\Phrasea\SearchEngine\Elastic\Exception\QueryException; use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext; use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryHelper; +use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryPostProcessor; class FieldEqualsExpression extends Node { - private $field; + private $key; private $value; - public function __construct(Field $field, $value) + public function __construct(Key $key, $value) { - $this->field = $field; + $this->key = $key; $this->value = $value; } public function buildQuery(QueryContext $context) { - $structure_field = $context->get($this->field); - if (!$structure_field) { - throw new QueryException(sprintf('Field "%s" does not exist', $this->field->getValue())); - } - if (!$structure_field->isValueCompatible($this->value)) { + if (!$this->key->isValueCompatible($this->value, $context)) { return null; } $query = [ 'term' => [ - $structure_field->getIndexField(true) => $this->value + $this->key->getIndexField($context, true) => $this->value ] ]; - return QueryHelper::wrapPrivateFieldQuery($structure_field, $query); + if ($this->key instanceof QueryPostProcessor) { + return $this->key->postProcessQuery($query, $context); + } + + return $query; } public function getTermNodes() @@ -43,6 +46,6 @@ class FieldEqualsExpression extends Node public function __toString() { - return sprintf('(%s == )', $this->field, $this->value); + return sprintf('(<%s> == )', $this->key, $this->value); } } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/KeyValue/FieldKey.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/KeyValue/FieldKey.php index 84ebed4bbf..e238b41cb1 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/KeyValue/FieldKey.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/AST/KeyValue/FieldKey.php @@ -19,9 +19,9 @@ class FieldKey implements Key, QueryPostProcessor $this->name = $name; } - public function getIndexField(QueryContext $context, ...$other) + public function getIndexField(QueryContext $context, $raw = false) { - return $this->getField($context)->getIndexField(...$other); + return $this->getField($context)->getIndexField($raw); } public function isValueCompatible($value, QueryContext $context) @@ -41,7 +41,8 @@ class FieldKey implements Key, QueryPostProcessor if (!isset($this->field_cache[$hash])) { $this->field_cache[$hash] = $context->get($this->name); } - if ($field = $this->field_cache[$hash] === null) { + $field = $this->field_cache[$hash]; + if ($field === null) { throw new QueryException(sprintf('Field "%s" does not exist', $this->name)); } return $field; diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/AST/FieldEqualsExpressionTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/AST/FieldEqualsExpressionTest.php index 99dbd0fb6c..d686efffe3 100644 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/AST/FieldEqualsExpressionTest.php +++ b/tests/Alchemy/Tests/Phrasea/SearchEngine/AST/FieldEqualsExpressionTest.php @@ -2,8 +2,8 @@ namespace Alchemy\Tests\Phrasea\SearchEngine\AST; -use Alchemy\Phrasea\SearchEngine\Elastic\AST\Field as ASTField; use Alchemy\Phrasea\SearchEngine\Elastic\AST\FieldEqualsExpression; +use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Key; use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext; use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Field as StructureField; @@ -17,10 +17,10 @@ class FieldEqualsExpressionTest extends \PHPUnit_Framework_TestCase public function testSerialization() { $this->assertTrue(method_exists(FieldEqualsExpression::class, '__toString'), 'Class does not have method __toString'); - $field = $this->prophesize(ASTField::class); - $field->__toString()->willReturn('foo'); - $node = new FieldEqualsExpression($field->reveal(), 'bar'); - $this->assertEquals('(foo == )', (string) $node); + $key = $this->prophesize(Key::class); + $key->__toString()->willReturn('foo'); + $node = new FieldEqualsExpression($key->reveal(), 'bar'); + $this->assertEquals('( == )', (string) $node); } /** @@ -28,20 +28,15 @@ class FieldEqualsExpressionTest extends \PHPUnit_Framework_TestCase */ public function testQueryBuild($index_field, $value, $compatible_value, $private, $expected_json) { - $structure_field = $this->prophesize(StructureField::class); - $structure_field->isValueCompatible($value)->willReturn($compatible_value); - $structure_field->getIndexField(true)->willReturn($index_field); - $structure_field->isPrivate()->willReturn($private); - if ($private) { - $structure_field->getDependantCollections()->willReturn(['baz', 'qux']); - } + $query_context = $this->prophesize(QueryContext::class)->reveal(); - $ast_field = $this->prophesize(ASTField::class); - $query_context = $this->prophesize(QueryContext::class); - $query_context->get($ast_field->reveal())->willReturn($structure_field); + $key = $this->prophesize(Key::class); + $key->isValueCompatible($value, $query_context)->willReturn($compatible_value); + $key->getIndexField($query_context, true)->willReturn($index_field); + // TODO Test keys implementing QueryPostProcessor - $node = new FieldEqualsExpression($ast_field->reveal(), 'bar'); - $query = $node->buildQuery($query_context->reveal()); + $node = new FieldEqualsExpression($key->reveal(), 'bar'); + $query = $node->buildQuery($query_context); $this->assertEquals(json_decode($expected_json, true), $query); } @@ -49,14 +44,15 @@ class FieldEqualsExpressionTest extends \PHPUnit_Framework_TestCase public function queryProvider() { return [ - ['foo.raw', 'bar', true, true, '{ - "filtered": { - "filter": { - "terms": { - "base_id": ["baz","qux"] } }, - "query": { - "term": { - "foo.raw": "bar" } } } }'], + // TODO Put this case in another test case + // ['foo.raw', 'bar', true, true, '{ + // "filtered": { + // "filter": { + // "terms": { + // "base_id": ["baz","qux"] } }, + // "query": { + // "term": { + // "foo.raw": "bar" } } } }'], ['foo.raw', 'bar', true, false, '{ "term": { "foo.raw": "bar" } }'], diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/resources/queries.csv b/tests/Alchemy/Tests/Phrasea/SearchEngine/resources/queries.csv index fa1b37a72b..53e39de0d9 100644 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/resources/queries.csv +++ b/tests/Alchemy/Tests/Phrasea/SearchEngine/resources/queries.csv @@ -61,9 +61,9 @@ foo < "2015/01/01"| foo ≤ "2015/01/01"| foo > "2015/01/01"| foo ≥ "2015/01/01"| -foo = 42|( == ) -foo = bar|( == ) -foo = "bar"|( == ) +foo = 42|( == ) +foo = bar|( == ) +foo = "bar"|( == ) # Field narrowing foo:bar|( MATCHES ) @@ -109,6 +109,7 @@ meta.Duration < 300| meta.Duration ≤ 300| meta.Duration > 300| meta.Duration ≥ 300| +meta.Duration = 300|( == ) # Unescaped "." issue on key prefixes fieldOne:foo|( MATCHES )