From 23b0fab7f9e62ee7c34f5189ab404e84862b14db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Parafi=C5=84ski?= Date: Fri, 23 Feb 2018 19:31:04 +0100 Subject: [PATCH] EZP-28843: As a Developer I want SQL Search by Field to use ezkeyword Ext. Storage (#2256) * [EZP-28843] Implementation of Operator IN inside ezkeyword * [EZP-28843] Functional and Integration test --- .../Resources/config/input_parsers.yml | 2 +- .../Tests/Functional/SearchViewTest.php | 59 +++++++++----- .../FieldType/KeywordIntegrationTest.php | 77 +++++++++++++++++++ .../FieldValue/Handler/Keyword.php | 43 +++++++++++ .../legacy/criterion_handlers_common.yml | 6 ++ 5 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Keyword.php diff --git a/eZ/Bundle/EzPublishRestBundle/Resources/config/input_parsers.yml b/eZ/Bundle/EzPublishRestBundle/Resources/config/input_parsers.yml index eee5d3a768f..2a34adf837d 100644 --- a/eZ/Bundle/EzPublishRestBundle/Resources/config/input_parsers.yml +++ b/eZ/Bundle/EzPublishRestBundle/Resources/config/input_parsers.yml @@ -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: diff --git a/eZ/Bundle/EzPublishRestBundle/Tests/Functional/SearchViewTest.php b/eZ/Bundle/EzPublishRestBundle/Tests/Functional/SearchViewTest.php index 76cc9e9f5ef..a31db207155 100644 --- a/eZ/Bundle/EzPublishRestBundle/Tests/Functional/SearchViewTest.php +++ b/eZ/Bundle/EzPublishRestBundle/Tests/Functional/SearchViewTest.php @@ -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; @@ -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 [ [ @@ -221,6 +226,19 @@ public function xmlProvider() ), 1, ], + [ + $this->getXmlString( + $this->wrapIn('OR', [ + $foobazInTag, + $bazfooInTag, + ]) + ), + 2, + ], + [ + $this->getXmlString($fooAndBarInTag), + 2, + ], ]; } @@ -228,37 +246,36 @@ public function xmlProvider() * @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) { @@ -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); } diff --git a/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php index 701d1209091..22729105fee 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php +++ b/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php @@ -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. @@ -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); + } } diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Keyword.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Keyword.php new file mode 100644 index 00000000000..b106d4bec72 --- /dev/null +++ b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Keyword.php @@ -0,0 +1,43 @@ +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'); + } +} diff --git a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml b/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml index d391d19719a..8eb58e3fdfe 100644 --- a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml +++ b/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml @@ -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: @@ -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: