Skip to content

Commit

Permalink
EZP-27458: Impl. Aggregation API
Browse files Browse the repository at this point in the history
  • Loading branch information
adamwojs committed Oct 6, 2020
1 parent 340c86a commit e5c3422
Show file tree
Hide file tree
Showing 91 changed files with 4,404 additions and 26 deletions.
14 changes: 10 additions & 4 deletions bundle/ApiLoader/SolrEngineFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ class SolrEngineFactory
private $documentMapper;

/** @var \EzSystems\EzPlatformSolrSearchEngine\ResultExtractor */
private $resultExtractor;
private $contentResultExtractor;

/** @var \EzSystems\EzPlatformSolrSearchEngine\ResultExtractor */
private $locationResultExtractor;

public function __construct(
RepositoryConfigurationProvider $repositoryConfigurationProvider,
Expand All @@ -51,7 +54,8 @@ public function __construct(
CoreFilterRegistry $coreFilterRegistry,
Handler $contentHandler,
DocumentMapper $documentMapper,
ResultExtractor $resultExtractor
ResultExtractor $contentResultExtractor,
ResultExtractor $locationResultExtractor
) {
$this->repositoryConfigurationProvider = $repositoryConfigurationProvider;
$this->defaultConnection = $defaultConnection;
Expand All @@ -60,7 +64,8 @@ public function __construct(
$this->coreFilterRegistry = $coreFilterRegistry;
$this->contentHandler = $contentHandler;
$this->documentMapper = $documentMapper;
$this->resultExtractor = $resultExtractor;
$this->contentResultExtractor = $contentResultExtractor;
$this->locationResultExtractor = $locationResultExtractor;
}

public function buildEngine()
Expand All @@ -76,7 +81,8 @@ public function buildEngine()
$gateway,
$this->contentHandler,
$this->documentMapper,
$this->resultExtractor,
$this->contentResultExtractor,
$this->locationResultExtractor,
$coreFilter
);
}
Expand Down
3 changes: 2 additions & 1 deletion bundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ services:
$coreFilterRegistry: '@EzSystems\EzPlatformSolrSearchEngine\CoreFilter\CoreFilterRegistry'
$contentHandler: "@ezpublish.spi.persistence.content_handler"
$documentMapper: "@ezpublish.search.solr.document_mapper"
$resultExtractor: "@ezpublish.search.solr.result_extractor"
$contentResultExtractor: "@ezpublish.search.solr.result_extractor.content"
$locationResultExtractor: "@ezpublish.search.solr.result_extractor.location"

