Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Mapper.php
Go to the documentation of this file.
1 <?php
7 
20 use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
22 
32 class Mapper
33 {
37  private $scoreBuilderFactory;
38 
42  private $filterBuilder;
43 
47  private $conditionManager;
48 
52  private $indexProviders;
53 
57  private $resource;
58 
62  private $entityMetadata;
63 
67  private $queryContainerFactory;
68 
72  private $matchBuilder;
73 
77  private $temporaryStorage;
78 
82  private $relevanceCalculationMethod;
83 
87  private $temporaryStorageFactory;
88 
103  public function __construct(
104  ScoreBuilderFactory $scoreBuilderFactory,
105  Builder $filterBuilder,
106  ConditionManager $conditionManager,
107  ResourceConnection $resource,
108  EntityMetadata $entityMetadata,
109  QueryContainerFactory $queryContainerFactory,
110  Match $matchBuilder,
111  TemporaryStorageFactory $temporaryStorageFactory,
112  array $indexProviders,
113  $relevanceCalculationMethod = 'SUM'
114  ) {
115  $this->scoreBuilderFactory = $scoreBuilderFactory;
116  $this->filterBuilder = $filterBuilder;
117  $this->conditionManager = $conditionManager;
118  $this->resource = $resource;
119  $this->entityMetadata = $entityMetadata;
120  $this->indexProviders = $indexProviders;
121  $this->queryContainerFactory = $queryContainerFactory;
122  $this->matchBuilder = $matchBuilder;
123  $this->temporaryStorage = $temporaryStorageFactory->create();
124  $this->temporaryStorageFactory = $temporaryStorageFactory;
125  if (!in_array($relevanceCalculationMethod, ['SUM', 'MAX'], true)) {
126  throw new \LogicException('Unsupported relevance calculation method used. Only SUM and MAX are allowed');
127  }
128  $this->relevanceCalculationMethod = $relevanceCalculationMethod;
129  }
130 
140  public function buildQuery(RequestInterface $request)
141  {
142  if (!array_key_exists($request->getIndex(), $this->indexProviders)) {
143  throw new \LogicException('Index provider not configured');
144  }
145 
146  $indexBuilder = $this->indexProviders[$request->getIndex()];
147 
148  $queryContainer = $this->queryContainerFactory->create(
149  [
150  'indexBuilder' => $indexBuilder,
151  'request' => $request,
152  ]
153  );
154  $select = $indexBuilder->build($request);
156  $scoreBuilder = $this->scoreBuilderFactory->create();
157  $select = $this->processQuery(
158  $scoreBuilder,
159  $request->getQuery(),
160  $select,
161  BoolQuery::QUERY_CONDITION_MUST,
162  $queryContainer
163  );
164 
165  $select = $this->addDerivedQueries(
166  $request,
167  $queryContainer,
168  $scoreBuilder,
169  $select,
171  );
172 
173  $select->limit($request->getSize(), $request->getFrom());
174  $select->order('relevance ' . Select::SQL_DESC)->order('entity_id ' . Select::SQL_DESC);
175  return $select;
176  }
177 
187  private function createAroundSelect(Select $select, ScoreBuilder $scoreBuilder)
188  {
189  $parentSelect = $this->getConnection()->select();
190  $parentSelect->from(
191  ['main_select' => $select],
192  [
193  $this->entityMetadata->getEntityId() => 'entity_id',
194  'relevance' => sprintf('%s(%s)', $this->relevanceCalculationMethod, $scoreBuilder->getScoreAlias()),
195  ]
196  )->group($this->entityMetadata->getEntityId());
197  return $parentSelect;
198  }
199 
211  protected function processQuery(
212  ScoreBuilder $scoreBuilder,
213  RequestQueryInterface $query,
214  Select $select,
215  $conditionType,
216  QueryContainer $queryContainer
217  ) {
218  switch ($query->getType()) {
219  case RequestQueryInterface::TYPE_MATCH:
221  $select = $queryContainer->addMatchQuery(
222  $select,
223  $query,
224  $conditionType
225  );
226  break;
227  case RequestQueryInterface::TYPE_BOOL:
229  $select = $this->processBoolQuery($scoreBuilder, $query, $select, $queryContainer);
230  break;
231  case RequestQueryInterface::TYPE_FILTER:
233  $select = $this->processFilterQuery($scoreBuilder, $query, $select, $conditionType, $queryContainer);
234  break;
235  default:
236  throw new \InvalidArgumentException(sprintf('Unknown query type \'%s\'', $query->getType()));
237  }
238  return $select;
239  }
240 
250  private function processBoolQuery(
251  ScoreBuilder $scoreBuilder,
252  BoolQuery $query,
253  Select $select,
254  QueryContainer $queryContainer
255  ) {
256  $scoreBuilder->startQuery();
257 
258  $select = $this->processBoolQueryCondition(
259  $scoreBuilder,
260  $query->getMust(),
261  $select,
262  BoolQuery::QUERY_CONDITION_MUST,
263  $queryContainer
264  );
265 
266  $select = $this->processBoolQueryCondition(
267  $scoreBuilder,
268  $query->getShould(),
269  $select,
270  BoolQuery::QUERY_CONDITION_SHOULD,
271  $queryContainer
272  );
273 
274  $select = $this->processBoolQueryCondition(
275  $scoreBuilder,
276  $query->getMustNot(),
277  $select,
278  BoolQuery::QUERY_CONDITION_NOT,
279  $queryContainer
280  );
281 
282  $scoreBuilder->endQuery($query->getBoost());
283 
284  return $select;
285  }
286 
297  private function processBoolQueryCondition(
298  ScoreBuilder $scoreBuilder,
299  array $subQueryList,
300  Select $select,
301  $conditionType,
302  QueryContainer $queryContainer
303  ) {
304  foreach ($subQueryList as $subQuery) {
305  $select = $this->processQuery($scoreBuilder, $subQuery, $select, $conditionType, $queryContainer);
306  }
307  return $select;
308  }
309 
320  private function processFilterQuery(
321  ScoreBuilder $scoreBuilder,
322  FilterQuery $query,
323  Select $select,
324  $conditionType,
325  QueryContainer $queryContainer
326  ) {
327  $scoreBuilder->startQuery();
328  switch ($query->getReferenceType()) {
329  case FilterQuery::REFERENCE_QUERY:
330  $select = $this->processQuery(
331  $scoreBuilder,
332  $query->getReference(),
333  $select,
334  $conditionType,
335  $queryContainer
336  );
337  $scoreBuilder->endQuery($query->getBoost());
338  break;
339  case FilterQuery::REFERENCE_FILTER:
340  $filterCondition = $this->filterBuilder->build($query->getReference(), $conditionType);
341  if ($filterCondition) {
342  $select->where($filterCondition);
343  }
344  break;
345  }
346  $scoreBuilder->endQuery($query->getBoost());
347  return $select;
348  }
349 
361  private function addDerivedQueries(
362  RequestInterface $request,
363  QueryContainer $queryContainer,
364  ScoreBuilder $scoreBuilder,
365  Select $select,
366  IndexBuilderInterface $indexBuilder
367  ) {
368  $matchQueries = $queryContainer->getMatchQueries();
369  if (!$matchQueries) {
370  $select->columns($scoreBuilder->build());
371  $select = $this->createAroundSelect($select, $scoreBuilder);
372  } else {
373  $matchContainer = array_shift($matchQueries);
374  $this->matchBuilder->build(
375  $scoreBuilder,
376  $select,
377  $matchContainer->getRequest(),
378  $matchContainer->getConditionType()
379  );
380  $select->columns($scoreBuilder->build());
381  $select = $this->createAroundSelect($select, $scoreBuilder);
382  $select = $this->addMatchQueries($request, $select, $indexBuilder, $matchQueries);
383  }
384 
385  return $select;
386  }
387 
393  private function getConnection()
394  {
395  return $this->resource->getConnection();
396  }
397 
407  private function addMatchQueries(
408  RequestInterface $request,
409  Select $select,
410  IndexBuilderInterface $indexBuilder,
411  array $matchQueries
412  ) {
413  $queriesCount = count($matchQueries);
414  if ($queriesCount) {
415  $table = $this->temporaryStorage->storeDocumentsFromSelect($select);
416  foreach ($matchQueries as $matchContainer) {
417  $queriesCount--;
418  $matchScoreBuilder = $this->scoreBuilderFactory->create();
419  $matchSelect = $this->matchBuilder->build(
420  $matchScoreBuilder,
421  $indexBuilder->build($request),
422  $matchContainer->getRequest(),
423  $matchContainer->getConditionType()
424  );
425  $select = $this->joinPreviousResultToSelect($matchSelect, $table, $matchScoreBuilder);
426  if ($queriesCount) {
427  $previousResultTable = $table;
428  $table = $this->temporaryStorage->storeDocumentsFromSelect($select);
429  $this->getConnection()->dropTable($previousResultTable->getName());
430  }
431  }
432  }
433  return $select;
434  }
435 
445  private function joinPreviousResultToSelect(Select $query, Table $previousResultTable, ScoreBuilder $scoreBuilder)
446  {
447  $query->joinInner(
448  ['previous_results' => $previousResultTable->getName()],
449  'previous_results.entity_id = search_index.entity_id',
450  []
451  );
452  $scoreBuilder->addCondition('previous_results.score', false);
453  $query->columns($scoreBuilder->build());
454 
455  $query = $this->createAroundSelect($query, $scoreBuilder);
456 
457  return $query;
458  }
459 }
$resource
Definition: bulk.php:12
const SQL_DESC
Definition: Select.php:82
$table
Definition: trigger.php:14
__construct(ScoreBuilderFactory $scoreBuilderFactory, Builder $filterBuilder, ConditionManager $conditionManager, ResourceConnection $resource, EntityMetadata $entityMetadata, QueryContainerFactory $queryContainerFactory, Match $matchBuilder, TemporaryStorageFactory $temporaryStorageFactory, array $indexProviders, $relevanceCalculationMethod='SUM')
Definition: Mapper.php:103