Skip to content

Commit

Permalink
Merge pull request #2214 from magento-panda/forwardports-pr
Browse files Browse the repository at this point in the history
Fixed issues:
  - MAGETWO-88082 [Magento Cloud] Using search synonyms from the same group gives different results
  - MAGETWO-72620 Configurable product shows up on category page on storefront when child products are disabled via Mass Action
  • Loading branch information
igrybkov authored Mar 14, 2018
2 parents 7b13100 + 093e98d commit 8f8caf3
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\EntityManager\MetadataPool;

/**
* Abstract action reindex class
Expand Down Expand Up @@ -70,25 +71,33 @@ abstract class AbstractAction
*/
private $cacheCleaner;

/**
* @var MetadataPool
*/
private $metadataPool;

/**
* @param ResourceConnection $resource
* @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory
* @param \Magento\Catalog\Model\Product\Type $catalogProductType
* @param \Magento\Framework\Indexer\CacheContext $cacheContext
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param MetadataPool|null $metadataPool
*/
public function __construct(
ResourceConnection $resource,
\Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory,
\Magento\Catalog\Model\Product\Type $catalogProductType,
\Magento\Framework\Indexer\CacheContext $cacheContext,
\Magento\Framework\Event\ManagerInterface $eventManager
\Magento\Framework\Event\ManagerInterface $eventManager,
MetadataPool $metadataPool = null
) {
$this->_resource = $resource;
$this->_indexerFactory = $indexerFactory;
$this->_catalogProductType = $catalogProductType;
$this->cacheContext = $cacheContext;
$this->eventManager = $eventManager;
$this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class);
}

