Skip to content

Commit bd49195

Browse files
committed
ACP2E-989: "Minimum should match" with enquoted search term causes incorrect search query generation
1 parent cd826aa commit bd49195

File tree

2 files changed

+62
-74
lines changed

2 files changed

+62
-74
lines changed

app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/MatchQuery.php

Lines changed: 50 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -70,51 +70,6 @@ public function __construct(
7070
$this->config = $config;
7171
}
7272

73-
/**
74-
* @inheritdoc
75-
*/
76-
public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType)
77-
{
78-
$queryValue = $this->prepareQuery($requestQuery->getValue(), $conditionType);
79-
$queries = $this->buildQueries($requestQuery->getMatches(), $queryValue);
80-
$requestQueryBoost = $requestQuery->getBoost() ?: 1;
81-
$minimumShouldMatch = $this->config->getElasticsearchConfigData('minimum_should_match');
82-
83-
foreach ($queries as $query) {
84-
$queryBody = $query['body'];
85-
$matchKey = array_keys($queryBody)[0];
86-
foreach ($queryBody[$matchKey] as $field => $matchQuery) {
87-
$matchQuery['boost'] = $requestQueryBoost + $matchQuery['boost'];
88-
if ($minimumShouldMatch && $matchKey != 'match_phrase_prefix') {
89-
$matchQuery['minimum_should_match'] = $minimumShouldMatch;
90-
}
91-
$queryBody[$matchKey][$field] = $matchQuery;
92-
}
93-
$selectQuery['bool'][$query['condition']][] = $queryBody;
94-
}
95-
96-
return $selectQuery;
97-
}
98-
99-
/**
100-
* Prepare query
101-
*
102-
* @param string $queryValue
103-
* @param string $conditionType
104-
* @return array
105-
*/
106-
private function prepareQuery(string $queryValue, string $conditionType): array
107-
{
108-
$condition = $conditionType === BoolExpression::QUERY_CONDITION_NOT
109-
? self::QUERY_CONDITION_MUST_NOT
110-
: $conditionType;
111-
112-
return [
113-
'condition' => $condition,
114-
'value' => $queryValue,
115-
];
116-
}
117-
11873
/**
11974
* Creates valid ElasticSearch search conditions from Match queries
12075
*
@@ -125,21 +80,24 @@ private function prepareQuery(string $queryValue, string $conditionType): array
12580
* The search query boost is an optional in the search query and therefore it will be set to 1 by default
12681
* if none passed with a match query.
12782
*
128-
* @param array $matches
129-
* @param array $queryValue
83+
* @param array $selectQuery
84+
* @param RequestQueryInterface $requestQuery
85+
* @param string $conditionType
13086
* @return array
13187
*/
132-
private function buildQueries(array $matches, array $queryValue): array
88+
public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType)
13389
{
134-
$conditions = [];
90+
$queryValue = $this->prepareQuery($requestQuery->getValue(), $conditionType);
91+
$requestQueryBoost = $requestQuery->getBoost() ?: 1;
92+
$minimumShouldMatch = $this->config->getElasticsearchConfigData('minimum_should_match');
13593

13694
// Checking for quoted phrase \"phrase test\", trim escaped surrounding quotes if found
13795
$count = 0;
13896
$value = preg_replace('#^"(.*)"$#m', '$1', $queryValue['value'], -1, $count);
13997
$condition = ($count) ? 'match_phrase' : 'match';
14098
$transformedTypes = [];
14199

142-
foreach ($matches as $match) {
100+
foreach ($requestQuery->getMatches() as $match) {
143101
$resolvedField = $this->fieldMapper->getFieldName(
144102
$match['field'],
145103
['type' => FieldMapperInterface::TYPE_QUERY]
@@ -153,29 +111,62 @@ private function buildQueries(array $matches, array $queryValue): array
153111
$transformedTypes[$valueTransformerHash] = $valueTransformer->transform($value);
154112
}
155113
$transformedValue = $transformedTypes[$valueTransformerHash];
156-
157114
if (null === $transformedValue) {
158115
//Value is incompatible with this field type.
159116
continue;
160117
}
118+
161119
$matchCondition = $match['matchCondition'] ?? $condition;
162120
$fields = [];
163121
$fields[$resolvedField] = [
164122
'query' => $transformedValue,
165-
'boost' => $match['boost'] ?? 1,
123+
'boost' => $requestQueryBoost + $match['boost'] ?? 1,
166124
];
167125

168126
if (isset($match['analyzer'])) {
169127
$fields[$resolvedField]['analyzer'] = $match['analyzer'];
170128
}
171-
$conditions[] = [
172-
'condition' => $queryValue['condition'],
173-
'body' => [
174-
$matchCondition => $fields,
175-
],
176-
];
129+
130+
if ($minimumShouldMatch && $this->isConditionSupportMinimumShouldMatch($matchCondition)) {
131+
$fields[$resolvedField]['minimum_should_match'] = $minimumShouldMatch;
132+
}
133+
134+
$selectQuery['bool'][$queryValue['condition']][] = [$matchCondition => $fields];
177135
}
178136

179-
return $conditions;
137+
return $selectQuery;
138+
}
139+
140+
/**
141+
* Prepare query
142+
*
143+
* @param string $queryValue
144+
* @param string $conditionType
145+
* @return array
146+
*/
147+
private function prepareQuery(string $queryValue, string $conditionType): array
148+
{
149+
$condition = $conditionType === BoolExpression::QUERY_CONDITION_NOT
150+
? self::QUERY_CONDITION_MUST_NOT
151+
: $conditionType;
152+
153+
return [
154+
'condition' => $condition,
155+
'value' => $queryValue,
156+
];
157+
}
158+
159+
/**
160+
* Check does condition support the minimum_should_match field
161+
*
162+
* @param string $condition
163+
* @return bool
164+
*/
165+
private function isConditionSupportMinimumShouldMatch(string $condition): bool
166+
{
167+
return !in_array($condition, [
168+
'match_phrase_prefix',
169+
'match_phrase',
170+
]);
180171
}
181172
}

