Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.

Commit a630b2b

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-88082' into forwardports-pr
2 parents 7d40088 + db75c60 commit a630b2b

File tree

4 files changed

+143
-29
lines changed

4 files changed

+143
-29
lines changed

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)

dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,38 @@ public static function loadGetSynonymsForPhraseDataProvider()
4242
'phrase' => 'universe is enormous',
4343
'expectedResult' => [['universe', 'cosmos'], ['is'], ['big', 'huge', 'large', 'enormous']]
4444
],
45+
'WithCaseMismatch' => [
46+
'phrase' => 'GNU\'s Not Unix',
47+
'expectedResult' => [['GNU\'s'], ['Not'], ['unix', 'linux'],]
48+
],
49+
'WithMultiWordPhrase' => [
50+
'phrase' => 'Coastline of Great Britain stretches for 11,073 miles',
51+
'expectedResult' => [
52+
['Coastline'],
53+
['of'],
54+
['Great Britain', 'United Kingdom'],
55+
['Britain'],
56+
['stretches'],
57+
['for'],
58+
['11,073'],
59+
['miles']
60+
]
61+
],
62+
'PartialSynonymMatching' => [
63+
'phrase' => 'Magento Engineering',
64+
'expectedResult' => [
65+
['orange', 'magento'],
66+
['Engineering', 'Technical Staff']
67+
]
68+
],
4569
'noSynonyms' => [
4670
'phrase' => 'this sentence has no synonyms',
4771
'expectedResult' => [['this'], ['sentence'], ['has'], ['no'], ['synonyms']]
4872
],
73+
'multipleSpaces' => [
74+
'phrase' => 'GNU\'s Not Unix',
75+
'expectedResult' => [['GNU\'s'], ['Not'], ['unix', 'linux'],]
76+
],
4977
'oneMoreTest' => [
5078
'phrase' => 'schlicht',
5179
'expectedResult' => [['schlicht', 'natürlich']]

dev/tests/integration/testsuite/Magento/Search/_files/synonym_reader.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,22 @@
2424
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
2525
$synonymsModel->setSynonyms('hill,mountain,peak')->setWebsiteId(1)->save();
2626

27+
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
28+
$synonymsModel->setSynonyms('Community Engineering,Contributors,Magento Community Engineering')->setWebsiteId(1)
29+
->save();
30+
31+
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
32+
$synonymsModel->setSynonyms('Engineering,Technical Staff')->setWebsiteId(1)->save();
33+
2734
// Synonym groups for "All Store Views"
2835
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
2936
$synonymsModel->setSynonyms('universe,cosmos')->setWebsiteId(0)->save();
3037

38+
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
39+
$synonymsModel->setSynonyms('unix,linux')->setWebsiteId(0)->save();
40+
41+
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
42+
$synonymsModel->setSynonyms('Great Britain,United Kingdom')->setWebsiteId(0)->save();
43+
3144
$synonymsModel = $objectManager->create(\Magento\Search\Model\SynonymReader::class);
3245
$synonymsModel->setSynonyms('big,huge,large,enormous')->setWebsiteId(0)->save();

0 commit comments

Comments
 (0)