vendor/shopware/elasticsearch/Framework/DataAbstractionLayer/ElasticsearchEntitySearcher.php line 71

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Elasticsearch\Framework\DataAbstractionLayer;
  3. use Elasticsearch\Client;
  4. use ONGR\ElasticsearchDSL\Aggregation\AbstractAggregation;
  5. use ONGR\ElasticsearchDSL\Aggregation\Bucketing\FilterAggregation;
  6. use ONGR\ElasticsearchDSL\Aggregation\Metric\CardinalityAggregation;
  7. use ONGR\ElasticsearchDSL\Search;
  8. use Shopware\Core\Framework\Context;
  9. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearcherInterface;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Grouping\FieldGrouping;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
  15. use Shopware\Elasticsearch\Framework\DataAbstractionLayer\Event\ElasticsearchEntitySearcherSearchEvent;
  16. use Shopware\Elasticsearch\Framework\ElasticsearchHelper;
  17. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  18. class ElasticsearchEntitySearcher implements EntitySearcherInterface
  19. {
  20.     public const MAX_LIMIT 10000;
  21.     /**
  22.      * @var Client
  23.      */
  24.     private $client;
  25.     /**
  26.      * @var EntitySearcherInterface
  27.      */
  28.     private $decorated;
  29.     /**
  30.      * @var ElasticsearchHelper
  31.      */
  32.     private $helper;
  33.     /**
  34.      * @var CriteriaParser
  35.      */
  36.     private $criteriaParser;
  37.     /**
  38.      * @var AbstractElasticsearchSearchHydrator
  39.      */
  40.     private $hydrator;
  41.     /**
  42.      * @var EventDispatcherInterface
  43.      */
  44.     private $eventDispatcher;
  45.     public function __construct(
  46.         Client $client,
  47.         EntitySearcherInterface $searcher,
  48.         ElasticsearchHelper $helper,
  49.         CriteriaParser $criteriaParser,
  50.         AbstractElasticsearchSearchHydrator $hydrator,
  51.         EventDispatcherInterface $eventDispatcher
  52.     ) {
  53.         $this->client $client;
  54.         $this->decorated $searcher;
  55.         $this->helper $helper;
  56.         $this->criteriaParser $criteriaParser;
  57.         $this->hydrator $hydrator;
  58.         $this->eventDispatcher $eventDispatcher;
  59.     }
  60.     public function search(EntityDefinition $definitionCriteria $criteriaContext $context): IdSearchResult
  61.     {
  62.         if (!$this->helper->allowSearch($definition$context)) {
  63.             return $this->decorated->search($definition$criteria$context);
  64.         }
  65.         $search $this->createSearch($criteria$definition$context);
  66.         $this->eventDispatcher->dispatch(
  67.             new ElasticsearchEntitySearcherSearchEvent(
  68.                 $search,
  69.                 $definition,
  70.                 $criteria,
  71.                 $context
  72.             )
  73.         );
  74.         $search $this->convertSearch($criteria$definition$context$search);
  75.         try {
  76.             $result $this->client->search([
  77.                 'index' => $this->helper->getIndexName($definition$context->getLanguageId()),
  78.                 'type' => $definition->getEntityName(),
  79.                 'track_total_hits' => true,
  80.                 'body' => $search,
  81.             ]);
  82.         } catch (\Throwable $e) {
  83.             $this->helper->logOrThrowException($e);
  84.             return $this->decorated->search($definition$criteria$context);
  85.         }
  86.         return $this->hydrator->hydrate($definition$criteria$context$result);
  87.     }
  88.     private function createSearch(Criteria $criteriaEntityDefinition $definitionContext $context): Search
  89.     {
  90.         $search = new Search();
  91.         $this->helper->handleIds($definition$criteria$search$context);
  92.         $this->helper->addFilters($definition$criteria$search$context);
  93.         $this->helper->addPostFilters($definition$criteria$search$context);
  94.         $this->helper->addQueries($definition$criteria$search$context);
  95.         $this->helper->addSortings($definition$criteria$search$context);
  96.         $this->helper->addTerm($criteria$search$context$definition);
  97.         $search->setSize($criteria->getLimit());
  98.         if ($criteria->getLimit() === null) {
  99.             $search->setSize(self::MAX_LIMIT);
  100.         }
  101.         $search->setFrom($criteria->getOffset());
  102.         return $search;
  103.     }
  104.     private function convertSearch(Criteria $criteriaEntityDefinition $definitionContext $contextSearch $search): array
  105.     {
  106.         if (!$criteria->getGroupFields()) {
  107.             return $search->toArray();
  108.         }
  109.         $aggregation $this->buildTotalCountAggregation($criteria$definition$context);
  110.         $search->addAggregation($aggregation);
  111.         $array $search->toArray();
  112.         $array['collapse'] = $this->parseGrouping($criteria->getGroupFields(), $definition$context);
  113.         return $array;
  114.     }
  115.     /**
  116.      * @param FieldGrouping[] $groupings
  117.      */
  118.     private function parseGrouping(array $groupingsEntityDefinition $definitionContext $context): array
  119.     {
  120.         /** @var FieldGrouping $grouping */
  121.         $grouping array_shift($groupings);
  122.         $accessor $this->criteriaParser->buildAccessor($definition$grouping->getField(), $context);
  123.         if (empty($groupings)) {
  124.             return ['field' => $accessor];
  125.         }
  126.         return [
  127.             'field' => $accessor,
  128.             'inner_hits' => [
  129.                 'name' => 'inner',
  130.                 'collapse' => $this->parseGrouping($groupings$definition$context),
  131.             ],
  132.         ];
  133.     }
  134.     private function buildTotalCountAggregation(Criteria $criteriaEntityDefinition $definitionContext $context): AbstractAggregation
  135.     {
  136.         $groupings $criteria->getGroupFields();
  137.         if (\count($groupings) === 1) {
  138.             /** @var FieldGrouping $first */
  139.             $first array_shift($groupings);
  140.             $accessor $this->criteriaParser->buildAccessor($definition$first->getField(), $context);
  141.             $aggregation = new CardinalityAggregation('total-count');
  142.             $aggregation->setField($accessor);
  143.             return $this->addPostFilterAggregation($criteria$definition$context$aggregation);
  144.         }
  145.         $fields = [];
  146.         foreach ($groupings as $grouping) {
  147.             $accessor $this->criteriaParser->buildAccessor($definition$grouping->getField(), $context);
  148.             $fields[] = sprintf(
  149.                 "
  150.                 if (doc['%s'].size()==0) {
  151.                     value = value + 'empty';
  152.                 } else {
  153.                     value = value + doc['%s'].value;
  154.                 }",
  155.                 $accessor,
  156.                 $accessor
  157.             );
  158.         }
  159.         $script '
  160.             def value = \'\';
  161.             ' implode(' '$fields) . '
  162.             return value;
  163.         ';
  164.         $aggregation = new CardinalityAggregation('total-count');
  165.         $aggregation->setScript($script);
  166.         return $this->addPostFilterAggregation($criteria$definition$context$aggregation);
  167.     }
  168.     private function addPostFilterAggregation(Criteria $criteriaEntityDefinition $definitionContext $contextCardinalityAggregation $aggregation): AbstractAggregation
  169.     {
  170.         if (!$criteria->getPostFilters()) {
  171.             return $aggregation;
  172.         }
  173.         $query $this->criteriaParser->parseFilter(
  174.             new MultiFilter(MultiFilter::CONNECTION_AND$criteria->getPostFilters()),
  175.             $definition,
  176.             $definition->getEntityName(),
  177.             $context
  178.         );
  179.         $filterAgg = new FilterAggregation('total-filtered-count'$query);
  180.         $filterAgg->addAggregation($aggregation);
  181.         return $filterAgg;
  182.     }
  183. }