Skip to content

Commit

Permalink
EZP-30951: Added missing support for searching (Not)Empty Field values (
Browse files Browse the repository at this point in the history
#157)

* Added missing features for Empty and Null field values

* Fixups after CR

* fixups

* Removed changes for Null Searching

* Fixups after CR

* CS Fixups

* Fixups after CR

* Small change for FieldEmpty Vistor

* Changes after CR

* Fixups after CR

* Updated composer.json kernel requirement
  • Loading branch information
mateuszbieniek authored and lserwatka committed Nov 22, 2019
1 parent 27ca8b1 commit 0a9da38
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 1 deletion.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
],
"require": {
"php": "^7.1",
"ezsystems/ezpublish-kernel": "^7.5@dev",
"ezsystems/ezpublish-kernel": "^7.5.7@dev",
"netgen/query-translator": "^1.0.2"
},
"require-dev": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace EzSystems\EzPlatformSolrSearchEngine\FieldMapper\ContentTranslationFieldMapper;

use eZ\Publish\Core\Persistence\FieldTypeRegistry;
use eZ\Publish\Core\Search\Common\FieldNameGenerator;
use eZ\Publish\SPI\Persistence\Content;
use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler;
use eZ\Publish\SPI\Search\Field;
use eZ\Publish\SPI\Search\FieldType;
use EzSystems\EzPlatformSolrSearchEngine\FieldMapper\ContentTranslationFieldMapper;

/**
* Indexes information on whether Content field is empty.
*/
class ContentDocumentEmptyFields extends ContentTranslationFieldMapper
{
public const IS_EMPTY_NAME = 'is_empty';

/**
* @var \eZ\Publish\SPI\Persistence\Content\Type\Handler
*/
private $contentTypeHandler;

/**
* @var \eZ\Publish\Core\Search\Common\FieldNameGenerator
*/
private $fieldNameGenerator;

/**
* @var \eZ\Publish\Core\Persistence\FieldTypeRegistry
*/
private $fieldTypeRegistry;

/**
* @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler
* @param \eZ\Publish\Core\Search\Common\FieldNameGenerator $fieldNameGenerator
* @param \eZ\Publish\Core\Persistence\FieldTypeRegistry $fieldTypeRegistry
*/
public function __construct(
ContentTypeHandler $contentTypeHandler,
FieldNameGenerator $fieldNameGenerator,
FieldTypeRegistry $fieldTypeRegistry
) {
$this->contentTypeHandler = $contentTypeHandler;
$this->fieldNameGenerator = $fieldNameGenerator;
$this->fieldTypeRegistry = $fieldTypeRegistry;
}

/**
* @param \eZ\Publish\SPI\Persistence\Content $content
* @param string $languageCode
*
* @return bool
*/
public function accept(Content $content, $languageCode)
{
return true;
}

/**
* @param \eZ\Publish\SPI\Persistence\Content $content
* @param string $languageCode
*
* @return \eZ\Publish\SPI\Search\Field[]
*/
public function mapFields(Content $content, $languageCode)
{
$fields = [];
$contentType = $this->contentTypeHandler->load(
$content->versionInfo->contentInfo->contentTypeId
);

foreach ($content->fields as $field) {
if ($field->languageCode !== $languageCode) {
continue;
}

foreach ($contentType->fieldDefinitions as $fieldDefinition) {
if ($fieldDefinition->isRequired) {
continue;
}
if ($fieldDefinition->id !== $field->fieldDefinitionId) {
continue;
}

/** @var \eZ\Publish\Core\Persistence\FieldType $fieldType */
$fieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldType);
$fields[] = new Field(
$name = $this->fieldNameGenerator->getName(
self::IS_EMPTY_NAME,
$fieldDefinition->identifier
),
$fieldType->isEmptyValue($field->value),
new FieldType\BooleanField()
);
}
}

return $fields;
}
}
93 changes: 93 additions & 0 deletions lib/Query/Common/CriterionVisitor/Field/FieldEmpty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field;

use eZ\Publish\SPI\Search\FieldType\BooleanField;
use EzSystems\EzPlatformSolrSearchEngine\FieldMapper\ContentTranslationFieldMapper\ContentDocumentEmptyFields;
use EzSystems\EzPlatformSolrSearchEngine\Query\CriterionVisitor;
use EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
use eZ\Publish\Core\Search\Common\FieldValueMapper;
use eZ\Publish\Core\Search\Common\FieldNameResolver;
use eZ\Publish\Core\Search\Common\FieldNameGenerator;

