'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]; } }