/**
Expand Down Expand Up @@ -154,10 +163,15 @@ protected function _getTable($entityName)
public function getRelationsByChild($childIds)
{
$connection = $this->_getConnection();
$select = $connection->select()
->from($this->_getTable('catalog_product_relation'), 'parent_id')
->where('child_id IN(?)', $childIds);

$linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
->getLinkField();
$select = $connection->select()->from(
['cpe' => $this->_getTable('catalog_product_entity')],
'entity_id'
)->join(
['relation' => $this->_getTable('catalog_product_relation')],
'relation.parent_id = cpe.' . $linkField
)->where('child_id IN(?)', $childIds);
return $connection->fetchCol($select);
}

Expand Down Expand Up @@ -230,7 +244,8 @@ protected function _reindexRows($productIds = [])
if (!is_array($productIds)) {
$productIds = [$productIds];
}

$parentIds = $this->getRelationsByChild($productIds);
$productIds = $parentIds ? array_unique(array_merge($parentIds, $productIds)) : $productIds;
$this->getCacheCleaner()->clean($productIds, function () use ($productIds) {
$this->doReindex($productIds);
});
Expand All @@ -248,13 +263,10 @@ private function doReindex($productIds = [])
{
$connection = $this->_getConnection();

$parentIds = $this->getRelationsByChild($productIds);
$processIds = $parentIds ? array_merge($parentIds, $productIds) : $productIds;

// retrieve product types by processIds
$select = $connection->select()
->from($this->_getTable('catalog_product_entity'), ['entity_id', 'type_id'])
->where('entity_id IN(?)', $processIds);
->where('entity_id IN(?)', $productIds);
$pairs = $connection->fetchPairs($select);

$byType = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\CatalogInventory\Model\Plugin;

use Magento\Catalog\Model\Product\Action as ProductAction;

/**
* Plugin for Magento\Catalog\Model\Product\Action
*/
class ReindexUpdatedProducts
{
/**
* @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor
*/
private $indexerProcessor;

/**
* @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor
*/
public function __construct(\Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor)
{
$this->indexerProcessor = $indexerProcessor;
}

/**
* Reindex on product attribute mass change
*
* @param ProductAction $subject
* @param ProductAction $action
* @param array $productIds
* @return ProductAction
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function afterUpdateAttributes(
ProductAction $subject,
ProductAction $action,
$productIds
) {
$this->indexerProcessor->reindexList(array_unique($productIds));
return $action;
}
}
8 changes: 8 additions & 0 deletions app/code/Magento/CatalogInventory/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@
<type name="Magento\Catalog\Model\Product">
<plugin name="catalogInventoryAfterLoad" type="Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/>
</type>
<type name="Magento\Catalog\Model\Product\Action">
<plugin name="ReindexUpdatedProducts" type="Magento\CatalogInventory\Model\Plugin\ReindexUpdatedProducts"/>
</type>
<type name="Magento\CatalogInventory\Setup\UpgradeData">
<arguments>
<argument name="indexerProcessor" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\Processor</argument>
</arguments>
</type>
<type name="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor">
<arguments>
<argument name="baseSelectProcessors" xsi:type="array">
Expand Down
130 changes: 101 additions & 29 deletions app/code/Magento/Search/Model/SynonymAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);
namespace Magento\Search\Model;

use Magento\Search\Api\SynonymAnalyzerInterface;

/**
* SynonymAnalyzer responsible for search of synonyms matching a word or a phrase.
*/
class SynonymAnalyzer implements SynonymAnalyzerInterface
{
/**
Expand Down Expand Up @@ -39,58 +44,125 @@ public function __construct(SynonymReader $synReader)
* ]
* @param string $phrase
* @return array
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getSynonymsForPhrase($phrase)
{
$synGroups = [];
$result = [];

if (empty($phrase)) {
return $synGroups;
if (empty(trim($phrase))) {
return $result;
}

$rows = $this->synReaderModel->loadByPhrase($phrase)->getData();
$synonyms = [];
foreach ($rows as $row) {
$synonyms [] = $row['synonyms'];
}
$synonymGroups = $this->getSynonymGroupsByPhrase($phrase);

// Replace multiple spaces in a row with the only one space
$phrase = preg_replace("/ {2,}/", " ", $phrase);

// Go through every returned record looking for presence of the actual phrase. If there were no matching
// records found in DB then create a new entry for it in the returned array
$words = explode(' ', $phrase);
foreach ($words as $w) {
$position = $this->findInArray($w, $synonyms);
if ($position !== false) {
$synGroups[] = explode(',', $synonyms[$position]);
} else {
// No synonyms were found. Return the original word in this position
$synGroups[] = [$w];

foreach ($words as $offset => $word) {
$synonyms = [$word];

if ($synonymGroups) {
$pattern = $this->getSearchPattern(\array_slice($words, $offset));
$position = $this->findInArray($pattern, $synonymGroups);
if ($position !== null) {
$synonyms = explode(',', $synonymGroups[$position]);
}
}

$result[] = $synonyms;
}
return $synGroups;

return $result;
}

/**
* Helper method to find the presence of $word in $wordsArray. If found, the particular array index is returned.
* Helper method to find the matching of $pattern to $synonymGroupsToExamine.
* If matches, the particular array index is returned.
* Otherwise false will be returned.
*
* @param string $word
* @param $array $wordsArray
* @return boolean | int
* @param string $pattern
* @param array $synonymGroupsToExamine
* @return int|null
*/
private function findInArray($word, $wordsArray)
private function findInArray(string $pattern, array $synonymGroupsToExamine)
{
if (empty($wordsArray)) {
return false;
}
$position = 0;
foreach ($wordsArray as $wordsLine) {
$pattern = '/^' . $word . ',|,' . $word . ',|,' . $word . '$/';
$rv = preg_match($pattern, $wordsLine);
if ($rv != 0) {
foreach ($synonymGroupsToExamine as $synonymGroup) {
$matchingResultCode = preg_match($pattern, $synonymGroup);
if ($matchingResultCode === 1) {
return $position;
}
$position++;
}
return false;
return null;
}

/**
* Returns a regular expression to search for synonyms of the phrase represented as the list of words.
*
* Returned pattern contains expression to search for a part of the phrase from the beginning.
*
* For example, in the phrase "Elizabeth is the English queen" with subset from the very first word,
* the method will build an expression which looking for synonyms for all these patterns:
* - Elizabeth is the English queen
* - Elizabeth is the English
* - Elizabeth is the
* - Elizabeth is
* - Elizabeth
*
* For the same phrase on the second iteration with the first word "is" it will match for these synonyms:
* - is the English queen
* - is the English
* - is the
* - is
*
* The pattern looking for exact match and will not find these phrases as synonyms:
* - Is there anybody in the room?
* - Is the English is most popular language?
* - Is the English queen Elizabeth?
*
* Take into account that returned pattern expects that data will be represented as comma-separated value.
*
* @param array $words
* @return string
*/
private function getSearchPattern(array $words): string
{
$patterns = [];
for ($lastItem = count($words); $lastItem > 0; $lastItem--) {
$phrase = implode("\s+", \array_slice($words, 0, $lastItem));
$patterns[] = '^' . $phrase . ',';
$patterns[] = ',' . $phrase . ',';
$patterns[] = ',' . $phrase . '$';
}

$pattern = '/' . implode('|', $patterns) . '/i';
return $pattern;
}

/**
* Get all synonym groups for the phrase
*
* Returns an array of synonyms which are represented as comma-separated value for each item in the list
*
* @param string $phrase
* @return string[]
* @throws \Magento\Framework\Exception\LocalizedException
*/
private function getSynonymGroupsByPhrase(string $phrase): array
{
$result = [];

/** @var array $synonymGroups */
$synonymGroups = $this->synReaderModel->loadByPhrase($phrase)->getData();
foreach ($synonymGroups as $row) {
$result[] = $row['synonyms'];
}
return $result;
}
}
1 change: 1 addition & 0 deletions app/code/Magento/Search/Model/SynonymReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ protected function _construct()
*
* @param string $phrase
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
* @since 100.1.0
*/
public function loadByPhrase($phrase)
Expand Down
Loading

0 comments on commit 8f8caf3

Please sign in to comment.