app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchQueryTest.php

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface;
1717
use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool;
1818
use Magento\Framework\Search\Request\Query\MatchQuery;
19-
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
2019
use PHPUnit\Framework\MockObject\MockObject;
2120
use PHPUnit\Framework\TestCase;
2221

@@ -39,12 +38,14 @@ class MatchQueryTest extends TestCase
3938
* @var MatchQueryBuilder
4039
*/
4140
private $matchQueryBuilder;
41+
4242
/**
43-
* @var MockObject
43+
* @var Config|MockObject
4444
*/
4545
private $config;
46+
4647
/**
47-
* @var MockObject
48+
* @var FieldMapperInterface|MockObject
4849
*/
4950
private $fieldMapper;
5051

@@ -65,16 +66,13 @@ protected function setUp(): void
6566
->willReturn($valueTransformerMock);
6667
$valueTransformerMock->method('transform')
6768
->willReturnArgument(0);
68-
$this->matchQueryBuilder = (new ObjectManager($this))->getObject(
69-
MatchQueryBuilder::class,
70-
[
71-
'fieldMapper' => $this->fieldMapper,
72-
'preprocessorContainer' => [],
73-
'attributeProvider' => $this->attributeProvider,
74-
'fieldTypeResolver' => $this->fieldTypeResolver,
75-
'valueTransformerPool' => $valueTransformerPoolMock,
76-
'config' => $this->config,
77-
]
69+
70+
$this->matchQueryBuilder = new MatchQueryBuilder(
71+
$this->fieldMapper,
72+
$this->attributeProvider,
73+
$this->fieldTypeResolver,
74+
$valueTransformerPoolMock,
75+
$this->config
7876
);
7977
}
8078

@@ -182,6 +180,7 @@ public function buildDataProvider(): array
182180
],
183181
'2<75%'
184182
],
183+
//[match_phrase] query does not support [minimum_should_match]
185184
'match_phrase query with minimum_should_match' => [
186185
'"fitness bottle"',
187186
[
@@ -196,14 +195,12 @@ public function buildDataProvider(): array
196195
'name' => [
197196
'query' => 'fitness bottle',
198197
'boost' => 6,
199-
'minimum_should_match' => '2<75%',
200198
],
201199
],
202200
],
203201
],
204202
'2<75%'
205203
],
206-
207204
];
208205
}
209206

0 commit comments

Comments
 (0)