diff --git a/src/Aggregating/Metrics/ScriptAggregation.php b/src/Aggregating/Metrics/ScriptAggregation.php new file mode 100644 index 0000000..479a8e8 --- /dev/null +++ b/src/Aggregating/Metrics/ScriptAggregation.php @@ -0,0 +1,54 @@ +name; + } + + public function parseResults(array $response): array + { + return [$this->name => Result::parseValue($response[$this->name]) ?? 0]; + } + + public function toDSL(): array + { + $script = [ + 'source' => $this->source, + 'lang' => $this->lang, + ]; + + if (!empty($this->params)) { + $script['params'] = $this->params; + } + + return [ + $this->name => [ + $this->aggregationType => [ + 'script' => $script, + ], + ], + ]; + } +} diff --git a/src/Concerns/ConstructsAggregations.php b/src/Concerns/ConstructsAggregations.php index 5913d2d..afd7499 100644 --- a/src/Concerns/ConstructsAggregations.php +++ b/src/Concerns/ConstructsAggregations.php @@ -15,9 +15,11 @@ use Ensi\LaravelElasticQuery\Aggregating\Metrics\MinAggregation; use Ensi\LaravelElasticQuery\Aggregating\Metrics\MinMaxAggregation; use Ensi\LaravelElasticQuery\Aggregating\Metrics\RangesAggregation; +use Ensi\LaravelElasticQuery\Aggregating\Metrics\ScriptAggregation; use Ensi\LaravelElasticQuery\Aggregating\Metrics\ValueCountAggregation; use Ensi\LaravelElasticQuery\Contracts\Aggregation; use Ensi\LaravelElasticQuery\Contracts\Criteria; +use Ensi\LaravelElasticQuery\Contracts\ScriptLang; use Ensi\LaravelElasticQuery\Filtering\BoolQueryBuilder; use Ensi\LaravelElasticQuery\Search\Sorting\Sort; use Ensi\LaravelElasticQuery\Search\Sorting\SortCollection; @@ -81,6 +83,18 @@ public function max(string $name, string $field, mixed $missing = null): static return $this; } + public function script( + string $name, + string $aggregationType, + string $source, + array $params = [], + string $lang = ScriptLang::PAINLESS + ): static { + $this->aggregations->add(new ScriptAggregation($name, $aggregationType, $source, $params, $lang)); + + return $this; + } + public function count(string $name, string $field): static { $this->aggregations->add(new ValueCountAggregation($name, $this->absolutePath($field))); diff --git a/src/Contracts/AggregationsBuilder.php b/src/Contracts/AggregationsBuilder.php index 0b614f6..b93c878 100644 --- a/src/Contracts/AggregationsBuilder.php +++ b/src/Contracts/AggregationsBuilder.php @@ -25,5 +25,7 @@ public function max(string $name, string $field, mixed $missing = null): static; public function count(string $path, string $field): static; + public function script(string $name, string $aggregationType, string $source, array $params = [], string $lang = ScriptLang::PAINLESS): static; + public function nested(string $path, Closure $callback): static; } diff --git a/tests/IntegrationTests/AggregationQueryIntegrationTest.php b/tests/IntegrationTests/AggregationQueryIntegrationTest.php index a465dfb..d9869c7 100644 --- a/tests/IntegrationTests/AggregationQueryIntegrationTest.php +++ b/tests/IntegrationTests/AggregationQueryIntegrationTest.php @@ -1,8 +1,10 @@ with([null, 'default_bucket']); + +test('aggregation query by script', function () { + /** @var IntegrationTestCase $this */ + $fieldName = 'max_tag_by_script'; + + $sort = new SortCollection(); + $sort->add(new Sort($fieldName)); + + $termAggregation = new AggregationCollection(); + $termAggregation->add(new ScriptAggregation( + name: $fieldName, + aggregationType: 'max', + params: ['tag' => 'video'], + source: ' + if (doc.containsKey("tags") + && doc["tags"].size() > 0 + && doc["tags"].contains(params.tag) + ) { + return 0; + } + return 1; + ', + )); + + $results = ProductsIndex::aggregate() + ->terms( + name: 'group_by', + field: 'product_id', + size: 3, + sort: $sort, + composite: $termAggregation + ) + ->get() + ->get('group_by'); + + $scores = $results->map( + fn (Bucket $bucket) => $bucket->getCompositeValue($fieldName) + )->toArray(); + + assertEqualsCanonicalizing( + [1, 328, 150], + $results->pluck('key')->toArray(), + ); + + assertEqualsCanonicalizing( + [0.0, 0.0, 1.0], + $scores, + ); +});