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
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.
*/
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 string $languageCode
*
* @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 \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),
andrerom marked this conversation as resolved.
Show resolved Hide resolved
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.
*/
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.
*/
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}'."
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}