Skip to content

Commit d398e31

Browse files
authored
Merge pull request #104 from QwertyRomanRus/idbpr-3454-v7
add sort by script in aggregation
2 parents 74aff7a + 0065f3e commit d398e31

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

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

0 commit comments

Comments
 (0)