123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Elasticsearch\SearchAdapter\Dynamic;
- use Magento\Elasticsearch\SearchAdapter\QueryAwareInterface;
- use Magento\Elasticsearch\SearchAdapter\QueryContainer;
- /**
- * Elastic search data provider
- *
- * @api
- * @since 100.1.0
- */
- class DataProvider implements \Magento\Framework\Search\Dynamic\DataProviderInterface, QueryAwareInterface
- {
- /**
- * @var \Magento\Elasticsearch\SearchAdapter\ConnectionManager
- * @since 100.1.0
- */
- protected $connectionManager;
- /**
- * @var \Magento\Elasticsearch\Model\Adapter\FieldMapperInterface
- * @since 100.1.0
- */
- protected $fieldMapper;
- /**
- * @var \Magento\Catalog\Model\Layer\Filter\Price\Range
- * @since 100.1.0
- */
- protected $range;
- /**
- * @var \Magento\Framework\Search\Dynamic\IntervalFactory
- * @since 100.1.0
- */
- protected $intervalFactory;
- /**
- * @var \Magento\Elasticsearch\Model\Config
- * @deprecated 100.2.0 as this class shouldn't be responsible for query building
- * and should only modify existing query
- * @since 100.1.0
- */
- protected $clientConfig;
- /**
- * @var \Magento\Store\Model\StoreManagerInterface
- * @deprecated 100.2.0 as this class shouldn't be responsible for query building
- * and should only modify existing query
- * @since 100.1.0
- */
- protected $storeManager;
- /**
- * @var \Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver
- * @deprecated 100.2.0 as this class shouldn't be responsible for query building
- * and should only modify existing query
- * @since 100.1.0
- */
- protected $searchIndexNameResolver;
- /**
- * @var string
- * @deprecated 100.2.0 as this class shouldn't be responsible for query building
- * and should only modify existing query
- * @since 100.1.0
- */
- protected $indexerId;
- /**
- * @var \Magento\Framework\App\ScopeResolverInterface
- * @since 100.1.0
- */
- protected $scopeResolver;
- /**
- * @var QueryContainer
- */
- private $queryContainer;
- /**
- * @param \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager
- * @param \Magento\Elasticsearch\Model\Adapter\FieldMapperInterface $fieldMapper
- * @param \Magento\Catalog\Model\Layer\Filter\Price\Range $range
- * @param \Magento\Framework\Search\Dynamic\IntervalFactory $intervalFactory
- * @param \Magento\Elasticsearch\Model\Config $clientConfig
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param \Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver $searchIndexNameResolver
- * @param string $indexerId
- * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver
- * @param QueryContainer|null $queryContainer
- *
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- */
- public function __construct(
- \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager,
- \Magento\Elasticsearch\Model\Adapter\FieldMapperInterface $fieldMapper,
- \Magento\Catalog\Model\Layer\Filter\Price\Range $range,
- \Magento\Framework\Search\Dynamic\IntervalFactory $intervalFactory,
- \Magento\Elasticsearch\Model\Config $clientConfig,
- \Magento\Store\Model\StoreManagerInterface $storeManager,
- \Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver $searchIndexNameResolver,
- $indexerId,
- \Magento\Framework\App\ScopeResolverInterface $scopeResolver,
- QueryContainer $queryContainer = null
- ) {
- $this->connectionManager = $connectionManager;
- $this->fieldMapper = $fieldMapper;
- $this->range = $range;
- $this->intervalFactory = $intervalFactory;
- $this->clientConfig = $clientConfig;
- $this->storeManager = $storeManager;
- $this->searchIndexNameResolver = $searchIndexNameResolver;
- $this->indexerId = $indexerId;
- $this->scopeResolver = $scopeResolver;
- $this->queryContainer = $queryContainer;
- }
- /**
- * @inheritdoc
- * @since 100.1.0
- */
- public function getRange()
- {
- return $this->range->getPriceRange();
- }
- /**
- * @inheritdoc
- * @since 100.1.0
- */
- public function getAggregations(\Magento\Framework\Search\Dynamic\EntityStorage $entityStorage)
- {
- $aggregations = [
- 'count' => 0,
- 'max' => 0,
- 'min' => 0,
- 'std' => 0,
- ];
- $query = $this->getBasicSearchQuery($entityStorage);
- $fieldName = $this->fieldMapper->getFieldName('price');
- $query['body']['aggregations'] = [
- 'prices' => [
- 'extended_stats' => [
- 'field' => $fieldName,
- ],
- ],
- ];
- $queryResult = $this->connectionManager->getConnection()
- ->query($query);
- if (isset($queryResult['aggregations']['prices'])) {
- $aggregations = [
- 'count' => $queryResult['aggregations']['prices']['count'],
- 'max' => $queryResult['aggregations']['prices']['max'],
- 'min' => $queryResult['aggregations']['prices']['min'],
- 'std' => $queryResult['aggregations']['prices']['std_deviation'],
- ];
- }
- return $aggregations;
- }
- /**
- * @inheritdoc
- * @since 100.1.0
- */
- public function getInterval(
- \Magento\Framework\Search\Request\BucketInterface $bucket,
- array $dimensions,
- \Magento\Framework\Search\Dynamic\EntityStorage $entityStorage
- ) {
- $entityIds = $entityStorage->getSource();
- $fieldName = $this->fieldMapper->getFieldName('price');
- $dimension = current($dimensions);
- $storeId = $this->scopeResolver->getScope($dimension->getValue())->getId();
- return $this->intervalFactory->create(
- [
- 'entityIds' => $entityIds,
- 'storeId' => $storeId,
- 'fieldName' => $fieldName,
- ]
- );
- }
- /**
- * @inheritdoc
- * @since 100.1.0
- */
- public function getAggregation(
- \Magento\Framework\Search\Request\BucketInterface $bucket,
- array $dimensions,
- $range,
- \Magento\Framework\Search\Dynamic\EntityStorage $entityStorage
- ) {
- $result = [];
- $query = $this->getBasicSearchQuery($entityStorage);
- $fieldName = $this->fieldMapper->getFieldName($bucket->getField());
- $query['body']['aggregations'] = [
- 'prices' => [
- 'histogram' => [
- 'field' => $fieldName,
- 'interval' => (float)$range,
- 'min_doc_count' => 1,
- ],
- ],
- ];
- $queryResult = $this->connectionManager->getConnection()
- ->query($query);
- foreach ($queryResult['aggregations']['prices']['buckets'] as $bucket) {
- $key = (int)($bucket['key'] / $range + 1);
- $result[$key] = $bucket['doc_count'];
- }
- return $result;
- }
- /**
- * @inheritdoc
- * @since 100.1.0
- */
- public function prepareData($range, array $dbRanges)
- {
- $data = [];
- if (!empty($dbRanges)) {
- $lastIndex = array_keys($dbRanges);
- $lastIndex = $lastIndex[count($lastIndex) - 1];
- foreach ($dbRanges as $index => $count) {
- $fromPrice = $index == 1 ? '' : ($index - 1) * $range;
- $toPrice = $index == $lastIndex ? '' : $index * $range;
- $data[] = [
- 'from' => $fromPrice,
- 'to' => $toPrice,
- 'count' => $count,
- ];
- }
- }
- return $data;
- }
- /**
- * Returns a basic search query which can be used for aggregations calculation
- *
- * The query may be requested from a query container if it has been set
- * or may be build by entity storage and dimensions.
- *
- * Building a query by entity storage is actually deprecated as the query
- * built in this way may cause ElasticSearch's TooManyClauses exception.
- *
- * The code which is responsible for building query in-place should be removed someday,
- * but for now it's a question of backward compatibility as this class may be used somewhere else
- * by extension developers and we can't guarantee that they'll pass a query into constructor.
- *
- * @param \Magento\Framework\Search\Dynamic\EntityStorage $entityStorage
- * @param array $dimensions
- * @return array
- */
- private function getBasicSearchQuery(
- \Magento\Framework\Search\Dynamic\EntityStorage $entityStorage,
- array $dimensions = []
- ) {
- if (null !== $this->queryContainer) {
- return $this->queryContainer->getQuery();
- }
- $entityIds = $entityStorage->getSource();
- $dimension = current($dimensions);
- $storeId = false !== $dimension
- ? $this->scopeResolver->getScope($dimension->getValue())->getId()
- : $this->storeManager->getStore()->getId();
- $query = [
- 'index' => $this->searchIndexNameResolver->getIndexName($storeId, $this->indexerId),
- 'type' => $this->clientConfig->getEntityType(),
- 'body' => [
- 'fields' => [
- '_id',
- '_score',
- ],
- 'query' => [
- 'bool' => [
- 'must' => [
- [
- 'terms' => [
- '_id' => $entityIds,
- ],
- ],
- ],
- ],
- ],
- ],
- ];
- return $query;
- }
- }
|