Skip to content

Commit 11cbaa1

Browse files
authored
Merge pull request #8 from ensi-platform/task-96329-2
#96329 Match and multi match filters options
2 parents 3f39041 + 257e58c commit 11cbaa1

File tree

7 files changed

+99
-16
lines changed

7 files changed

+99
-16
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ AllowedFilter::greater('name', 'field'); // The field value must be great
135135
AllowedFilter::greaterOrEqual('name', 'field'); // The field value must be greater than or equal to the specified one.
136136
AllowedFilter::less('name', 'field'); // The field value must be less than the specified one.
137137
AllowedFilter::lessOrEqual('name', 'field'); // The field value must be less than or equal to the specified one.
138+
AllowedFilter::match('name', 'field'); // Full text search in the field
139+
AllowedFilter::multiMatch('name', ['field1^3', 'field2']); // Full text search in the fields
138140
```
139141

140142
The sorts available to the client are added by the `allowedSorts` method. The sorting direction is set in its name.

composer.lock

Lines changed: 10 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Filtering/AllowedFilter.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Ensi\LaravelElasticQuerySpecification\Filtering;
44

55
use Ensi\LaravelElasticQuery\Contracts\BoolQuery;
6+
use Ensi\LaravelElasticQuery\Contracts\MatchOptions;
7+
use Ensi\LaravelElasticQuery\Contracts\MultiMatchOptions;
68
use Ensi\LaravelElasticQuerySpecification\Contracts\Constraint;
79
use Ensi\LaravelElasticQuerySpecification\Contracts\FilterAction;
810
use Webmozart\Assert\Assert;
@@ -117,9 +119,18 @@ public static function lessOrEqual(string $name, ?string $field = null): self
117119
return new static($name, new RangeFilterAction('<='), $field);
118120
}
119121

120-
public static function match(string $name, ?string $field): self
122+
public static function match(string $name, ?string $field, ?MatchOptions $options = null): self
121123
{
122-
return new static($name, new MatchFilterAction(), $field);
124+
return new static($name, new MatchFilterAction($options ?? new MatchOptions()), $field);
125+
}
126+
127+
public static function multiMatch(string $name, ?array $fields = null, ?MultiMatchOptions $options = null): self
128+
{
129+
return new static(
130+
$name,
131+
new MultiMatchFilterAction($options ?? new MultiMatchOptions()),
132+
MultiMatchFilterAction::encodeFields($fields)
133+
);
123134
}
124135

125136
private function refineValue(mixed $value): mixed

src/Filtering/MatchFilterAction.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@
33
namespace Ensi\LaravelElasticQuerySpecification\Filtering;
44

55
use Ensi\LaravelElasticQuery\Contracts\BoolQuery;
6+
use Ensi\LaravelElasticQuery\Contracts\MatchOptions;
67
use Ensi\LaravelElasticQuerySpecification\Contracts\FilterAction;
78
use Ensi\LaravelElasticQuerySpecification\Exceptions\InvalidQueryException;
89

910
class MatchFilterAction implements FilterAction
1011
{
12+
public function __construct(private MatchOptions $options)
13+
{
14+
}
15+
1116
public function __invoke(BoolQuery $query, mixed $value, string $field): void
1217
{
1318
FilterValue::make($value)
14-
->whenSingle(fn (mixed $value) => $query->whereMatch($field, (string)$value))
19+
->whenSingle(fn (mixed $value) => $query->whereMatch($field, (string)$value, $this->options))
1520
->whenMultiple(fn () => throw InvalidQueryException::notSupportMultipleValues($field));
1621
}
1722
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Ensi\LaravelElasticQuerySpecification\Filtering;
4+
5+
use Ensi\LaravelElasticQuery\Contracts\BoolQuery;
6+
use Ensi\LaravelElasticQuery\Contracts\MultiMatchOptions;
7+
use Ensi\LaravelElasticQuerySpecification\Contracts\FilterAction;
8+
use Ensi\LaravelElasticQuerySpecification\Exceptions\InvalidQueryException;
9+
10+
class MultiMatchFilterAction implements FilterAction
11+
{
12+
public function __construct(private MultiMatchOptions $options)
13+
{
14+
}
15+
16+
public function __invoke(BoolQuery $query, mixed $value, string $field): void
17+
{
18+
$fields = explode(',', $field);
19+
20+
FilterValue::make($value)
21+
->whenSingle(fn (mixed $value) => $query->whereMultiMatch($fields, (string)$value, $this->options))
22+
->whenMultiple(fn () => throw InvalidQueryException::notSupportMultipleValues($field));
23+
}
24+
25+
public static function encodeFields(?array $fields): string
26+
{
27+
return implode(',', $fields ?: ['*']);
28+
}
29+
}

tests/Data/fixtures/products.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"name": "Gloves VITTOVAR",
8282
"code": "perchatki-s-zashchitoy",
8383
"rating": 3,
84-
"description": "VITTOVAR description",
84+
"description": "VITTOVAR leather description",
8585
"tags": ["clothes", "gloves"],
8686
"offers": [
8787
]

tests/Integration/FilteringTest.php

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
use Ensi\LaravelElasticQuery\Contracts\MatchOptions;
4+
use Ensi\LaravelElasticQuery\Contracts\MultiMatchOptions;
35
use Ensi\LaravelElasticQuerySpecification\Exceptions\InvalidQueryException;
46
use Ensi\LaravelElasticQuerySpecification\Filtering\AllowedFilter;
57
use Ensi\LaravelElasticQuerySpecification\Specification\CompositeSpecification;
@@ -79,14 +81,46 @@
7981
'between' => [['rating__lte' => 7, 'rating__gte' => 5], [1, 328]],
8082
]);
8183

82-
test('match filter', function (string $query, array $expectedIds) {
84+
test('match filter', function (string $query, ?MatchOptions $options, array $expectedIds) {
8385
$spec = CompositeSpecification::new()
8486
->allowedFilters([
85-
AllowedFilter::match('name', 'search_name'),
87+
AllowedFilter::match('name', 'search_name', $options),
8688
]);
8789

8890
searchQuery($spec, ['filter' => ['name' => $query]])->assertDocumentIds($expectedIds);
8991
})->with([
90-
'single' => ['water', [150]],
91-
'multiple' => ['gloves', [319, 471]],
92+
'single result' => ['water', null, [150]],
93+
'multiple results' => ['gloves', null, [319, 471]],
94+
'with options' => ['woter', MatchOptions::make(fuzziness: 'AUTO'), [150]],
9295
]);
96+
97+
test('multi match filter', function (string $query, ?MultiMatchOptions $options, array $expectedIds) {
98+
$spec = CompositeSpecification::new()
99+
->allowedFilters([
100+
AllowedFilter::multiMatch('name', ['search_name', 'description'], $options),
101+
]);
102+
103+
searchQuery($spec, ['filter' => ['name' => $query]])->assertDocumentIds($expectedIds);
104+
})->with([
105+
'single result' => ['water', null, [150]],
106+
'multiple results' => ['gloves', null, [319, 471]],
107+
'with options' => ['woter', MultiMatchOptions::make(fuzziness: 'AUTO'), [150]],
108+
]);
109+
110+
test('multi match filter priority', function () {
111+
$spec = CompositeSpecification::new()
112+
->allowedFilters([
113+
AllowedFilter::multiMatch('name', ['search_name^3', 'description']),
114+
]);
115+
116+
searchQuery($spec, ['filter' => ['name' => 'leather']])->assertDocumentOrder([319, 471]);
117+
});
118+
119+
test('multi match filter without fields', function () {
120+
$spec = CompositeSpecification::new()
121+
->allowedFilters([
122+
AllowedFilter::multiMatch('name'),
123+
]);
124+
125+
searchQuery($spec, ['filter' => ['name' => 'leather']])->assertDocumentOrder([319, 471]);
126+
});

0 commit comments

Comments
 (0)