Skip to content

Commit 6f7fbc9

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-39942' into bugs
2 parents 8042eb1 + 45b6c02 commit 6f7fbc9

File tree

9 files changed

+440
-272
lines changed

9 files changed

+440
-272
lines changed

app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
use Magento\Framework\DB\Select;
1212
use Magento\Framework\Search\Adapter\Mysql\ConditionManager;
1313
use Magento\Framework\Search\Adapter\Mysql\IndexBuilderInterface;
14-
use Magento\Framework\Search\Adapter\Mysql\ScoreBuilder;
1514
use Magento\Framework\Search\Request\Dimension;
15+
use Magento\Framework\Search\Request\Query\Bool;
16+
use Magento\Framework\Search\Request\QueryInterface;
17+
use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
1618
use Magento\Framework\Search\RequestInterface;
1719
use Magento\Search\Model\ScopeResolver\IndexScopeResolver;
1820
use Magento\Store\Model\ScopeInterface;
@@ -82,22 +84,26 @@ public function build(RequestInterface $request)
8284
['search_index' => $searchIndexTable],
8385
['entity_id' => 'entity_id']
8486
)
85-
->joinLeft(
86-
['category_index' => $this->resource->getTableName('catalog_category_product_index')],
87-
'search_index.entity_id = category_index.product_id',
88-
[]
89-
)
9087
->joinLeft(
9188
['cea' => $this->resource->getTableName('catalog_eav_attribute')],
9289
'search_index.attribute_id = cea.attribute_id',
93-
[ScoreBuilder::WEIGHT_FIELD]
94-
)
95-
->joinLeft(
96-
['cpie' => $this->resource->getTableName('catalog_product_index_eav')],
97-
'search_index.entity_id = cpie.entity_id AND search_index.attribute_id = cpie.attribute_id',
9890
[]
9991
);
10092

93+
if ($this->isNeedToAddFilters($request)) {
94+
$select
95+
->joinLeft(
96+
['category_index' => $this->resource->getTableName('catalog_category_product_index')],
97+
'search_index.entity_id = category_index.product_id',
98+
[]
99+
)
100+
->joinLeft(
101+
['cpie' => $this->resource->getTableName('catalog_product_index_eav')],
102+
'search_index.entity_id = cpie.entity_id AND search_index.attribute_id = cpie.attribute_id',
103+
[]
104+
);
105+
}
106+
101107
$select = $this->processDimensions($request, $select);
102108

103109
$isShowOutOfStock = $this->config->isSetFlag(
@@ -113,8 +119,8 @@ public function build(RequestInterface $request)
113119
$this->storeManager->getWebsite()->getId()
114120
),
115121
[]
116-
)
117-
->where('stock_index.stock_status = ?', 1);
122+
);
123+
$select->where('stock_index.stock_status = ?', 1);
118124
}
119125

120126
return $select;
@@ -179,4 +185,43 @@ private function getSelect()
179185
{
180186
return $this->getReadConnection()->select();
181187
}
188+
189+
/**
190+
* @param RequestInterface $request
191+
* @return bool
192+
*/
193+
private function isNeedToAddFilters(RequestInterface $request)
194+
{
195+
return $this->hasFilters($request->getQuery());
196+
}
197+
198+
/**
199+
* @param QueryInterface $query
200+
* @return bool
201+
*/
202+
private function hasFilters(QueryInterface $query)
203+
{
204+
$hasFilters = false;
205+
switch ($query->getType()) {
206+
case RequestQueryInterface::TYPE_BOOL:
207+
/** @var \Magento\Framework\Search\Request\Query\Bool $query */
208+
foreach ($query->getMust() as $subQuery) {
209+
$hasFilters |= $this->hasFilters($subQuery);
210+
}
211+
foreach ($query->getShould() as $subQuery) {
212+
$hasFilters |= $this->hasFilters($subQuery);
213+
}
214+
foreach ($query->getMustNot() as $subQuery) {
215+
$hasFilters |= $this->hasFilters($subQuery);
216+
}
217+
break;
218+
case RequestQueryInterface::TYPE_FILTER:
219+
$hasFilters |= true;
220+
break;
221+
default:
222+
$hasFilters |= false;
223+
break;
224+
}
225+
return $hasFilters;
226+
}
182227
}