ezpublish.solr.boost_factor_provider_factory:
class: "%ezpublish.solr.boost_factor_provider_factory.class%"
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
],
"require": {
"php": "^7.3",
"ext-json": "*",
"ezsystems/ezplatform-kernel": "^1.2@dev",
"netgen/query-translator": "^1.0.2",
"symfony/http-kernel": "^5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ public function mapFields(Content $content)
array_keys($versionInfo->names),
new FieldType\MultipleStringField()
),
new Field(
'content_language_codes_raw',
array_keys($versionInfo->names),
new FieldType\MultipleIdentifierField(['raw' => true])
),
new Field(
'content_main_language_code',
$contentInfo->mainLanguageCode,
Expand Down
2 changes: 1 addition & 1 deletion lib/Gateway/Native.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function __construct(
*/
public function findContent(Query $query, array $languageSettings = [])
{
$parameters = $this->contentQueryConverter->convert($query);
$parameters = $this->contentQueryConverter->convert($query, $languageSettings);

return $this->internalFind($parameters, $languageSettings);
}
Expand Down
36 changes: 30 additions & 6 deletions lib/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,26 @@ class Handler implements SearchHandlerInterface, Capable, ContentTranslationHand
/**
* Result extractor.
*
* @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. Use $contentResultExtractor or $locationResultExtractor instead of $resultExtractor.
*
* @var \EzSystems\EzPlatformSolrSearchEngine\ResultExtractor
*/
protected $resultExtractor;

/**
* Content result extractor.
*
* @var \EzSystems\EzPlatformSolrSearchEngine\ResultExtractor
*/
protected $contentResultExtractor;

/**
* Location result extractor.
*
* @var \EzSystems\EzPlatformSolrSearchEngine\ResultExtractor
*/
protected $locationResultExtractor;

/**
* Core filter service.
*
Expand All @@ -97,14 +113,19 @@ public function __construct(
Gateway $gateway,
ContentHandler $contentHandler,
DocumentMapper $mapper,
ResultExtractor $resultExtractor,
ResultExtractor $contentResultExtractor,
ResultExtractor $locationResultExtractor,
CoreFilter $coreFilter
) {
$this->gateway = $gateway;
$this->contentHandler = $contentHandler;
$this->mapper = $mapper;
$this->resultExtractor = $resultExtractor;
$this->contentResultExtractor = $contentResultExtractor;
$this->locationResultExtractor = $locationResultExtractor;
$this->coreFilter = $coreFilter;

// For BC these are still set
$this->resultExtractor = $contentResultExtractor;
}

/**
Expand All @@ -131,9 +152,11 @@ public function findContent(Query $query, array $languageFilter = [])
DocumentMapper::DOCUMENT_TYPE_IDENTIFIER_CONTENT
);

return $this->resultExtractor->extract(
return $this->contentResultExtractor->extract(
$this->gateway->findContent($query, $languageFilter),
$query->facetBuilders
$query->facetBuilders,
$query->aggregations,
$languageFilter
);
}

Expand Down Expand Up @@ -201,9 +224,10 @@ public function findLocations(LocationQuery $query, array $languageFilter = [])
DocumentMapper::DOCUMENT_TYPE_IDENTIFIER_LOCATION
);

return $this->resultExtractor->extract(
return $this->locationResultExtractor->extract(
$this->gateway->findLocations($query, $languageFilter),
$query->facetBuilders
$query->facetBuilders,
$query->aggregations
);
}

Expand Down
28 changes: 28 additions & 0 deletions lib/Query/AggregationVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?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;

use eZ\Publish\API\Repository\Values\Content\Query\Aggregation;

interface AggregationVisitor
{
/**
* Check if visitor is applicable to current aggreagtion.
*/
public function canVisit(Aggregation $aggregation, array $languageFilter): bool;

/**
* @return string[]
*/
public function visit(
AggregationVisitor $dispatcherVisitor,
Aggregation $aggregation,
array $languageFilter
): array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?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\AggregationVisitor;

use DateTimeInterface;
use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation;
use eZ\Publish\API\Repository\Values\Content\Query\Aggregation;
use EzSystems\EzPlatformSolrSearchEngine\Query\AggregationVisitor;

abstract class AbstractRangeAggregationVisitor implements AggregationVisitor
{
/**
* @param \eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation $aggregation
*/
public function visit(
AggregationVisitor $dispatcherVisitor,
Aggregation $aggregation,
array $languageFilter
): array {
$field = $this->getTargetField($aggregation);

$rangeFacets = [];
foreach ($aggregation->getRanges() as $range) {
$from = $this->formatRangeValue($range->getFrom());
$to = $this->formatRangeValue($range->getTo());

$rangeFacets["${from}_${to}"] = [
'type' => 'query',
'q' => sprintf('%s:[%s TO %s}', $field, $from, $to),
];
}

return [
'type' => 'query',
'q' => '*:*',
'facet' => $rangeFacets,
];
}

abstract protected function getTargetField(AbstractRangeAggregation $aggregation): string;

private function formatRangeValue($value): string
{
if ($value === null) {
return '*';
}

if ($value instanceof DateTimeInterface) {
return $value->format('Y-m-d\\TH:i:s\\Z');
}

return (string)$value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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\AggregationVisitor;

use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractStatsAggregation;
use eZ\Publish\API\Repository\Values\Content\Query\Aggregation;
use EzSystems\EzPlatformSolrSearchEngine\Query\AggregationVisitor;

abstract class AbstractStatsAggregationVisitor implements AggregationVisitor
{
/**
* @param \eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractStatsAggregation $aggregation
*/
public function visit(
AggregationVisitor $dispatcherVisitor,
Aggregation $aggregation,
array $languageFilter
): array {
$field = $this->getTargetField($aggregation);

return [
'type' => 'query',
'q' => '*:*',
'facet' => [
'sum' => "sum($field)",
'min' => "min($field)",
'max' => "max($field)",
'avg' => "avg($field)",
],
];
}

abstract protected function getTargetField(AbstractStatsAggregation $aggregation): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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\AggregationVisitor;

use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation;
use eZ\Publish\API\Repository\Values\Content\Query\Aggregation;
use EzSystems\EzPlatformSolrSearchEngine\Query\AggregationVisitor;

abstract class AbstractTermAggregationVisitor implements AggregationVisitor
{
/**
* @param \eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation $aggregation
*/
public function visit(
AggregationVisitor $dispatcherVisitor,
Aggregation $aggregation,
array $languageFilter
): array {
return [
'type' => 'terms',
'field' => $this->getTargetField($aggregation),
'limit' => $aggregation->getLimit(),
'mincount' => $aggregation->getMinCount(),
];
}

abstract protected function getTargetField(AbstractTermAggregation $aggregation): string;
}
19 changes: 19 additions & 0 deletions lib/Query/Common/AggregationVisitor/AggregationFieldResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?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\AggregationVisitor;

use eZ\Publish\API\Repository\Values\Content\Query\Aggregation;

/**
* Resolves search index field name used for aggregation.
*/
interface AggregationFieldResolver
{
public function resolveTargetField(Aggregation $aggregation): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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\AggregationVisitor\AggregationFieldResolver;

use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\FieldAggregation;
use eZ\Publish\API\Repository\Values\Content\Query\Aggregation;
use eZ\Publish\Core\Search\Common\FieldNameResolver;
use EzSystems\EzPlatformSolrSearchEngine\Query\Common\AggregationVisitor\AggregationFieldResolver;
use RuntimeException;

final class ContentFieldAggregationFieldResolver implements AggregationFieldResolver
{
/** @var \eZ\Publish\Core\Search\Common\FieldNameResolver */
private $fieldNameResolver;

/** @var string */
private $searchFieldName;

public function __construct(FieldNameResolver $fieldNameResolver, string $searchFieldName)
{
$this->fieldNameResolver = $fieldNameResolver;
$this->searchFieldName = $searchFieldName;
}

public function resolveTargetField(Aggregation $aggregation): string
{
if (!($aggregation instanceof FieldAggregation)) {
throw new RuntimeException('Expected instance of ' . FieldAggregation::class . ' , got ' . get_class($aggregation));
}

$searchFieldName = $this->fieldNameResolver->getAggregationFieldName(
$aggregation->getContentTypeIdentifier(),
$aggregation->getFieldDefinitionIdentifier(),
$this->searchFieldName
);

if ($searchFieldName === null) {
throw new RuntimeException('No searchable fields found for the provided aggregation target');
}

return $searchFieldName;
}
}
Loading

0 comments on commit e5c3422

Please sign in to comment.