/**
* Visits the IsFieldEmpty criterion.
*/
final class FieldEmpty extends Field
{
/**
* @var \eZ\Publish\Core\Search\Common\FieldNameGenerator
*/
private $fieldNameGenerator;

/**
* @param \eZ\Publish\Core\Search\Common\FieldNameResolver $fieldNameResolver
* @param \eZ\Publish\Core\Search\Common\FieldValueMapper $fieldValueMapper
* @param \eZ\Publish\Core\Search\Common\FieldNameGenerator $fieldNameGenerator
*/
public function __construct(
FieldNameResolver $fieldNameResolver,
FieldValueMapper $fieldValueMapper,
FieldNameGenerator $fieldNameGenerator
) {
parent::__construct($fieldNameResolver, $fieldValueMapper);

$this->fieldNameGenerator = $fieldNameGenerator;
}

/**
* Check if visitor is applicable to current criterion.
*/
public function canVisit(Criterion $criterion): bool
{
return $criterion instanceof Criterion\IsFieldEmpty;
}

/**
* Map field value to a proper Solr representation.
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If no searchable fields are found for the given criterion target.
*
* @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
* @param \EzSystems\EzPlatformSolrSearchEngine\Query\CriterionVisitor $subVisitor
*
* @return string
*/
public function visit(Criterion $criterion, CriterionVisitor $subVisitor = null): string
{
$searchFields = $this->getSearchFields($criterion);

if (empty($searchFields)) {
throw new InvalidArgumentException(
'$criterion->target',
"No searchable fields found for the given criterion target '{$criterion->target}'."
);
}

$criterion->value = (array)$criterion->value;
$queries = [];

foreach ($searchFields as $name => $fieldType) {
foreach ($criterion->value as $value) {
$name = $this->fieldNameGenerator->getTypedName(
$this->fieldNameGenerator->getName(
ContentDocumentEmptyFields::IS_EMPTY_NAME,
$criterion->target
),
new BooleanField()
);
$queries[] = $name . ':' . (int) $value;
}
}

return '(' . implode(' OR ', $queries) . ')';
}
}
11 changes: 11 additions & 0 deletions lib/Resources/config/container/solr/criterion_visitors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ parameters:
ezpublish.search.solr.query.common.criterion_visitor.custom_field_in.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\CustomField\CustomFieldIn
ezpublish.search.solr.query.common.criterion_visitor.custom_field_range.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\CustomField\CustomFieldRange
ezpublish.search.solr.query.common.criterion_visitor.field_in.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field\FieldIn
ezpublish.search.solr.query.common.criterion_visitor.field_empty.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field\FieldEmpty
ezpublish.search.solr.query.common.criterion_visitor.field_range.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field\FieldRange
ezpublish.search.solr.query.common.criterion_visitor.field_like.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field\FieldLike
ezpublish.search.solr.query.common.criterion_visitor.field_relation.class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\CriterionVisitor\Field\FieldRelation
Expand Down Expand Up @@ -108,6 +109,16 @@ services:
- {name: ezpublish.search.solr.query.content.criterion_visitor}
- {name: ezpublish.search.solr.query.location.criterion_visitor}

ezpublish.search.solr.query.common.criterion_visitor.field_empty:
class: "%ezpublish.search.solr.query.common.criterion_visitor.field_empty.class%"
arguments:
- "@ezpublish.search.common.field_name_resolver"
- "@ezpublish.search.common.field_value_mapper.aggregate"
- "@ezpublish.search.common.field_name_generator"
tags:
- {name: ezpublish.search.solr.query.content.criterion_visitor}
- {name: ezpublish.search.solr.query.location.criterion_visitor}

ezpublish.search.solr.query.common.criterion_visitor.field_range:
class: "%ezpublish.search.solr.query.common.criterion_visitor.field_range.class%"
arguments:
Expand Down
9 changes: 9 additions & 0 deletions lib/Resources/config/container/solr/field_mappers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,12 @@ services:
- '@ezpublish.spi.persistence.content_handler'
tags:
- {name: ezpublish.search.solr.field_mapper.location}

ezpublish.search.solr.field_mapper.content_translation.content_document_empty_fields:
class: EzSystems\EzPlatformSolrSearchEngine\FieldMapper\ContentTranslationFieldMapper\ContentDocumentEmptyFields
arguments:
- '@ezpublish.spi.persistence.content_type_handler'
- '@ezpublish.search.common.field_name_generator'
- '@ezpublish.persistence.field_type_registry'
tags:
- {name: ezpublish.search.solr.field_mapper.content_translation}

0 comments on commit 0a9da38

Please sign in to comment.