mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-12 12:33:26 +00:00
Merge metadata & native key expressions
Do not delegates query building to Key instance anymore.
This commit is contained in:
@@ -8,8 +8,8 @@ use Assert\Assertion;
|
|||||||
|
|
||||||
class Expression extends Node
|
class Expression extends Node
|
||||||
{
|
{
|
||||||
protected $key;
|
private $key;
|
||||||
protected $value;
|
private $value;
|
||||||
|
|
||||||
public function __construct(Key $key, $value)
|
public function __construct(Key $key, $value)
|
||||||
{
|
{
|
||||||
@@ -20,7 +20,11 @@ class Expression extends Node
|
|||||||
|
|
||||||
public function buildQuery(QueryContext $context)
|
public function buildQuery(QueryContext $context)
|
||||||
{
|
{
|
||||||
return $this->key->buildQueryForValue($this->value, $context);
|
return [
|
||||||
|
'term' => [
|
||||||
|
$this->key->getIndexField() => $this->value
|
||||||
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTermNodes()
|
public function getTermNodes()
|
||||||
@@ -30,6 +34,6 @@ class Expression extends Node
|
|||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
return sprintf('<%s:%s>', $this->key, $this->value);
|
return sprintf('<%s:"%s">', $this->key, $this->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
||||||
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
|
||||||
|
|
||||||
interface Key
|
interface Key
|
||||||
{
|
{
|
||||||
public function buildQueryForValue($value, QueryContext $context);
|
public function getIndexField();
|
||||||
public function __toString();
|
public function __toString();
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
|||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
||||||
use Assert\Assertion;
|
use Assert\Assertion;
|
||||||
|
|
||||||
class MetadataKey
|
class MetadataKey implements Key
|
||||||
{
|
{
|
||||||
private $name;
|
private $name;
|
||||||
|
|
||||||
@@ -15,8 +15,13 @@ class MetadataKey
|
|||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getIndexField()
|
||||||
|
{
|
||||||
|
return sprintf('exif.%s', $this->name);
|
||||||
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
return $this->name;
|
return sprintf('metadata.%s', $this->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue;
|
||||||
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
|
||||||
use Assert\Assertion;
|
|
||||||
|
|
||||||
class NativeKey implements Key
|
class NativeKey implements Key
|
||||||
{
|
{
|
||||||
const TYPE_DATABASE = 'database';
|
const TYPE_DATABASE = 'database';
|
||||||
@@ -41,14 +38,9 @@ class NativeKey implements Key
|
|||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildQueryForValue($value, QueryContext $context)
|
public function getIndexField()
|
||||||
{
|
{
|
||||||
Assertion::string($value);
|
return $this->key;
|
||||||
return [
|
|
||||||
'term' => [
|
|
||||||
$this->key => $value
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic\AST;
|
|
||||||
|
|
||||||
use Assert\Assertion;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\MetadataKey;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Exception\QueryException;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryHelper;
|
|
||||||
|
|
||||||
class MetadataMatchStatement extends Node
|
|
||||||
{
|
|
||||||
private $key;
|
|
||||||
private $value;
|
|
||||||
|
|
||||||
public function __construct(MetadataKey $key, $value)
|
|
||||||
{
|
|
||||||
Assertion::string($value);
|
|
||||||
$this->key = $key;
|
|
||||||
$this->value = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildQuery(QueryContext $context)
|
|
||||||
{
|
|
||||||
$field = sprintf('exif.%s', $this->key);
|
|
||||||
return [
|
|
||||||
'term' => [
|
|
||||||
$field => $this->value
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTermNodes()
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return sprintf('<metadata:%s value:"%s">', $this->key, $this->value);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -85,12 +85,6 @@ class QueryVisitor implements Visit
|
|||||||
case NodeTypes::FIELD:
|
case NodeTypes::FIELD:
|
||||||
return new AST\Field($this->visitString($element));
|
return new AST\Field($this->visitString($element));
|
||||||
|
|
||||||
case NodeTypes::METADATA_STATEMENT:
|
|
||||||
return $this->visitMetadataStatementNode($element);
|
|
||||||
|
|
||||||
case NodeTypes::METADATA_KEY:
|
|
||||||
return $this->visitMetadataKeyNode($element);
|
|
||||||
|
|
||||||
case NodeTypes::FLAG_STATEMENT:
|
case NodeTypes::FLAG_STATEMENT:
|
||||||
return $this->visitFlagStatementNode($element);
|
return $this->visitFlagStatementNode($element);
|
||||||
|
|
||||||
@@ -98,11 +92,15 @@ class QueryVisitor implements Visit
|
|||||||
return new AST\Flag($this->visitString($element));
|
return new AST\Flag($this->visitString($element));
|
||||||
|
|
||||||
case NodeTypes::NATIVE_KEY_VALUE:
|
case NodeTypes::NATIVE_KEY_VALUE:
|
||||||
return $this->visitNativeKeyValueNode($element);
|
case NodeTypes::METADATA_STATEMENT:
|
||||||
|
return $this->visitKeyValueNode($element);
|
||||||
|
|
||||||
case NodeTypes::NATIVE_KEY:
|
case NodeTypes::NATIVE_KEY:
|
||||||
return $this->visitNativeKeyNode($element);
|
return $this->visitNativeKeyNode($element);
|
||||||
|
|
||||||
|
case NodeTypes::METADATA_KEY:
|
||||||
|
return $this->visitMetadataKeyNode($element);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception(sprintf('Unknown node type "%s".', $element->getId()));
|
throw new Exception(sprintf('Unknown node type "%s".', $element->getId()));
|
||||||
}
|
}
|
||||||
@@ -315,17 +313,7 @@ class QueryVisitor implements Visit
|
|||||||
return new AST\KeyValue\MetadataKey($name);
|
return new AST\KeyValue\MetadataKey($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function visitMetadataStatementNode(TreeNode $node)
|
private function visitKeyValueNode(TreeNode $node)
|
||||||
{
|
|
||||||
if ($node->getChildrenNumber() !== 2) {
|
|
||||||
throw new Exception('Flag statement can only have 2 childs.');
|
|
||||||
}
|
|
||||||
$name = $this->visit($node->getChild(0));
|
|
||||||
$value = $this->visit($node->getChild(1));
|
|
||||||
return new AST\MetadataMatchStatement($name, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function visitNativeKeyValueNode(TreeNode $node)
|
|
||||||
{
|
{
|
||||||
if ($node->getChildrenNumber() !== 2) {
|
if ($node->getChildrenNumber() !== 2) {
|
||||||
throw new Exception('Key value expression can only have 2 childs.');
|
throw new Exception('Key value expression can only have 2 childs.');
|
||||||
|
@@ -4,6 +4,8 @@ namespace Alchemy\Tests\Phrasea\SearchEngine\AST;
|
|||||||
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Key;
|
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Key;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Expression as KeyValueExpression;
|
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\Expression as KeyValueExpression;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\MetadataKey;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\NativeKey;
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,17 +21,28 @@ class KeyValueExpressionTest extends \PHPUnit_Framework_TestCase
|
|||||||
$key = $this->prophesize(Key::class);
|
$key = $this->prophesize(Key::class);
|
||||||
$key->__toString()->willReturn('foo');
|
$key->__toString()->willReturn('foo');
|
||||||
$node = new KeyValueExpression($key->reveal(), 'bar');
|
$node = new KeyValueExpression($key->reveal(), 'bar');
|
||||||
$this->assertEquals('<foo:bar>', (string) $node);
|
$this->assertEquals('<foo:"bar">', (string) $node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testQueryBuild()
|
/**
|
||||||
|
* @dataProvider keyProvider
|
||||||
|
*/
|
||||||
|
public function testQueryBuild($key, $value, $result)
|
||||||
{
|
{
|
||||||
$query_context = $this->prophesize(QueryContext::class);
|
$query_context = $this->prophesize(QueryContext::class);
|
||||||
$key = $this->prophesize(Key::class);
|
$node = new KeyValueExpression($key, $value);
|
||||||
$key->buildQueryForValue('bar', $query_context->reveal())->willReturn('baz');
|
|
||||||
|
|
||||||
$node = new KeyValueExpression($key->reveal(), 'bar');
|
|
||||||
$query = $node->buildQuery($query_context->reveal());
|
$query = $node->buildQuery($query_context->reveal());
|
||||||
$this->assertEquals('baz', $query);
|
$this->assertEquals(json_decode($result, true), $query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function keyProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[NativeKey::database(), 'foo', '{"term":{"databox_name": "foo"}}'],
|
||||||
|
[NativeKey::collection(), 'bar', '{"term":{"collection_name": "bar"}}'],
|
||||||
|
[NativeKey::mediaType(), 'baz', '{"term":{"type": "baz"}}'],
|
||||||
|
[NativeKey::recordIdentifier(), 'qux', '{"term":{"record_id": "qux"}}'],
|
||||||
|
[new MetadataKey('foo'), 'bar', '{"term":{"exif.foo": "bar"}}'],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\Tests\Phrasea\SearchEngine\AST;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\KeyValue\MetadataKey;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\AST\MetadataMatchStatement;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @group unit
|
|
||||||
* @group searchengine
|
|
||||||
* @group ast
|
|
||||||
*/
|
|
||||||
class MetdataMatchStatementTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
public function testSerialization()
|
|
||||||
{
|
|
||||||
$this->assertTrue(method_exists(MetadataMatchStatement::class, '__toString'), 'Class does not have method __toString');
|
|
||||||
$key = new MetadataKey('foo');
|
|
||||||
$node = new MetadataMatchStatement($key, 'bar');
|
|
||||||
$this->assertEquals('<metadata:foo value:"bar">', (string) $node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testQueryBuild()
|
|
||||||
{
|
|
||||||
$query_context = $this->prophesize(QueryContext::class);
|
|
||||||
|
|
||||||
$key = new MetadataKey('foo');
|
|
||||||
$node = new MetadataMatchStatement($key, 'bar');
|
|
||||||
$query = $node->buildQuery($query_context->reveal());
|
|
||||||
|
|
||||||
$expected = '{
|
|
||||||
"term": {
|
|
||||||
"exif.foo": "bar"
|
|
||||||
}
|
|
||||||
}';
|
|
||||||
|
|
||||||
$this->assertEquals(json_decode($expected, true), $query);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -21,63 +21,21 @@ class NativeKeyTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('record_identifier', (string) NativeKey::recordIdentifier());
|
$this->assertEquals('record_identifier', (string) NativeKey::recordIdentifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDatabaseQuery()
|
/**
|
||||||
|
* @dataProvider keyProvider
|
||||||
|
*/
|
||||||
|
public function testGetIndexField($key, $field)
|
||||||
{
|
{
|
||||||
$query_context = $this->prophesize(QueryContext::class);
|
$this->assertEquals($key->getIndexField(), $field);
|
||||||
$key = NativeKey::database();
|
|
||||||
$query = $key->buildQueryForValue('bar', $query_context->reveal());
|
|
||||||
|
|
||||||
$expected = '{
|
|
||||||
"term": {
|
|
||||||
"databox_name": "bar"
|
|
||||||
}
|
|
||||||
}';
|
|
||||||
|
|
||||||
$this->assertEquals(json_decode($expected, true), $query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCollectionQuery()
|
public function keyProvider()
|
||||||
{
|
{
|
||||||
$query_context = $this->prophesize(QueryContext::class);
|
return [
|
||||||
$key = NativeKey::collection();
|
[NativeKey::database(), 'databox_name'],
|
||||||
$query = $key->buildQueryForValue('bar', $query_context->reveal());
|
[NativeKey::collection(), 'collection_name'],
|
||||||
|
[NativeKey::mediaType(), 'type'],
|
||||||
$expected = '{
|
[NativeKey::recordIdentifier(), 'record_id']
|
||||||
"term": {
|
];
|
||||||
"collection_name": "bar"
|
|
||||||
}
|
|
||||||
}';
|
|
||||||
|
|
||||||
$this->assertEquals(json_decode($expected, true), $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMediaTypeQuery()
|
|
||||||
{
|
|
||||||
$query_context = $this->prophesize(QueryContext::class);
|
|
||||||
$key = NativeKey::mediaType();
|
|
||||||
$query = $key->buildQueryForValue('bar', $query_context->reveal());
|
|
||||||
|
|
||||||
$expected = '{
|
|
||||||
"term": {
|
|
||||||
"type": "bar"
|
|
||||||
}
|
|
||||||
}';
|
|
||||||
|
|
||||||
$this->assertEquals(json_decode($expected, true), $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRecordIdentifierQuery()
|
|
||||||
{
|
|
||||||
$query_context = $this->prophesize(QueryContext::class);
|
|
||||||
$key = NativeKey::recordIdentifier();
|
|
||||||
$query = $key->buildQueryForValue('bar', $query_context->reveal());
|
|
||||||
|
|
||||||
$expected = '{
|
|
||||||
"term": {
|
|
||||||
"record_id": "bar"
|
|
||||||
}
|
|
||||||
}';
|
|
||||||
|
|
||||||
$this->assertEquals(json_decode($expected, true), $query);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,19 +79,19 @@ field.type:foo|(<field:type> MATCHES <text:"foo">)
|
|||||||
field.id:foo|(<field:id> MATCHES <text:"foo">)
|
field.id:foo|(<field:id> MATCHES <text:"foo">)
|
||||||
|
|
||||||
# Matchers
|
# Matchers
|
||||||
collection:foo|<collection:foo>
|
collection:foo|<collection:"foo">
|
||||||
collection:foo AND bar|(<collection:foo> AND <text:"bar">)
|
collection:foo AND bar|(<collection:"foo"> AND <text:"bar">)
|
||||||
collection:foo bar|(<collection:foo> AND <text:"bar">)
|
collection:foo bar|(<collection:"foo"> AND <text:"bar">)
|
||||||
database:foo|<database:foo>
|
database:foo|<database:"foo">
|
||||||
database:foo AND bar|(<database:foo> AND <text:"bar">)
|
database:foo AND bar|(<database:"foo"> AND <text:"bar">)
|
||||||
database:foo bar|(<database:foo> AND <text:"bar">)
|
database:foo bar|(<database:"foo"> AND <text:"bar">)
|
||||||
type:foo|<media_type:foo>
|
type:foo|<media_type:"foo">
|
||||||
type:foo AND bar|(<media_type:foo> AND <text:"bar">)
|
type:foo AND bar|(<media_type:"foo"> AND <text:"bar">)
|
||||||
type:foo bar|(<media_type:foo> AND <text:"bar">)
|
type:foo bar|(<media_type:"foo"> AND <text:"bar">)
|
||||||
id:90|<record_identifier:90>
|
id:90|<record_identifier:"90">
|
||||||
id:90 AND foo|(<record_identifier:90> AND <text:"foo">)
|
id:90 AND foo|(<record_identifier:"90"> AND <text:"foo">)
|
||||||
id:90 foo|(<record_identifier:90> AND <text:"foo">)
|
id:90 foo|(<record_identifier:"90"> AND <text:"foo">)
|
||||||
recordid:90|<record_identifier:90>
|
recordid:90|<record_identifier:"90">
|
||||||
|
|
||||||
# Flag matcher
|
# Flag matcher
|
||||||
flag.foo:true|<flag:foo set>
|
flag.foo:true|<flag:foo set>
|
||||||
@@ -103,7 +103,7 @@ flag.foo bar:true|(<text:"flag.foo"> AND (<field:bar> MATCHES <text:"true">))
|
|||||||
true|<text:"true">
|
true|<text:"true">
|
||||||
|
|
||||||
# Metadata (EXIF or anything else) matcher
|
# Metadata (EXIF or anything else) matcher
|
||||||
meta.MimeType:image/jpeg|<metadata:MimeType value:"image/jpeg">
|
meta.MimeType:image/jpeg|<metadata.MimeType:"image/jpeg">
|
||||||
|
|
||||||
# Unescaped "." issue on key prefixes
|
# Unescaped "." issue on key prefixes
|
||||||
fieldOne:foo|(<field:fieldOne> MATCHES <text:"foo">)
|
fieldOne:foo|(<field:fieldOne> MATCHES <text:"foo">)
|
||||||
|
Can't render this file because it contains an unexpected character in line 1 and column 11.
|
Reference in New Issue
Block a user