Skip to content

Commit f9365f9

Browse files
committed
Merge remote-tracking branch 'origin/2.3-develop' into 2.3-develop-pr4
2 parents 7decefe + 8f8caf3 commit f9365f9

File tree

8 files changed

+286
-39
lines changed

8 files changed

+286
-39
lines changed

app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use Magento\Framework\App\ObjectManager;
1212
use Magento\Framework\App\ResourceConnection;
13+
use Magento\Framework\EntityManager\MetadataPool;
1314

1415
/**
1516
* Abstract action reindex class
@@ -70,25 +71,33 @@ abstract class AbstractAction
7071
*/
7172
private $cacheCleaner;
7273

74+
/**
75+
* @var MetadataPool
76+
*/
77+
private $metadataPool;
78+
7379
/**
7480
* @param ResourceConnection $resource
7581
* @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory
7682
* @param \Magento\Catalog\Model\Product\Type $catalogProductType
7783
* @param \Magento\Framework\Indexer\CacheContext $cacheContext
7884
* @param \Magento\Framework\Event\ManagerInterface $eventManager
85+
* @param MetadataPool|null $metadataPool
7986
*/
8087
public function __construct(
8188
ResourceConnection $resource,
8289
\Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory,
8390
\Magento\Catalog\Model\Product\Type $catalogProductType,
8491
\Magento\Framework\Indexer\CacheContext $cacheContext,
85-
\Magento\Framework\Event\ManagerInterface $eventManager
92+
\Magento\Framework\Event\ManagerInterface $eventManager,
93+
MetadataPool $metadataPool = null
8694
) {
8795
$this->_resource = $resource;
8896
$this->_indexerFactory = $indexerFactory;
8997
$this->_catalogProductType = $catalogProductType;
9098
$this->cacheContext = $cacheContext;
9199
$this->eventManager = $eventManager;
100+
$this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class);
92101
}
93102

94103
/**
@@ -154,10 +163,15 @@ protected function _getTable($entityName)
154163
public function getRelationsByChild($childIds)
155164
{
156165
$connection = $this->_getConnection();
157-
$select = $connection->select()
158-
->from($this->_getTable('catalog_product_relation'), 'parent_id')
159-
->where('child_id IN(?)', $childIds);
160-
166+
$linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
167+
->getLinkField();
168+
$select = $connection->select()->from(
169+
['cpe' => $this->_getTable('catalog_product_entity')],
170+
'entity_id'
171+
)->join(
172+
['relation' => $this->_getTable('catalog_product_relation')],
173+
'relation.parent_id = cpe.' . $linkField
174+
)->where('child_id IN(?)', $childIds);
161175
return $connection->fetchCol($select);
162176
}
163177

@@ -230,7 +244,8 @@ protected function _reindexRows($productIds = [])
230244
if (!is_array($productIds)) {
231245
$productIds = [$productIds];
232246
}
233-
247+
$parentIds = $this->getRelationsByChild($productIds);
248+
$productIds = $parentIds ? array_unique(array_merge($parentIds, $productIds)) : $productIds;
234249
$this->getCacheCleaner()->clean($productIds, function () use ($productIds) {
235250
$this->doReindex($productIds);
236251
});
@@ -248,13 +263,10 @@ private function doReindex($productIds = [])
248263
{
249264
$connection = $this->_getConnection();
250265

251-
$parentIds = $this->getRelationsByChild($productIds);
252-
$processIds = $parentIds ? array_merge($parentIds, $productIds) : $productIds;
253-
254266
// retrieve product types by processIds
255267
$select = $connection->select()
256268
->from($this->_getTable('catalog_product_entity'), ['entity_id', 'type_id'])
257-
->where('entity_id IN(?)', $processIds);
269+
->where('entity_id IN(?)', $productIds);
258270
$pairs = $connection->fetchPairs($select);
259271

260272
$byType = [];
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CatalogInventory\Model\Plugin;
7+
8+
use Magento\Catalog\Model\Product\Action as ProductAction;
9+
10+
/**
11+
* Plugin for Magento\Catalog\Model\Product\Action
12+
*/
13+
class ReindexUpdatedProducts
14+
{
15+
/**
16+
* @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor
17+
*/
18+
private $indexerProcessor;
19+
20+
/**
21+
* @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor
22+
*/
23+
public function __construct(\Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor)
24+
{
25+
$this->indexerProcessor = $indexerProcessor;
26+
}
27+
28+
/**
29+
* Reindex on product attribute mass change
30+
*
31+
* @param ProductAction $subject
32+
* @param ProductAction $action
33+
* @param array $productIds
34+
* @return ProductAction
35+
*
36+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
37+
*/
38+
public function afterUpdateAttributes(
39+
ProductAction $subject,
40+
ProductAction $action,
41+
$productIds
42+
) {
43+
$this->indexerProcessor->reindexList(array_unique($productIds));
44+
return $action;
45+
}
46+
}

