Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EZP-30951: Added missing support for searching (Not)Empty Field values #157

Merged
merged 11 commits into from
Nov 22, 2019
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.
*/
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
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;
}

mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
/**
* @param \eZ\Publish\SPI\Persistence\Content $content
* @param $languageCode
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
*
* @return bool
*/
public function accept(Content $content, $languageCode)
{
return true;
}

mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
/**
* @param \eZ\Publish\SPI\Persistence\Content $content
* @param string $languageCode
*
* @return array|\eZ\Publish\SPI\Search\Field[]
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
*/
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->getEmptyValue() == $field->value),
andrerom marked this conversation as resolved.
Show resolved Hide resolved
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
new FieldType\BooleanField()
);
}
}

return $fields;
}
}
97 changes: 97 additions & 0 deletions lib/Query/Common/CriterionVisitor/Field/FieldEmpty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?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.
*/
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
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.
*/
class FieldEmpty extends Field
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
{
/**
* @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.
*
* @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
*
* @return bool
*/
public function canVisit(Criterion $criterion)
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
{
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)
mateuszbieniek marked this conversation as resolved.
Show resolved Hide resolved
{
$searchFields = $this->getSearchFields($criterion);

if (empty($searchFields)) {
throw new InvalidArgumentException(
'$criterion->target',
"No searchable fields found for the given criterion target '{$criterion->target}'."
alongosz marked this conversation as resolved.
Show resolved Hide resolved
);
}

$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}