app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,13 @@ protected function setUp()
6969

7070
$this->request = $this->getMockBuilder('\Magento\Framework\Search\RequestInterface')
7171
->disableOriginalConstructor()
72-
->setMethods(['getIndex', 'getDimensions'])
72+
->setMethods(['getIndex', 'getDimensions', 'getQuery'])
7373
->getMockForAbstractClass();
74+
$this->request->expects($this->once())
75+
->method('getQuery')
76+
->willReturn(
77+
$this->getMockBuilder('Magento\Framework\Search\Request\QueryInterface')->getMockForAbstractClass()
78+
);
7479

7580
$this->config = $this->getMockBuilder('\Magento\Framework\App\Config\ScopeConfigInterface')
7681
->disableOriginalConstructor()
@@ -157,7 +162,7 @@ public function testBuildWithoutOutOfStock()
157162
->method('getDimensions')
158163
->willReturn($dimensions);
159164

160-
$this->mockBuild($index, $tableSuffix);
165+
$this->mockBuild($index, $tableSuffix, false);
161166

162167
$this->config->expects($this->once())
163168
->method('isSetFlag')
@@ -168,11 +173,11 @@ public function testBuildWithoutOutOfStock()
168173
$website = $this->getMockBuilder('Magento\Store\Model\Website')->disableOriginalConstructor()->getMock();
169174
$website->expects($this->once())->method('getId')->willReturn(1);
170175
$this->storeManager->expects($this->once())->method('getWebsite')->willReturn($website);
171-
$this->select->expects($this->at(4))
176+
$this->select->expects($this->at(2))
172177
->method('where')
173178
->with('(someName=someValue)')
174179
->willReturnSelf();
175-
$this->select->expects($this->at(5))
180+
$this->select->expects($this->at(3))
176181
->method('joinLeft')
177182
->with(
178183
['stock_index' => 'cataloginventory_stock_status'],
@@ -181,7 +186,7 @@ public function testBuildWithoutOutOfStock()
181186
[]
182187
)
183188
->willReturnSelf();
184-
$this->select->expects($this->at(6))
189+
$this->select->expects($this->at(4))
185190
->method('where')
186191
->with('stock_index.stock_status = ?', 1)
187192
->will($this->returnSelf());
@@ -190,7 +195,7 @@ public function testBuildWithoutOutOfStock()
190195
$this->assertSame($this->select, $result);
191196
}
192197

193-
protected function mockBuild($index, $tableSuffix)
198+
protected function mockBuild($index, $tableSuffix, $hasFilters = false)
194199
{
195200
$this->request->expects($this->atLeastOnce())
196201
->method('getIndex')
@@ -220,7 +225,7 @@ function ($index, $dimensions) {
220225
)
221226
);
222227

