123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\Search\Adapter\Mysql;
- use Magento\Framework\App\ResourceConnection;
- use Magento\Framework\DB\Ddl\Table;
- use Magento\Framework\DB\Select;
- use Magento\Framework\Search\Adapter\Mysql\Filter\Builder;
- use Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match;
- use Magento\Framework\Search\Adapter\Mysql\Query\MatchContainer;
- use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer;
- use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainerFactory;
- use Magento\Framework\Search\EntityMetadata;
- use Magento\Framework\Search\Request\Query\BoolExpression as BoolQuery;
- use Magento\Framework\Search\Request\Query\Filter as FilterQuery;
- use Magento\Framework\Search\Request\Query\Match as MatchQuery;
- use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
- use Magento\Framework\Search\RequestInterface;
- /**
- * Mapper class. Maps library request to specific adapter dependent query
- *
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @api
- * @deprecated 102.0.0
- * @see \Magento\ElasticSearch
- * @since 100.0.2
- */
- class Mapper
- {
- /**
- * @var ScoreBuilder
- */
- private $scoreBuilderFactory;
- /**
- * @var Filter\Builder
- */
- private $filterBuilder;
- /**
- * @var ConditionManager
- */
- private $conditionManager;
- /**
- * @var IndexBuilderInterface[]
- */
- private $indexProviders;
- /**
- * @var Resource
- */
- private $resource;
- /**
- * @var EntityMetadata
- */
- private $entityMetadata;
- /**
- * @var QueryContainerFactory
- */
- private $queryContainerFactory;
- /**
- * @var Query\Builder\Match
- */
- private $matchBuilder;
- /**
- * @var TemporaryStorage
- */
- private $temporaryStorage;
- /**
- * @var string
- */
- private $relevanceCalculationMethod;
- /**
- * @var TemporaryStorageFactory
- */
- private $temporaryStorageFactory;
- /**
- * @param ScoreBuilderFactory $scoreBuilderFactory
- * @param Builder $filterBuilder
- * @param ConditionManager $conditionManager
- * @param ResourceConnection $resource
- * @param EntityMetadata $entityMetadata
- * @param QueryContainerFactory $queryContainerFactory
- * @param Query\Builder\Match $matchBuilder
- * @param TemporaryStorageFactory $temporaryStorageFactory
- * @param IndexBuilderInterface[] $indexProviders
- * @param string $relevanceCalculationMethod
- *
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- */
- public function __construct(
- ScoreBuilderFactory $scoreBuilderFactory,
- Builder $filterBuilder,
- ConditionManager $conditionManager,
- ResourceConnection $resource,
- EntityMetadata $entityMetadata,
- QueryContainerFactory $queryContainerFactory,
- Match $matchBuilder,
- TemporaryStorageFactory $temporaryStorageFactory,
- array $indexProviders,
- $relevanceCalculationMethod = 'SUM'
- ) {
- $this->scoreBuilderFactory = $scoreBuilderFactory;
- $this->filterBuilder = $filterBuilder;
- $this->conditionManager = $conditionManager;
- $this->resource = $resource;
- $this->entityMetadata = $entityMetadata;
- $this->indexProviders = $indexProviders;
- $this->queryContainerFactory = $queryContainerFactory;
- $this->matchBuilder = $matchBuilder;
- $this->temporaryStorage = $temporaryStorageFactory->create();
- $this->temporaryStorageFactory = $temporaryStorageFactory;
- if (!in_array($relevanceCalculationMethod, ['SUM', 'MAX'], true)) {
- throw new \LogicException('Unsupported relevance calculation method used. Only SUM and MAX are allowed');
- }
- $this->relevanceCalculationMethod = $relevanceCalculationMethod;
- }
- /**
- * Build adapter dependent query
- *
- * @param RequestInterface $request
- * @return Select
- * @throws \LogicException
- * @throws \Zend_Db_Exception
- * @throws \InvalidArgumentException
- */
- public function buildQuery(RequestInterface $request)
- {
- if (!array_key_exists($request->getIndex(), $this->indexProviders)) {
- throw new \LogicException('Index provider not configured');
- }
- $indexBuilder = $this->indexProviders[$request->getIndex()];
- $queryContainer = $this->queryContainerFactory->create(
- [
- 'indexBuilder' => $indexBuilder,
- 'request' => $request,
- ]
- );
- $select = $indexBuilder->build($request);
- /** @var ScoreBuilder $scoreBuilder */
- $scoreBuilder = $this->scoreBuilderFactory->create();
- $select = $this->processQuery(
- $scoreBuilder,
- $request->getQuery(),
- $select,
- BoolQuery::QUERY_CONDITION_MUST,
- $queryContainer
- );
- $select = $this->addDerivedQueries(
- $request,
- $queryContainer,
- $scoreBuilder,
- $select,
- $indexBuilder
- );
- $select->limit($request->getSize(), $request->getFrom());
- $select->order('relevance ' . Select::SQL_DESC)->order('entity_id ' . Select::SQL_DESC);
- return $select;
- }
- /**
- * Creates Select which wraps search result select
- *
- * It is used to group search results by entity id.
- *
- * @param Select $select
- * @param ScoreBuilder $scoreBuilder
- * @return Select
- */
- private function createAroundSelect(Select $select, ScoreBuilder $scoreBuilder)
- {
- $parentSelect = $this->getConnection()->select();
- $parentSelect->from(
- ['main_select' => $select],
- [
- $this->entityMetadata->getEntityId() => 'entity_id',
- 'relevance' => sprintf('%s(%s)', $this->relevanceCalculationMethod, $scoreBuilder->getScoreAlias()),
- ]
- )->group($this->entityMetadata->getEntityId());
- return $parentSelect;
- }
- /**
- * Process query
- *
- * @param ScoreBuilder $scoreBuilder
- * @param RequestQueryInterface $query
- * @param Select $select
- * @param string $conditionType
- * @param QueryContainer $queryContainer
- * @return Select
- * @throws \InvalidArgumentException
- */
- protected function processQuery(
- ScoreBuilder $scoreBuilder,
- RequestQueryInterface $query,
- Select $select,
- $conditionType,
- QueryContainer $queryContainer
- ) {
- switch ($query->getType()) {
- case RequestQueryInterface::TYPE_MATCH:
- /** @var MatchQuery $query */
- $select = $queryContainer->addMatchQuery(
- $select,
- $query,
- $conditionType
- );
- break;
- case RequestQueryInterface::TYPE_BOOL:
- /** @var BoolQuery $query */
- $select = $this->processBoolQuery($scoreBuilder, $query, $select, $queryContainer);
- break;
- case RequestQueryInterface::TYPE_FILTER:
- /** @var FilterQuery $query */
- $select = $this->processFilterQuery($scoreBuilder, $query, $select, $conditionType, $queryContainer);
- break;
- default:
- throw new \InvalidArgumentException(sprintf('Unknown query type \'%s\'', $query->getType()));
- }
- return $select;
- }
- /**
- * Process bool query
- *
- * @param ScoreBuilder $scoreBuilder
- * @param BoolQuery $query
- * @param Select $select
- * @param QueryContainer $queryContainer
- * @return Select
- */
- private function processBoolQuery(
- ScoreBuilder $scoreBuilder,
- BoolQuery $query,
- Select $select,
- QueryContainer $queryContainer
- ) {
- $scoreBuilder->startQuery();
- $select = $this->processBoolQueryCondition(
- $scoreBuilder,
- $query->getMust(),
- $select,
- BoolQuery::QUERY_CONDITION_MUST,
- $queryContainer
- );
- $select = $this->processBoolQueryCondition(
- $scoreBuilder,
- $query->getShould(),
- $select,
- BoolQuery::QUERY_CONDITION_SHOULD,
- $queryContainer
- );
- $select = $this->processBoolQueryCondition(
- $scoreBuilder,
- $query->getMustNot(),
- $select,
- BoolQuery::QUERY_CONDITION_NOT,
- $queryContainer
- );
- $scoreBuilder->endQuery($query->getBoost());
- return $select;
- }
- /**
- * Process bool query condition (must, should, must_not)
- *
- * @param ScoreBuilder $scoreBuilder
- * @param RequestQueryInterface[] $subQueryList
- * @param Select $select
- * @param string $conditionType
- * @param QueryContainer $queryContainer
- * @return Select
- */
- private function processBoolQueryCondition(
- ScoreBuilder $scoreBuilder,
- array $subQueryList,
- Select $select,
- $conditionType,
- QueryContainer $queryContainer
- ) {
- foreach ($subQueryList as $subQuery) {
- $select = $this->processQuery($scoreBuilder, $subQuery, $select, $conditionType, $queryContainer);
- }
- return $select;
- }
- /**
- * Process filter query
- *
- * @param ScoreBuilder $scoreBuilder
- * @param FilterQuery $query
- * @param Select $select
- * @param string $conditionType
- * @param QueryContainer $queryContainer
- * @return Select
- */
- private function processFilterQuery(
- ScoreBuilder $scoreBuilder,
- FilterQuery $query,
- Select $select,
- $conditionType,
- QueryContainer $queryContainer
- ) {
- $scoreBuilder->startQuery();
- switch ($query->getReferenceType()) {
- case FilterQuery::REFERENCE_QUERY:
- $select = $this->processQuery(
- $scoreBuilder,
- $query->getReference(),
- $select,
- $conditionType,
- $queryContainer
- );
- $scoreBuilder->endQuery($query->getBoost());
- break;
- case FilterQuery::REFERENCE_FILTER:
- $filterCondition = $this->filterBuilder->build($query->getReference(), $conditionType);
- if ($filterCondition) {
- $select->where($filterCondition);
- }
- break;
- }
- $scoreBuilder->endQuery($query->getBoost());
- return $select;
- }
- /**
- * Add match queries to select.
- *
- * @param RequestInterface $request
- * @param QueryContainer $queryContainer
- * @param ScoreBuilder $scoreBuilder
- * @param Select $select
- * @param IndexBuilderInterface $indexBuilder
- * @return Select
- * @throws \Zend_Db_Exception
- */
- private function addDerivedQueries(
- RequestInterface $request,
- QueryContainer $queryContainer,
- ScoreBuilder $scoreBuilder,
- Select $select,
- IndexBuilderInterface $indexBuilder
- ) {
- $matchQueries = $queryContainer->getMatchQueries();
- if (!$matchQueries) {
- $select->columns($scoreBuilder->build());
- $select = $this->createAroundSelect($select, $scoreBuilder);
- } else {
- $matchContainer = array_shift($matchQueries);
- $this->matchBuilder->build(
- $scoreBuilder,
- $select,
- $matchContainer->getRequest(),
- $matchContainer->getConditionType()
- );
- $select->columns($scoreBuilder->build());
- $select = $this->createAroundSelect($select, $scoreBuilder);
- $select = $this->addMatchQueries($request, $select, $indexBuilder, $matchQueries);
- }
- return $select;
- }
- /**
- * Get connection.
- *
- * @return false|\Magento\Framework\DB\Adapter\AdapterInterface
- */
- private function getConnection()
- {
- return $this->resource->getConnection();
- }
- /**
- * Add match queries to select.
- *
- * @param RequestInterface $request
- * @param Select $select
- * @param IndexBuilderInterface $indexBuilder
- * @param MatchContainer[] $matchQueries
- * @return Select
- */
- private function addMatchQueries(
- RequestInterface $request,
- Select $select,
- IndexBuilderInterface $indexBuilder,
- array $matchQueries
- ) {
- $queriesCount = count($matchQueries);
- if ($queriesCount) {
- $table = $this->temporaryStorage->storeDocumentsFromSelect($select);
- foreach ($matchQueries as $matchContainer) {
- $queriesCount--;
- $matchScoreBuilder = $this->scoreBuilderFactory->create();
- $matchSelect = $this->matchBuilder->build(
- $matchScoreBuilder,
- $indexBuilder->build($request),
- $matchContainer->getRequest(),
- $matchContainer->getConditionType()
- );
- $select = $this->joinPreviousResultToSelect($matchSelect, $table, $matchScoreBuilder);
- if ($queriesCount) {
- $previousResultTable = $table;
- $table = $this->temporaryStorage->storeDocumentsFromSelect($select);
- $this->getConnection()->dropTable($previousResultTable->getName());
- }
- }
- }
- return $select;
- }
- /**
- * Join previous result to select.
- *
- * @param Select $query
- * @param Table $previousResultTable
- * @param ScoreBuilder $scoreBuilder
- * @return Select
- * @throws \Zend_Db_Exception
- */
- private function joinPreviousResultToSelect(Select $query, Table $previousResultTable, ScoreBuilder $scoreBuilder)
- {
- $query->joinInner(
- ['previous_results' => $previousResultTable->getName()],
- 'previous_results.entity_id = search_index.entity_id',
- []
- );
- $scoreBuilder->addCondition('previous_results.score', false);
- $query->columns($scoreBuilder->build());
- $query = $this->createAroundSelect($query, $scoreBuilder);
- return $query;
- }
- }
|