[skip ci] PHRAS-3417_search-empty-fields_MASTER

add : special values "_empty_" and "_any_" to search empty field or not-empty field. Works only with = operator.
e.g.: Title=_empty_

wip/todo : unit test ; facet
This commit is contained in:
jygaulier
2021-04-13 16:35:15 +02:00
parent e66cea63d4
commit 1e7092b86a
3 changed files with 107 additions and 2 deletions

View File

@@ -0,0 +1,45 @@
<?php
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Node;
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryPostProcessor;
class ExistsExpression extends Node
{
const EXISTS_VALUE = '_any_';
private $key;
private $value;
public function __construct(Key $key)
{
$this->key = $key;
}
public function buildQuery(QueryContext $context)
{
$query = [
'exists' => [
'field' => $this->key->getIndexField($context, true)
]
];
if ($this->key instanceof QueryPostProcessor) {
return $this->key->postProcessQuery($query, $context);
}
return $query;
}
public function getTermNodes()
{
return array();
}
public function __toString()
{
return sprintf('(<%s> == <%s>)', $this->key, self::EXISTS_VALUE);
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
use Alchemy\Phrasea\SearchEngine\Elastic\AST\Node;
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryPostProcessor;
class MissingExpression extends Node
{
const MISSING_VALUE = '_empty_';
private $key;
private $value;
public function __construct(Key $key)
{
$this->key = $key;
}
public function buildQuery(QueryContext $context)
{
$query = [
'bool' => [
'must_not' => [
'exists' => [
'field' => $this->key->getIndexField($context, true)
]
]
]
];
if ($this->key instanceof QueryPostProcessor) {
return $this->key->postProcessQuery($query, $context);
}
return $query;
}
public function getTermNodes()
{
return array();
}
public function __toString()
{
return sprintf('(<%s> == <%s>)', $this->key, self::MISSING_VALUE);
}
}

View File

@@ -197,25 +197,36 @@ class QueryVisitor implements Visit
private function visitEqualNode(TreeNode $node) private function visitEqualNode(TreeNode $node)
{ {
return $this->handleBinaryExpression($node, function($left, $right) { return $this->handleBinaryExpression($node, function($left, $right) {
if($right === AST\KeyValue\MissingExpression::MISSING_VALUE) {
return new AST\KeyValue\MissingExpression($left);
}
if($right === AST\KeyValue\ExistsExpression::EXISTS_VALUE) {
return new AST\KeyValue\ExistsExpression($left);
}
if ($this->isDateKey($left)) { if ($this->isDateKey($left)) {
try { try {
// Try to create a range for incomplete dates // Try to create a range for incomplete dates
$range = QueryHelper::getRangeFromDateString($right); $range = QueryHelper::getRangeFromDateString($right);
if ($range['from'] === $range['to']) { if ($range['from'] === $range['to']) {
return new AST\KeyValue\EqualExpression($left, $range['from']); return new AST\KeyValue\EqualExpression($left, $range['from']);
} else { }
else {
return new AST\KeyValue\RangeExpression( return new AST\KeyValue\RangeExpression(
$left, $left,
$range['from'], true, $range['from'], true,
$range['to'], false $range['to'], false
); );
} }
} catch (\InvalidArgumentException $e) { }
catch (\InvalidArgumentException $e) {
// Fall back to equal expression // Fall back to equal expression
} }
} }
return new AST\KeyValue\EqualExpression($left, $right); return new AST\KeyValue\EqualExpression($left, $right);
}); });
} }