Skip to content

Commit 2520c17

Browse files
authored
Merge pull request #101 from QwertyRomanRus/idbpr-3454-v8
idbpr-3454 add sort by script in aggregation
2 parents bf2ffff + 4f63ace commit 2520c17

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Ensi\LaravelElasticQuery\Aggregating\Metrics;
4+
5+
use Ensi\LaravelElasticQuery\Aggregating\Result;
6+
use Ensi\LaravelElasticQuery\Contracts\Aggregation;
7+
use Ensi\LaravelElasticQuery\Contracts\ScriptLang;
8+
use Webmozart\Assert\Assert;
9+
10+
class ScriptAggregation implements Aggregation
11+
{
12+
public function __construct(
13+
private readonly string $name,
14+
private readonly string $aggregationType,
15+
private readonly string $source,
16+
private readonly array $params = [],
17+
private readonly string $lang = ScriptLang::PAINLESS,
18+
) {
19+
Assert::stringNotEmpty(trim($name));
20+
Assert::stringNotEmpty(trim($aggregationType));
21+
Assert::stringNotEmpty(trim($source));
22+
Assert::oneOf($lang, ScriptLang::cases());
23+
}
24+
25+
public function name(): string
26+
{
27+
return $this->name;
28+
}
29+
30+
public function parseResults(array $response): array
31+
{
32+
return [$this->name => Result::parseValue($response[$this->name]) ?? 0];
33+
}
34+
35+
public function toDSL(): array
36+
{
37+
$script = [
38+
'source' => $this->source,
39+
'lang' => $this->lang,
40+
];
41+
42+
if (!empty($this->params)) {
43+
$script['params'] = $this->params;
44+
}
45+
46+
return [
47+
$this->name => [
48+
$this->aggregationType => [
49+
'script' => $script,
50+
],
51+
],
52+
];
53+
}
54+
}

src/Concerns/ConstructsAggregations.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
use Ensi\LaravelElasticQuery\Aggregating\Metrics\MinAggregation;
1616
use Ensi\LaravelElasticQuery\Aggregating\Metrics\MinMaxAggregation;
1717
use Ensi\LaravelElasticQuery\Aggregating\Metrics\RangesAggregation;
18+
use Ensi\LaravelElasticQuery\Aggregating\Metrics\ScriptAggregation;
1819
use Ensi\LaravelElasticQuery\Aggregating\Metrics\ValueCountAggregation;
1920
use Ensi\LaravelElasticQuery\Contracts\Aggregation;
2021
use Ensi\LaravelElasticQuery\Contracts\Criteria;
22+
use Ensi\LaravelElasticQuery\Contracts\ScriptLang;
2123
use Ensi\LaravelElasticQuery\Filtering\BoolQueryBuilder;
2224
use Ensi\LaravelElasticQuery\Search\Sorting\Sort;
2325
use Ensi\LaravelElasticQuery\Search\Sorting\SortCollection;
@@ -81,6 +83,18 @@ public function max(string $name, string $field, mixed $missing = null): static
8183
return $this;
8284
}
8385

86+
public function script(
87+
string $name,
88+
string $aggregationType,
89+
string $source,
90+
array $params = [],
91+
string $lang = ScriptLang::PAINLESS
92+
): static {
93+
$this->aggregations->add(new ScriptAggregation($name, $aggregationType, $source, $params, $lang));
94+
95+
return $this;
96+
}
97+
8498
public function count(string $name, string $field): static
8599
{
86100
$this->aggregations->add(new ValueCountAggregation($name, $this->absolutePath($field)));

src/Contracts/AggregationsBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@ public function max(string $name, string $field, mixed $missing = null): static;
2525

2626
public function count(string $path, string $field): static;
2727

28+
public function script(string $name, string $aggregationType, string $source, array $params = [], string $lang = ScriptLang::PAINLESS): static;
29+
2830
public function nested(string $path, Closure $callback): static;
2931
}

tests/IntegrationTests/AggregationQueryIntegrationTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
<?php
22

3+
use Ensi\LaravelElasticQuery\Aggregating\AggregationCollection;
34
use Ensi\LaravelElasticQuery\Aggregating\Bucket;
45
use Ensi\LaravelElasticQuery\Aggregating\FiltersCollection;
56
use Ensi\LaravelElasticQuery\Aggregating\Metrics\MinMaxScoreAggregation;
7+
use Ensi\LaravelElasticQuery\Aggregating\Metrics\ScriptAggregation;
68
use Ensi\LaravelElasticQuery\Aggregating\Metrics\TopHitsAggregation;
79
use Ensi\LaravelElasticQuery\Aggregating\MinMax;
810
use Ensi\LaravelElasticQuery\Aggregating\Range;
911
use Ensi\LaravelElasticQuery\Contracts\AggregationsBuilder;
1012
use Ensi\LaravelElasticQuery\Filtering\Criterias\RangeBound;
1113
use Ensi\LaravelElasticQuery\Filtering\Criterias\Term;
1214
use Ensi\LaravelElasticQuery\Search\Sorting\Sort;
15+
use Ensi\LaravelElasticQuery\Search\Sorting\SortCollection;
1316
use Ensi\LaravelElasticQuery\Tests\Data\Models\ProductsIndex;
1417
use Ensi\LaravelElasticQuery\Tests\IntegrationTestCase;
1518

@@ -210,3 +213,52 @@
210213
);
211214
}
212215
})->with([null, 'default_bucket']);
216+
217+
test('aggregation query by script', function () {
218+
/** @var IntegrationTestCase $this */
219+
$fieldName = 'max_tag_by_script';
220+
221+
$sort = new SortCollection();
222+
$sort->add(new Sort($fieldName));
223+
224+
$termAggregation = new AggregationCollection();
225+
$termAggregation->add(new ScriptAggregation(
226+
name: $fieldName,
227+
aggregationType: 'max',
228+
params: ['tag' => 'video'],
229+
source: '
230+
if (doc.containsKey("tags")
231+
&& doc["tags"].size() > 0
232+
&& doc["tags"].contains(params.tag)
233+
) {
234+
return 0;
235+
}
236+
return 1;
237+
',
238+
));
239+
240+
$results = ProductsIndex::aggregate()
241+
->terms(
242+
name: 'group_by',
243+
field: 'product_id',
244+
size: 3,
245+
sort: $sort,
246+
composite: $termAggregation
247+
)
248+
->get()
249+
->get('group_by');
250+
251+
$scores = $results->map(
252+
fn (Bucket $bucket) => $bucket->getCompositeValue($fieldName)
253+
)->toArray();
254+
255+
assertEqualsCanonicalizing(
256+
[1, 328, 150],
257+
$results->pluck('key')->toArray(),
258+
);
259+
260+
assertEqualsCanonicalizing(
261+
[0.0, 0.0, 1.0],
262+
$scores,
263+
);
264+
});

0 commit comments

Comments
 (0)