205 lines
7.0 KiB
PHP
Executable File
205 lines
7.0 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* @author Amasty Team
|
|
* @copyright Copyright (c) Amasty (https://www.amasty.com)
|
|
* @package Elastic Search Base for Magento 2
|
|
*/
|
|
|
|
namespace Amasty\ElasticSearch\Model\Search;
|
|
|
|
use Amasty\ElasticSearch\Api\RelevanceRuleRepositoryInterface;
|
|
use Amasty\ElasticSearch\Model\Indexer\Data\External\RelevanceBoostDataMapper;
|
|
use Amasty\ElasticSearch\Model\Search\GetRequestQuery\InjectSubqueryInterface;
|
|
use Amasty\ElasticSearch\Model\Search\GetRequestQuery\SortingProvider;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Framework\Search\Request\Query\BoolExpression as BoolQuery;
|
|
use Magento\Framework\Search\Request\Query\Filter as FilterQuery;
|
|
use Magento\Framework\Search\Request\Query\Match as MatchQuery;
|
|
use Magento\Framework\Search\Request\QueryInterface;
|
|
use Magento\Framework\Search\RequestInterface;
|
|
|
|
class GetRequestQuery
|
|
{
|
|
public const QUERY_CONDITION_MUST_NOT = 'must_not';
|
|
|
|
/**
|
|
* @var \Amasty\ElasticSearch\Model\Config
|
|
*/
|
|
private $config;
|
|
|
|
/**
|
|
* @var GetRequestQuery\GetAggregations
|
|
*/
|
|
private $getAggregations;
|
|
|
|
/**
|
|
* @var InjectSubqueryInterface[]
|
|
*/
|
|
private $subqueryInjectors;
|
|
|
|
/**
|
|
* @var SortingProvider
|
|
*/
|
|
private $sortingProvider;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $requestNamesForApplyRelevanceRules;
|
|
|
|
/**
|
|
* @var string[]
|
|
*/
|
|
private $booleanMethods = [
|
|
BoolQuery::QUERY_CONDITION_MUST => 'getMust',
|
|
BoolQuery::QUERY_CONDITION_SHOULD => 'getShould',
|
|
self::QUERY_CONDITION_MUST_NOT => 'getMustNot'
|
|
];
|
|
|
|
/**
|
|
* @var RelevanceRuleRepositoryInterface
|
|
*/
|
|
private $relevanceRuleRepository;
|
|
|
|
public function __construct(
|
|
\Amasty\ElasticSearch\Model\Config $config,
|
|
GetRequestQuery\GetAggregations $getAggregations,
|
|
SortingProvider $sortingProvider,
|
|
RelevanceRuleRepositoryInterface $relevanceRuleRepository,
|
|
array $subqueryInjectors,
|
|
array $requestNamesForApplyRelevanceRules = []
|
|
) {
|
|
$this->config = $config;
|
|
$this->subqueryInjectors = $subqueryInjectors;
|
|
$this->getAggregations = $getAggregations;
|
|
$this->sortingProvider = $sortingProvider;
|
|
$this->requestNamesForApplyRelevanceRules = array_unique($requestNamesForApplyRelevanceRules);
|
|
$this->relevanceRuleRepository = $relevanceRuleRepository;
|
|
}
|
|
|
|
/**
|
|
* @param RequestInterface $request
|
|
* @return array
|
|
* @throws LocalizedException
|
|
*/
|
|
public function execute(RequestInterface $request): array
|
|
{
|
|
$storeId = (int) current($request->getDimensions())->getValue();
|
|
$query = [
|
|
'index' => $this->config->getIndexName('product', $storeId),
|
|
'type' => $this->config->getEntityType(),
|
|
'body' => [
|
|
'from' => $request->getFrom(),
|
|
'size' => $request->getSize(),
|
|
'stored_fields' => ['_id', '_score'],
|
|
'sort' => $this->sortingProvider->execute($request),
|
|
'query' => $this->getQuery($request)
|
|
],
|
|
];
|
|
$aggregations = $this->getAggregations->execute($request);
|
|
if ($aggregations) {
|
|
$query['body']['aggregations'] = $aggregations;
|
|
}
|
|
|
|
if (isset($query['body']['query']['script_score']['query']['bool']['should'])) {
|
|
$query['body']['query']['script_score']['query']['bool']['minimum_should_match'] = 1;
|
|
}
|
|
if (isset($query['body']['query']['bool']['should'])) {
|
|
$query['body']['query']['bool']['minimum_should_match'] = 1;
|
|
}
|
|
|
|
$query['track_total_hits'] = true;
|
|
|
|
return $query;
|
|
}
|
|
|
|
private function getQuery(RequestInterface $request): array
|
|
{
|
|
if ($this->isCanApplyRelevanceSorting($request) && $this->isNeedApplyRelevanceSorting()) {
|
|
$query = [
|
|
'script_score' => [
|
|
'query' => $this->processQuery($request->getQuery(), [], BoolQuery::QUERY_CONDITION_MUST),
|
|
'script' => [
|
|
'source' => "params['_source']['" . RelevanceBoostDataMapper::ATTRIBUTE_NAME . "']*_score"
|
|
]
|
|
]
|
|
];
|
|
} else {
|
|
$query = $this->processQuery($request->getQuery(), [], BoolQuery::QUERY_CONDITION_MUST);
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
public function isCanApplyRelevanceSorting(RequestInterface $request): bool
|
|
{
|
|
return in_array($request->getName(), $this->requestNamesForApplyRelevanceRules);
|
|
}
|
|
|
|
private function isNeedApplyRelevanceSorting(): bool
|
|
{
|
|
return $this->relevanceRuleRepository->getActiveRules()->getSize() !== 0;
|
|
}
|
|
|
|
/**
|
|
* @param QueryInterface $request
|
|
* @param array $elasticQuery
|
|
* @param $conditionType
|
|
* @return array
|
|
* @throws LocalizedException
|
|
*/
|
|
private function processQuery(QueryInterface $request, array $elasticQuery, $conditionType)
|
|
{
|
|
switch ($request->getType()) {
|
|
case QueryInterface::TYPE_BOOL:
|
|
/** @var BoolQuery $request */
|
|
foreach ($this->booleanMethods as $conditionType => $method) {
|
|
foreach ($request->{$method}() as $subQuery) {
|
|
$elasticQuery = $this->processQuery($subQuery, $elasticQuery, $conditionType);
|
|
}
|
|
}
|
|
break;
|
|
case QueryInterface::TYPE_MATCH:
|
|
/** @var MatchQuery $request */
|
|
$addSubquery = $this->getSubqueryInjectorByType('match');
|
|
$elasticQuery = $addSubquery->execute($elasticQuery, $request, $conditionType);
|
|
break;
|
|
case QueryInterface::TYPE_FILTER:
|
|
/** @var FilterQuery $request */
|
|
if ($request->getReferenceType() === FilterQuery::REFERENCE_QUERY) {
|
|
$elasticQuery = $this->processQuery($request->getReference(), $elasticQuery, $conditionType);
|
|
} elseif ($request->getReferenceType() === FilterQuery::REFERENCE_FILTER) {
|
|
$addSubquery = $this->getSubqueryInjectorByType($request->getReference()->getType());
|
|
$elasticQuery = $addSubquery->execute($elasticQuery, $request, $conditionType);
|
|
} else {
|
|
throw new LocalizedException(
|
|
__("Builder for %1 reference type doesn't exist", $request->getReferenceType())
|
|
);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
throw new LocalizedException(__("Processor for %1 type doesn't exist", $request->getType()));
|
|
}
|
|
|
|
return $elasticQuery;
|
|
}
|
|
|
|
/**
|
|
* @param string $filterType
|
|
* @return InjectSubqueryInterface
|
|
* @throws LocalizedException
|
|
*/
|
|
private function getSubqueryInjectorByType($filterType)
|
|
{
|
|
if (!array_key_exists($filterType, $this->subqueryInjectors)) {
|
|
throw new LocalizedException(__("'%1' filter type is not supported", $filterType));
|
|
}
|
|
|
|
return $this->subqueryInjectors[$filterType];
|
|
}
|
|
}
|