Skip to content

Commit

Permalink
EZP-28843: As a Developer I want SQL Search by Field to use ezkeyword…
Browse files Browse the repository at this point in the history
… Ext. Storage (ezsystems#2256)

* [EZP-28843] Implementation of Operator IN inside ezkeyword

* [EZP-28843] Functional and Integration test
  • Loading branch information
ViniTou authored and alongosz committed Feb 23, 2018
1 parent a259cb7 commit 23b0fab
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 22 deletions.
Expand Up @@ -462,7 +462,7 @@ services:
tags:
- { name: ezpublish_rest.input.parser, mediaType: application/vnd.ez.api.internal.criterion.DateMetadata }

ezpublish_rest.input.parser.internal.cgriterion.Field:
ezpublish_rest.input.parser.internal.criterion.Field:
parent: ezpublish_rest.input.parser
class: "%ezpublish_rest.input.parser.internal.criterion.Field.class%"
tags:
Expand Down
59 changes: 38 additions & 21 deletions eZ/Bundle/EzPublishRestBundle/Tests/Functional/SearchViewTest.php
Expand Up @@ -8,6 +8,8 @@
*/
namespace eZ\Bundle\EzPublishRestBundle\Tests\Functional;

use DOMDocument;
use DOMElement;
use eZ\Bundle\EzPublishRestBundle\Tests\Functional\TestCase as RESTFunctionalTestCase;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator;

Expand Down Expand Up @@ -195,6 +197,9 @@ public function xmlProvider()
$barTag = $this->buildFieldXml('tags', Operator::CONTAINS, 'bar');
$bazTag = $this->buildFieldXml('tags', Operator::CONTAINS, 'baz');
$foobazTag = $this->buildFieldXml('tags', Operator::CONTAINS, 'foobaz');
$foobazInTag = $this->buildFieldXml('tags', Operator::IN, ['foobaz']);
$bazfooInTag = $this->buildFieldXml('tags', Operator::IN, ['bazfoo']);
$fooAndBarInTag = $this->buildFieldXml('tags', Operator::IN, ['foo', 'bar']);

return [
[
Expand All @@ -221,44 +226,56 @@ public function xmlProvider()
),
1,
],
[
$this->getXmlString(
$this->wrapIn('OR', [
$foobazInTag,
$bazfooInTag,
])
),
2,
],
[
$this->getXmlString($fooAndBarInTag),
2,
],
];
}