app/code/Magento/CatalogInventory/etc/di.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@
7878
<type name="Magento\Catalog\Model\Product">
7979
<plugin name="catalogInventoryAfterLoad" type="Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/>
8080
</type>
81+
<type name="Magento\Catalog\Model\Product\Action">
82+
<plugin name="ReindexUpdatedProducts" type="Magento\CatalogInventory\Model\Plugin\ReindexUpdatedProducts"/>
83+
</type>
84+
<type name="Magento\CatalogInventory\Setup\UpgradeData">
85+
<arguments>
86+
<argument name="indexerProcessor" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\Processor</argument>
87+
</arguments>
88+
</type>
8189
<type name="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor">
8290
<arguments>
8391
<argument name="baseSelectProcessors" xsi:type="array">

app/code/Magento/Search/Model/SynonymAnalyzer.php

Lines changed: 101 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
7+
declare(strict_types=1);
68
namespace Magento\Search\Model;
79

810
use Magento\Search\Api\SynonymAnalyzerInterface;
911

12+
/**
13+
* SynonymAnalyzer responsible for search of synonyms matching a word or a phrase.
14+
*/
1015
class SynonymAnalyzer implements SynonymAnalyzerInterface
1116
{
1217
/**
@@ -39,58 +44,125 @@ public function __construct(SynonymReader $synReader)
3944
* ]
4045
* @param string $phrase
4146
* @return array
47+
* @throws \Magento\Framework\Exception\LocalizedException
4248
*/
4349
public function getSynonymsForPhrase($phrase)
4450
{
45-
$synGroups = [];
51+
$result = [];
4652

47-
if (empty($phrase)) {
48-
return $synGroups;
53+
if (empty(trim($phrase))) {
54+
return $result;
4955
}
5056

51-
$rows = $this->synReaderModel->loadByPhrase($phrase)->getData();
52-
$synonyms = [];
53-
foreach ($rows as $row) {
54-
$synonyms [] = $row['synonyms'];
55-
}
57+
$synonymGroups = $this->getSynonymGroupsByPhrase($phrase);
58+
59+
// Replace multiple spaces in a row with the only one space
60+
$phrase = preg_replace("/ {2,}/", " ", $phrase);
5661

5762
// Go through every returned record looking for presence of the actual phrase. If there were no matching
5863
// records found in DB then create a new entry for it in the returned array
5964
$words = explode(' ', $phrase);
60-
foreach ($words as $w) {
61-
$position = $this->findInArray($w, $synonyms);
62-
if ($position !== false) {
63-
$synGroups[] = explode(',', $synonyms[$position]);
64-
} else {
65-
// No synonyms were found. Return the original word in this position
66-
$synGroups[] = [$w];
65+
66+
foreach ($words as $offset => $word) {
67+
$synonyms = [$word];
68+
69+
if ($synonymGroups) {
70+
$pattern = $this->getSearchPattern(\array_slice($words, $offset));
71+
$position = $this->findInArray($pattern, $synonymGroups);
72+
if ($position !== null) {
73+
$synonyms = explode(',', $synonymGroups[$position]);
74+
}
6775
}
76+
77+
$result[] = $synonyms;
6878
}
69-
return $synGroups;
79+
80+
return $result;
7081
}
7182

7283
/**
73-
* Helper method to find the presence of $word in $wordsArray. If found, the particular array index is returned.
84+
* Helper method to find the matching of $pattern to $synonymGroupsToExamine.
85+
* If matches, the particular array index is returned.
7486
* Otherwise false will be returned.
7587
*
76-
* @param string $word
77-
* @param $array $wordsArray
78-
* @return boolean | int
88+
* @param string $pattern
89+
* @param array $synonymGroupsToExamine
90+
* @return int|null
7991
*/
80-
private function findInArray($word, $wordsArray)
92+
private function findInArray(string $pattern, array $synonymGroupsToExamine)
8193
{
82-
if (empty($wordsArray)) {
83-
return false;
84-
}
8594
$position = 0;
86-
foreach ($wordsArray as $wordsLine) {
87-
$pattern = '/^' . $word . ',|,' . $word . ',|,' . $word . '$/';
88-
$rv = preg_match($pattern, $wordsLine);
89-
if ($rv != 0) {
95+
foreach ($synonymGroupsToExamine as $synonymGroup) {
96+
$matchingResultCode = preg_match($pattern, $synonymGroup);
97+
if ($matchingResultCode === 1) {
9098
return $position;
9199
}
92100
$position++;
93101
}
94-
return false;
102+
return null;
103+
}
104+
105+
/**
106+
* Returns a regular expression to search for synonyms of the phrase represented as the list of words.
107+
*
108+
* Returned pattern contains expression to search for a part of the phrase from the beginning.
109+
*
110+
* For example, in the phrase "Elizabeth is the English queen" with subset from the very first word,
111+
* the method will build an expression which looking for synonyms for all these patterns:
112+
* - Elizabeth is the English queen
113+
* - Elizabeth is the English
114+
* - Elizabeth is the
115+
* - Elizabeth is
116+
* - Elizabeth
117+
*
118+
* For the same phrase on the second iteration with the first word "is" it will match for these synonyms:
119+
* - is the English queen
120+
* - is the English
121+
* - is the
122+
* - is
123+
*
124+
* The pattern looking for exact match and will not find these phrases as synonyms:
125+
* - Is there anybody in the room?
126+
* - Is the English is most popular language?
127+
* - Is the English queen Elizabeth?
128+
*
129+
* Take into account that returned pattern expects that data will be represented as comma-separated value.
130+
*
131+
* @param array $words
132+
* @return string
133+
*/
134+
private function getSearchPattern(array $words): string
135+
{
136+
$patterns = [];
137+
for ($lastItem = count($words); $lastItem > 0; $lastItem--) {
138+
$phrase = implode("\s+", \array_slice($words, 0, $lastItem));
139+
$patterns[] = '^' . $phrase . ',';
140+
$patterns[] = ',' . $phrase . ',';
141+
$patterns[] = ',' . $phrase . '$';
142+
}
143+
144+
$pattern = '/' . implode('|', $patterns) . '/i';
145+
return $pattern;
146+
}
147+
148+
/**
149+
* Get all synonym groups for the phrase
150+
*
151+
* Returns an array of synonyms which are represented as comma-separated value for each item in the list
152+
*
153+
* @param string $phrase
154+
* @return string[]
155+
* @throws \Magento\Framework\Exception\LocalizedException
156+
*/
157+
private function getSynonymGroupsByPhrase(string $phrase): array
158+
{
159+
$result = [];
160+
161+
/** @var array $synonymGroups */
162+
$synonymGroups = $this->synReaderModel->loadByPhrase($phrase)->getData();
163+
foreach ($synonymGroups as $row) {
164+
$result[] = $row['synonyms'];
165+
}
166+
return $result;
95167
}
96168
}

app/code/Magento/Search/Model/SynonymReader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ protected function _construct()
7878
*
7979
* @param string $phrase
8080
* @return $this
81+
* @throws \Magento\Framework\Exception\LocalizedException
8182
* @since 100.1.0
8283
*/
8384
public function loadByPhrase($phrase)

0 commit comments

Comments
 (0)