223-
$this->select->expects($this->once())
228+
$this->select->expects($this->at(0))
224229
->method('from')
225230
->with(
226231
['search_index' => $index . '_' . $tableSuffix],
@@ -229,30 +234,31 @@ function ($index, $dimensions) {
229234
->will($this->returnSelf());
230235

231236
$this->select->expects($this->at(1))
232-
->method('joinLeft')
233-
->with(
234-
['category_index' => 'catalog_category_product_index'],
235-
'search_index.entity_id = category_index.product_id',
236-
[]
237-
)
238-
->will($this->returnSelf());
239-
240-
$this->select->expects($this->at(2))
241237
->method('joinLeft')
242238
->with(
243239
['cea' => 'catalog_eav_attribute'],
244240
'search_index.attribute_id = cea.attribute_id',
245-
['search_weight']
246-
)
247-
->will($this->returnSelf());
248-
$this->select->expects($this->at(3))
249-
->method('joinLeft')
250-
->with(
251-
['cpie' => $this->resource->getTableName('catalog_product_index_eav')],
252-
'search_index.entity_id = cpie.entity_id AND search_index.attribute_id = cpie.attribute_id',
253241
[]
254242
)
255-
->willReturnSelf();
243+
->will($this->returnSelf());
244+
if ($hasFilters) {
245+
$this->select->expects($this->at(2))
246+
->method('joinLeft')
247+
->with(
248+
['category_index' => 'catalog_category_product_index'],
249+
'search_index.entity_id = category_index.product_id',
250+
[]
251+
)
252+
->will($this->returnSelf());
253+
$this->select->expects($this->at(3))
254+
->method('joinLeft')
255+
->with(
256+
['cpie' => $this->resource->getTableName('catalog_product_index_eav')],
257+
'search_index.entity_id = cpie.entity_id AND search_index.attribute_id = cpie.attribute_id',
258+
[]
259+
)
260+
->willReturnSelf();
261+
}
256262
}
257263

258264
/**

lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php

Lines changed: 81 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Magento\Framework\App\Resource;
99
use Magento\Framework\DB\Select;
1010
use Magento\Framework\Search\Adapter\Mysql\Filter\Builder;
11+
use Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match;
12+
use Magento\Framework\Search\Adapter\Mysql\Query\MatchContainer;
1113
use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer;
1214
use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainerFactory;
1315
use Magento\Framework\Search\EntityMetadata;
@@ -57,6 +59,10 @@ class Mapper
5759
* @var QueryContainerFactory
5860
*/
5961
private $queryContainerFactory;
62+
/**
63+
* @var Query\Builder\Match
64+
*/
65+
private $matchBuilder;
6066

6167
/**
6268
* @param ScoreBuilderFactory $scoreBuilderFactory
@@ -65,6 +71,7 @@ class Mapper
6571
* @param Resource|Resource $resource
6672
* @param EntityMetadata $entityMetadata
6773
* @param QueryContainerFactory $queryContainerFactory
74+
* @param Query\Builder\Match $matchBuilder
6875
* @param IndexBuilderInterface[] $indexProviders
6976
*/
7077
public function __construct(
@@ -74,6 +81,7 @@ public function __construct(
7481
Resource $resource,
7582
EntityMetadata $entityMetadata,
7683
QueryContainerFactory $queryContainerFactory,
84+
Match $matchBuilder,
7785
array $indexProviders
7886
) {
7987
$this->scoreBuilderFactory = $scoreBuilderFactory;
@@ -83,6 +91,7 @@ public function __construct(
8391
$this->entityMetadata = $entityMetadata;
8492
$this->indexProviders = $indexProviders;
8593
$this->queryContainerFactory = $queryContainerFactory;
94+
$this->matchBuilder = $matchBuilder;
8695
}
8796

8897
/**
@@ -116,42 +125,20 @@ public function buildQuery(RequestInterface $request)
116125
BoolQuery::QUERY_CONDITION_MUST,
117126
$queryContainer
118127
);
119-
$select->columns($scoreBuilder->build());
120128

121129
$filtersCount = $queryContainer->getFiltersCount();
122130
if ($filtersCount > 1) {
123131
$select->group('entity_id');
124132
$select->having('COUNT(DISTINCT search_index.attribute_id) = ' . $filtersCount);
125133
}
126134

127-
$select = $this->createAroundSelect($select, $scoreBuilder);
128-
129-
$matchQueries = $queryContainer->getDerivedQueries();
130-
131-
if ($matchQueries) {
132-
$subSelect = $select;
133-
$select = $this->resource->getConnection(Resource::DEFAULT_READ_RESOURCE)->select();
134-
$tables = array_merge($queryContainer->getDerivedQueryNames(), ['main_select.relevance']);
135-
$relevance = implode('.relevance + ', $tables);
136-
$select
137-
->from(
138-
['main_select' => $subSelect],
139-
[
140-
$this->entityMetadata->getEntityId() => 'entity_id',
141-
'relevance' => sprintf('(%s)', $relevance),
142-
]
143-
);
144-
145-
foreach ($matchQueries as $matchName => $matchSelect) {
146-
$select->join(
147-
[$matchName => $this->createAroundSelect($matchSelect, $scoreBuilder)],
148-
$matchName . '.entity_id = main_select.entity_id',
149-
[]
150-
);
151-
}
152-
}
153-
154-
$select->limit($request->getSize());
135+
$select = $this->addMatchQueries(
136+
$request,
137+
$queryContainer->getDerivedQueries(),
138+
$scoreBuilder,
139+
$select,
140+
$indexBuilder
141+
);
155142

156143
$select->limit($request->getSize());
157144
$select->order('relevance ' . Select::SQL_DESC);
@@ -334,4 +321,69 @@ private function processFilterQuery(
334321
$scoreBuilder->endQuery($query->getBoost());
335322
return $select;
336323
}
324+
325+
/**
326+
* @param RequestInterface $request
327+
* @param MatchContainer[] $matchQueries
328+
* @param ScoreBuilder $scoreBuilder
329+
* @param Select $select
330+
* @param IndexBuilderInterface $indexBuilder
331+
* @return Select
332+
* @internal param QueryContainer $queryContainer
333+
*/
334+
private function addMatchQueries(
335+
RequestInterface $request,
336+
array $matchQueries,
337+
ScoreBuilder $scoreBuilder,
338+
Select $select,
339+
IndexBuilderInterface $indexBuilder
340+
) {
341+
if (!$matchQueries) {
342+
$select->columns($scoreBuilder->build());
343+
$select = $this->createAroundSelect($select, $scoreBuilder);
344+
} elseif (count($matchQueries) === 1) {
345+
$matchContainer = reset($matchQueries);
346+
$this->matchBuilder->build(
347+
$scoreBuilder,
348+
$select,
349+
$matchContainer->getRequest(),
350+
$matchContainer->getConditionType()
351+
);
352+
$select->columns($scoreBuilder->build());
353+
$select = $this->createAroundSelect($select, $scoreBuilder);
354+
} elseif (count($matchQueries) > 1) {
355+
$select->columns($scoreBuilder->build());
356+
$select = $this->createAroundSelect($select, $scoreBuilder);
357+
$subSelect = $select;
358+
$select = $this->resource->getConnection(Resource::DEFAULT_READ_RESOURCE)->select();
359+
$tables = array_merge(array_keys($matchQueries), ['main_select.relevance']);
360+
$relevance = implode('.relevance + ', $tables);
361+
$select
362+
->from(
363+
['main_select' => $subSelect],
364+
[
365+
$this->entityMetadata->getEntityId() => 'entity_id',
366+
'relevance' => sprintf('(%s)', $relevance),
367+
]
368+
);
369+
370+
foreach ($matchQueries as $matchName => $matchContainer) {
371+
$matchSelect = $indexBuilder->build($request);
372+
$matchScoreBuilder = $this->scoreBuilderFactory->create();
373+
$matchSelect = $this->matchBuilder->build(
374+
$matchScoreBuilder,
375+
$matchSelect,
376+
$matchContainer->getRequest(),
377+
$matchContainer->getConditionType()
378+
);
379+
$matchSelect->columns($matchScoreBuilder->build());
380+
$select->join(
381+
[$matchName => $this->createAroundSelect($matchSelect, $scoreBuilder)],
382+
$matchName . '.entity_id = main_select.entity_id',
383+
[]
384+
);
385+
}
386+
}
387+
return $select;
388+
}
337389
}

0 commit comments

Comments
 (0)