| Current Path : /home/rtorresani/www/vendor/magento/module-elasticsearch/Model/DataProvider/Base/ |
| Current File : //home/rtorresani/www/vendor/magento/module-elasticsearch/Model/DataProvider/Base/Suggestions.php |
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Elasticsearch\Model\DataProvider\Base;
use Elasticsearch\Common\Exceptions\BadRequest400Exception;
use Exception;
use Magento\AdvancedSearch\Model\SuggestedQueriesInterface;
use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface;
use Magento\Elasticsearch\Model\Config;
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Search\Model\QueryInterface;
use Magento\Search\Model\QueryResultFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface as StoreManager;
use Psr\Log\LoggerInterface;
/**
* Default implementation to provide suggestions mechanism for Elasticsearch
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Suggestions implements SuggestedQueriesInterface
{
/**
* @var Config
*/
private $config;
/**
* @var QueryResultFactory
*/
private $queryResultFactory;
/**
* @var ConnectionManager
*/
private $connectionManager;
/**
* @var ScopeConfigInterface
*/
private $scopeConfig;
/**
* @var SearchIndexNameResolver
*/
private $searchIndexNameResolver;
/**
* @var StoreManager
*/
private $storeManager;
/**
* @var FieldProviderInterface
*/
private $fieldProvider;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var GetSuggestionFrequencyInterface
*/
private $getSuggestionFrequency;
/**
* @var array
*/
private $responseErrorExceptionList = [
'elasticsearchBadRequest404' => BadRequest400Exception::class
];
/**
* Suggestions constructor.
*
* @param ScopeConfigInterface $scopeConfig
* @param Config $config
* @param QueryResultFactory $queryResultFactory
* @param ConnectionManager $connectionManager
* @param SearchIndexNameResolver $searchIndexNameResolver
* @param StoreManager $storeManager
* @param FieldProviderInterface $fieldProvider
* @param LoggerInterface|null $logger
* @param GetSuggestionFrequencyInterface|null $getSuggestionFrequency
* @param array $responseErrorExceptionList
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
Config $config,
QueryResultFactory $queryResultFactory,
ConnectionManager $connectionManager,
SearchIndexNameResolver $searchIndexNameResolver,
StoreManager $storeManager,
FieldProviderInterface $fieldProvider,
LoggerInterface $logger = null,
?GetSuggestionFrequencyInterface $getSuggestionFrequency = null,
array $responseErrorExceptionList = []
) {
$this->queryResultFactory = $queryResultFactory;
$this->connectionManager = $connectionManager;
$this->scopeConfig = $scopeConfig;
$this->config = $config;
$this->searchIndexNameResolver = $searchIndexNameResolver;
$this->storeManager = $storeManager;
$this->fieldProvider = $fieldProvider;
$this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class);
$this->getSuggestionFrequency = $getSuggestionFrequency ?:
ObjectManager::getInstance()->get(GetSuggestionFrequencyInterface::class);
$this->responseErrorExceptionList = array_merge($this->responseErrorExceptionList, $responseErrorExceptionList);
}
/**
* @inheritdoc
*/
public function getItems(QueryInterface $query)
{
$result = [];
if ($this->isSuggestionsAllowed()) {
$isResultsCountEnabled = $this->isResultsCountEnabled();
try {
$suggestions = $this->getSuggestions($query);
} catch (Exception $e) {
if ($this->validateException($e)) {
$this->logger->critical($e);
$suggestions = [];
} else {
throw $e;
}
}
foreach ($suggestions as $suggestion) {
$count = null;
if ($isResultsCountEnabled) {
try {
$count = $this->getSuggestionFrequency->execute($suggestion['text']);
} catch (Exception $e) {
$this->logger->critical($e);
}
}
$result[] = $this->queryResultFactory->create(
[
'queryText' => $suggestion['text'],
'resultsCount' => $count,
]
);
}
}
return $result;
}
/**
* @inheritdoc
*/
public function isResultsCountEnabled()
{
return $this->scopeConfig->isSetFlag(
SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT_RESULTS_ENABLED,
ScopeInterface::SCOPE_STORE
);
}
/**
* Check if the given class name is in the exception list
*
* @param Exception $exception
* @return bool
*/
private function validateException(Exception $exception): bool
{
return in_array(get_class($exception), $this->responseErrorExceptionList, true);
}
/**
* Get Suggestions
*
* @param QueryInterface $query
* @return array
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
private function getSuggestions(QueryInterface $query)
{
$suggestions = [];
$searchSuggestionsCount = $this->getSearchSuggestionsCount();
$searchQuery = $this->initQuery($query);
$searchQuery = $this->addSuggestFields($searchQuery, $searchSuggestionsCount);
$result = $this->fetchQuery($searchQuery);
if (is_array($result)) {
foreach ($result['suggest'] ?? [] as $suggest) {
foreach ($suggest as $token) {
foreach ($token['options'] ?? [] as $key => $suggestion) {
$suggestions[$suggestion['score'] . '_' . $key] = $suggestion;
}
}
}
krsort($suggestions);
$texts = array_unique(array_column($suggestions, 'text'));
$suggestions = array_slice(
array_intersect_key(array_values($suggestions), $texts),
0,
$searchSuggestionsCount
);
}
return $suggestions;
}
/**
* Init Search Query
*
* @param QueryInterface $query
* @return array
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
private function initQuery(QueryInterface $query): array
{
$searchQuery = [
'index' => $this->searchIndexNameResolver->getIndexName(
$this->storeManager->getStore()->getId(),
Config::ELASTICSEARCH_TYPE_DEFAULT
),
'type' => Config::ELASTICSEARCH_TYPE_DEFAULT,
'body' => [
'suggest' => [
'text' => $query->getQueryText()
]
],
];
return $searchQuery;
}
/**
* Build Suggest on searchable fields.
*
* @param array $searchQuery
* @param int $searchSuggestionsCount
*
* @return array
*/
private function addSuggestFields($searchQuery, $searchSuggestionsCount)
{
$fields = $this->getSuggestFields();
foreach ($fields as $field) {
$searchQuery['body']['suggest']['phrase_' . $field] = [
'phrase' => [
'field' => $field,
'analyzer' => 'standard',
'size' => $searchSuggestionsCount,
'max_errors' => 0.9,
'direct_generator' => [
[
'field' => $field,
'min_word_length' => 3,
]
],
],
];
}
return $searchQuery;
}
/**
* Get fields to build suggest query on.
*
* @return array
*/
private function getSuggestFields()
{
$fields = array_filter($this->fieldProvider->getFields(), function ($field) {
return (($field['type'] ?? null) === 'text') && (($field['index'] ?? null) !== false);
});
return array_keys($fields);
}
/**
* Fetch Query
*
* @param array $query
* @return array
*/
private function fetchQuery(array $query)
{
return $this->connectionManager->getConnection()->query($query);
}
/**
* Get search suggestions Max Count from config
*
* @return int
*/
private function getSearchSuggestionsCount()
{
return (int) $this->scopeConfig->getValue(
SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT,
ScopeInterface::SCOPE_STORE
);
}
/**
* Is Search Suggestions Allowed
*
* @return bool
*/
private function isSuggestionsAllowed()
{
$isSuggestionsEnabled = $this->scopeConfig->isSetFlag(
SuggestedQueriesInterface::SEARCH_SUGGESTION_ENABLED,
ScopeInterface::SCOPE_STORE
);
$isEnabled = $this->config->isElasticsearchEnabled();
return $isEnabled && $isSuggestionsEnabled;
}
}