-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #97 from kununu/composite-aggregations
Add Composite Aggregations Builder and Repository
- Loading branch information
Showing
18 changed files
with
718 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Exception; | ||
|
||
use RuntimeException; | ||
|
||
final class MissingAggregationAttributesException extends RuntimeException | ||
{ | ||
} |
105 changes: 105 additions & 0 deletions
105
src/Query/Aggregation/Builder/CompositeAggregationQueryBuilder.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Query\Aggregation\Builder; | ||
|
||
use Kununu\Elasticsearch\Exception\MissingAggregationAttributesException; | ||
use Kununu\Elasticsearch\Query\Aggregation\SourceProperty; | ||
use Kununu\Elasticsearch\Query\Aggregation\Sources; | ||
use Kununu\Elasticsearch\Query\CompositeAggregationQueryInterface; | ||
use Kununu\Elasticsearch\Query\Criteria\Filter; | ||
use Kununu\Elasticsearch\Query\Criteria\Filters; | ||
use Kununu\Elasticsearch\Query\QueryInterface; | ||
use Kununu\Elasticsearch\Query\RawQuery; | ||
use Kununu\Elasticsearch\Util\ArrayUtilities; | ||
|
||
final class CompositeAggregationQueryBuilder implements CompositeAggregationQueryInterface | ||
{ | ||
private ?array $afterKey = null; | ||
private Filters $filters; | ||
private ?string $name = null; | ||
private ?Sources $sources = null; | ||
|
||
private function __construct() | ||
{ | ||
$this->filters = new Filters(); | ||
} | ||
|
||
public static function create(): self | ||
{ | ||
return new self(); | ||
} | ||
|
||
public function withAfterKey(?array $afterKey): self | ||
{ | ||
$this->afterKey = $afterKey; | ||
|
||
return $this; | ||
} | ||
|
||
public function withFilters(Filters $filters): self | ||
{ | ||
$this->filters = $filters; | ||
|
||
return $this; | ||
} | ||
|
||
public function withName(string $name): self | ||
{ | ||
$this->name = $name; | ||
|
||
return $this; | ||
} | ||
|
||
public function withSources(Sources $sources): self | ||
{ | ||
$this->sources = $sources; | ||
|
||
return $this; | ||
} | ||
|
||
public function getName(): string | ||
{ | ||
if (null === $this->name) { | ||
throw new MissingAggregationAttributesException('Aggregation name is missing'); | ||
} | ||
|
||
return $this->name; | ||
} | ||
|
||
public function getQuery(int $compositeSize = 100): QueryInterface | ||
{ | ||
return RawQuery::create( | ||
ArrayUtilities::filterNullAndEmptyValues([ | ||
'query' => [ | ||
'bool' => [ | ||
'must' => $this->filters->map(fn(Filter $filter) => $filter->toArray()), | ||
], | ||
], | ||
'aggs' => [ | ||
$this->getName() => [ | ||
'composite' => [ | ||
'size' => $compositeSize, | ||
'sources' => $this->sources?->map( | ||
fn(SourceProperty $sourceProperty) => [ | ||
$sourceProperty->source => [ | ||
'terms' => [ | ||
'field' => $sourceProperty->property, | ||
'missing_bucket' => $sourceProperty->missingBucket, | ||
] | ||
], | ||
] | ||
) ?? [], | ||
'after' => $this->afterKey, | ||
], | ||
], | ||
], | ||
], true) | ||
); | ||
} | ||
|
||
public function toArray(): array | ||
{ | ||
return $this->getQuery()->toArray(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Query\Aggregation; | ||
|
||
final class SourceProperty | ||
{ | ||
public function __construct( | ||
public readonly string $source, | ||
public readonly string $property, | ||
public readonly bool $missingBucket = false | ||
) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Query\Aggregation; | ||
|
||
use InvalidArgumentException; | ||
use Kununu\Collection\AbstractCollection; | ||
|
||
final class Sources extends AbstractCollection | ||
{ | ||
private const INVALID = 'Can only append %s'; | ||
|
||
public function __construct(SourceProperty ...$sourceProperties) | ||
{ | ||
parent::__construct(); | ||
|
||
foreach ($sourceProperties as $sourceProperty) { | ||
$this->append($sourceProperty); | ||
} | ||
} | ||
|
||
public function current(): ?SourceProperty | ||
{ | ||
$current = parent::current(); | ||
assert($this->count() > 0 ? $current instanceof SourceProperty : null === $current); | ||
|
||
return $current; | ||
} | ||
|
||
public function append($value): void | ||
{ | ||
match (true) { | ||
$value instanceof SourceProperty => parent::append($value), | ||
default => throw new InvalidArgumentException(sprintf(self::INVALID, SourceProperty::class)) | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Query; | ||
|
||
interface CompositeAggregationQueryInterface | ||
{ | ||
public function getQuery(int $compositeSize = 100): QueryInterface; | ||
|
||
public function getName(): string; | ||
|
||
public function withName(string $name): self; | ||
|
||
public function withAfterKey(?array $afterKey): self; | ||
|
||
public function toArray(): array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Query\Criteria; | ||
|
||
use InvalidArgumentException; | ||
use Kununu\Collection\AbstractCollection; | ||
|
||
class Filters extends AbstractCollection | ||
{ | ||
private const INVALID = 'Can only append %s'; | ||
|
||
public function __construct(Filter ...$propertyFilters) | ||
{ | ||
parent::__construct(); | ||
|
||
foreach ($propertyFilters as $propertyFilter) { | ||
$this->append($propertyFilter); | ||
} | ||
} | ||
|
||
public function current(): ?Filter | ||
{ | ||
$current = parent::current(); | ||
assert($this->count() > 0 ? $current instanceof Filter : null === $current); | ||
|
||
return $current; | ||
} | ||
|
||
public function append($value): void | ||
{ | ||
match (true) { | ||
$value instanceof Filter => parent::append($value), | ||
default => throw new InvalidArgumentException(sprintf(self::INVALID, Filter::class)) | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Result; | ||
|
||
final class CompositeResult | ||
{ | ||
public function __construct( | ||
public readonly array $results, | ||
public readonly int $documentsCount, | ||
public readonly string $aggregationName | ||
) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kununu\Elasticsearch\Util; | ||
|
||
final class ArrayUtilities | ||
{ | ||
public static function filterNullAndEmptyValues(array $values, bool $recursive = false): array | ||
{ | ||
if ($recursive) { | ||
foreach ($values as &$value) { | ||
if (is_array($value)) { | ||
$value = self::filterNullAndEmptyValues($value, true); | ||
} | ||
} | ||
} | ||
|
||
return array_filter($values, fn ($value) => $value !== null && $value !== []); | ||
} | ||
} |
Oops, something went wrong.