/**
* @param string $name
* @param string $operator
* @param string|string[] $value
* @return \DomElement
* @return DOMElement
*/
private function buildFieldXml(string $name, string $operator, $value): \DomElement
private function buildFieldXml(string $name, string $operator, $value): DOMElement
{
$xml = new \DOMDocument();
$xml = new DOMDocument();
$element = $xml->createElement('Field');
$element->appendChild(new \DOMElement('name', $name));
$element->appendChild(new \DOMElement('operator', $operator));
$element->appendChild(new DOMElement('name', $name));
$element->appendChild(new DOMElement('operator', $operator));

//Force xml array with one value
if (is_array($value)) {
$valueWrapper = $xml->createElement('value');
foreach ($value as $key => $singleValue) {
$valueWrapper->appendChild(new \DOMElement('value', $singleValue));
if (count($value) === 1) {
$valueWrapper = $xml->createElement('value');
$valueWrapper->appendChild(new DOMElement('value', $value[0]));
$element->appendChild($valueWrapper);
} else {
foreach ($value as $key => $singleValue) {
$element->appendChild(new DOMElement('value', $singleValue));
}
}
$element->appendChild($valueWrapper);

return $element;
} else {
$element->appendChild(new DOMElement('value', $value));
}

$element->appendChild(new \DOMElement('value', $value));

return $element;
}

/**
* @param string $logicalOperator
* @param \DomElement|\DomElement[] $toWrap
* @return \DomElement
*/
private function wrapIn(string $logicalOperator, array $toWrap): \DomElement
private function wrapIn(string $logicalOperator, array $toWrap): DOMElement
{
$xml = new \DOMDocument();
$xml = new DOMDocument();
$wrapper = $xml->createElement($logicalOperator);

foreach ($toWrap as $key => $field) {
Expand All @@ -270,7 +287,7 @@ private function wrapIn(string $logicalOperator, array $toWrap): \DomElement
return $wrapper;
}

private function getXmlString(\DomElement $simpleXMLElement): string
private function getXmlString(DOMElement $simpleXMLElement): string
{
return $simpleXMLElement->ownerDocument->saveXML($simpleXMLElement);
}
Expand Down
Expand Up @@ -11,6 +11,8 @@
use eZ\Publish\Core\FieldType\Keyword\Value as KeywordValue;
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
use eZ\Publish\API\Repository\Values\Content\Field;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query;

/**
* Integration test for use field type.
Expand Down Expand Up @@ -527,4 +529,79 @@ public function testTruncateField($emptyValue)
'Field value is not empty: ' . var_export($fieldValue, true)
);
}

/**
* Create test Content with ezkeyword type.
*
* @return \eZ\Publish\API\Repository\Values\Content\Content[]
*/
protected function createKeywordContent()
{
$repository = $this->getRepository();
$contentTypeService = $repository->getContentTypeService();
$contentService = $repository->getContentService();

$createStruct = $contentTypeService->newContentTypeCreateStruct('content-keyword');
$createStruct->mainLanguageCode = 'eng-GB';
$createStruct->remoteId = 'content-keyword-123';
$createStruct->names = ['eng-GB' => 'Keywords'];
$createStruct->creatorId = 14;
$createStruct->creationDate = new \DateTime();

$fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('tags', 'ezkeyword');
$fieldCreate->names = ['eng-GB' => 'Tags'];
$fieldCreate->fieldGroup = 'main';
$fieldCreate->position = 1;
$fieldCreate->isTranslatable = false;
$fieldCreate->isSearchable = true;

$createStruct->addFieldDefinition($fieldCreate);

$contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
$contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
$contentTypeService->publishContentTypeDraft($contentTypeDraft);
$contentType = $contentTypeService->loadContentType($contentTypeDraft->id);

$createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');

$toCreate = [
'content-keyword-456' => ['foo', 'bar'],
'content-keyword-789' => ['bar', 'foobar'],
];
$createdContent = [];
foreach ($toCreate as $remoteId => $tagsString) {
$createStruct->remoteId = $remoteId;
$createStruct->alwaysAvailable = false;
$createStruct->setField(
'tags',
$tagsString
);

$draft = $contentService->createContent($createStruct);
$createdContent[] = $contentService->publishVersion($draft->getVersionInfo());
}

$this->refreshSearch($repository);

return $createdContent;
}

/**
* Test for the findContent() method.
*
* @see \eZ\Publish\API\Repository\SearchService::findContent()
*/
public function testFindContentFieldCriterion()
{
$this->createKeywordContent();
$repository = $this->getRepository();

$criterion = new Criterion\Field('tags', Criterion\Operator::IN, ['foo']);
$query = new Query(['query' => $criterion]);

$searchService = $repository->getSearchService();
$searchResult = $searchService->findContent($query);

$this->assertEquals(1, $searchResult->totalCount);
}
}
@@ -0,0 +1,43 @@
<?php

/**
* This file is part of the eZ Publish Kernel package.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler;

use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\Core\Persistence\Database\SelectQuery;

/**
* FieldValue CriterionHandler handling ezkeyword External Storage for Legacy/SQL Search.
*/
class Keyword extends Collection
{
/**
* Generates query expression for operator and value of a Field Criterion.
*
* @param \eZ\Publish\Core\Persistence\Database\SelectQuery $query
* @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
* @param string $column
*
* @return \eZ\Publish\Core\Persistence\Database\Expression
*/
public function handle(SelectQuery $query, Criterion $criterion, $column)
{
$query
->innerJoin(
$this->dbHandler->quoteTable('ezkeyword_attribute_link'),
'ezcontentobject_attribute.id',
'ezkeyword_attribute_link.objectattribute_id'
)->innerJoin(
$this->dbHandler->quoteTable('ezkeyword'),
'ezkeyword.id',
'ezkeyword_attribute_link.keyword_id'
);

return parent::handle($query, $criterion, 'keyword');
}
}
Expand Up @@ -27,6 +27,7 @@ parameters:
ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Collection
ezpublish.search.legacy.gateway.criterion_field_value_handler.composite.class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Composite
ezpublish.search.legacy.gateway.criterion_field_value_handler.simple.class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Simple
ezpublish.search.legacy.gateway.criterion_field_value_handler.keyword.class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Keyword

# Full text search configuration options.
ezpublish.search.legacy.criterion_handler.full_text.configuration:
Expand Down Expand Up @@ -251,6 +252,11 @@ services:
tags:
- {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezcountry}
- {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezobjectrelationlist}

ezpublish.search.legacy.gateway.criterion_field_value_handler.keyword:
parent: ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.comma_separated
class: '%ezpublish.search.legacy.gateway.criterion_field_value_handler.keyword.class%'
tags:
- {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezkeyword}

ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.hypen_separated:
Expand Down

0 comments on commit 23b0fab

Please sign in to comment.