├── tools
└── php-cs-fixer
│ ├── .gitignore
│ └── composer.json
├── .gitignore
├── src
├── QueryException.php
├── Contracts
│ ├── QueryInterface.php
│ └── BuildsArray.php
├── Entities
│ └── GpsPointEntity.php
├── Aggregation
│ ├── MaxAggregation.php
│ ├── MinAggregation.php
│ ├── SumAggregation.php
│ ├── AvgAggregation.php
│ ├── ReverseNestedAggregation.php
│ ├── StatsAggregation.php
│ ├── ValueCountAggregation.php
│ ├── WidthHistogramAggregation.php
│ ├── FilterAggregation.php
│ ├── NestedAggregation.php
│ ├── TopHitsAggregation.php
│ ├── AbstractAggregation.php
│ ├── CardinalityAggregation.php
│ ├── HistogramAggregation.php
│ ├── DateHistogramAggregation.php
│ ├── RangesAggregation.php
│ ├── MetricAggregation.php
│ ├── TermsAggregation.php
│ └── Aggregation.php
├── Query
│ ├── MatchPhraseQuery.php
│ ├── MatchPhrasePrefixQuery.php
│ ├── ExistsQuery.php
│ ├── FunctionsQuery.php
│ ├── RankFeatureQuery.php
│ ├── NestedQuery.php
│ ├── TermsQuery.php
│ ├── WildcardQuery.php
│ ├── GeoDistanceQuery.php
│ ├── AbstractMatchQuery.php
│ ├── MatchQuery.php
│ ├── PrefixQuery.php
│ ├── TermQuery.php
│ ├── GeoShapeQuery.php
│ ├── FunctionScoreQuery.php
│ ├── GeoBoundingBoxQuery.php
│ ├── MultiMatchQuery.php
│ ├── RangeQuery.php
│ ├── QueryStringQuery.php
│ ├── BoolQuery.php
│ ├── Query.php
│ └── SimpleQueryStringQuery.php
├── Features
│ ├── HasField.php
│ ├── HasBoost.php
│ ├── HasFormat.php
│ ├── HasRewrite.php
│ ├── HasOperator.php
│ ├── HasFuzziness.php
│ ├── HasCaseInsensitive.php
│ ├── HasMinimumShouldMatch.php
│ ├── HasExtendedBounds.php
│ ├── HasSorting.php
│ ├── HasCollapse.php
│ └── HasAggregations.php
├── Constants
│ └── SortDirections.php
├── Options
│ ├── Field.php
│ ├── SourceScript.php
│ ├── InlineScript.php
│ ├── Collapse.php
│ └── InnerHit.php
└── QueryBuilder.php
├── phpstan.neon
├── tests
├── Query
│ ├── ExistsQueryTest.php
│ ├── TermsQueryTest.php
│ ├── RankFeatureQueryTest.php
│ ├── PrefixQueryTest.php
│ ├── RangeQueryTest.php
│ ├── TermQueryTest.php
│ ├── GeoDistanceQueryTest.php
│ ├── QueryStringQueryTest.php
│ ├── GeoShapeQueryTest.php
│ ├── MatchPhraseQueryTest.php
│ ├── FunctionsQueryTest.php
│ ├── MatchPhrasePrefixQueryTest.php
│ ├── MultiMatchQueryTest.php
│ ├── MatchQueryTest.php
│ ├── FunctionScoreQueryTest.php
│ ├── GeoBoundingBoxQueryTest.php
│ ├── BoolQueryTest.php
│ └── SimpleQueryStringQueryTest.php
├── Aggregation
│ ├── MinAggregationTest.php
│ ├── StatsAggregationTest.php
│ ├── SumAggregationTest.php
│ ├── AvgAggregationTest.php
│ ├── MaxAggregationTest.php
│ ├── DateHistogramAggregationTest.php
│ ├── CardinalityAggregationTest.php
│ └── RangesAggregationTest.php
└── QueryBuilderTest.php
├── phpunit.xml.dist
├── LICENSE
├── UPGRADE-3.0.md
├── .github
└── workflows
│ └── php.yml
├── composer.json
├── .php_cs.dist.php
└── README.md
/tools/php-cs-fixer/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 | .phpunit.result.cache
4 | .editorconfig
5 | .php-cs-fixer.cache
--------------------------------------------------------------------------------
/src/QueryException.php:
--------------------------------------------------------------------------------
1 | field = $field;
12 |
13 | return $this;
14 | }
15 |
16 | public function getField(): string
17 | {
18 | return $this->field;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/phpstan/phpstan-phpunit/extension.neon
3 | - vendor/phpstan/phpstan-phpunit/rules.neon
4 | - vendor/phpstan/phpstan-deprecation-rules/rules.neon
5 |
6 | parameters:
7 |
8 | parallel:
9 | processTimeout: 600.0
10 |
11 | paths:
12 | - src
13 | - tests
14 |
15 | # The level 9 is the highest level
16 | level: 9
17 |
18 | # it is impossible to map build()
19 | checkMissingIterableValueType: false
20 |
21 |
--------------------------------------------------------------------------------
/src/Constants/SortDirections.php:
--------------------------------------------------------------------------------
1 | self::ASC,
21 | self::DESC => self::DESC,
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Query/ExistsQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'exists' => [
18 | 'field' => 'someFieldName',
19 | ],
20 | ], $query->build());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Query/ExistsQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
15 | }
16 |
17 | public function build(): array
18 | {
19 | return [
20 | 'exists' => [
21 | 'field' => $this->field,
22 | ],
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Query/TermsQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'terms' => [
18 | 'field' => ['value1', 'value2'],
19 | 'boost' => 1.4,
20 | ],
21 | ], $query->build());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Options/Field.php:
--------------------------------------------------------------------------------
1 | $this->value,
17 | ];
18 | }
19 |
20 | public function getValue(): string
21 | {
22 | return $this->value;
23 | }
24 |
25 | public function setValue(string $value): void
26 | {
27 | $this->value = $value;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Features/HasBoost.php:
--------------------------------------------------------------------------------
1 | boost = $boost;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildBoostTo(array &$array): self
17 | {
18 | if (null === $this->boost) {
19 | return $this;
20 | }
21 |
22 | $array['boost'] = $this->boost;
23 |
24 | return $this;
25 | }
26 |
27 | public function getBoost(): ?float
28 | {
29 | return $this->boost;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Query/RankFeatureQueryTest.php:
--------------------------------------------------------------------------------
1 | setBoost(0.9);
17 |
18 | $this->assertEquals([
19 | 'rank_feature' => [
20 | 'field' => 'rank',
21 | 'boost' => 0.9,
22 | ],
23 | ], $rankFeatureQuery->build());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Features/HasFormat.php:
--------------------------------------------------------------------------------
1 | format = $format;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildFormatTo(array &$array): self
17 | {
18 | if (null === $this->format) {
19 | return $this;
20 | }
21 |
22 | $array['format'] = $this->format;
23 |
24 | return $this;
25 | }
26 |
27 | public function getFormat(): ?string
28 | {
29 | return $this->format;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Query/PrefixQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'prefix' => [
18 | 'title' => [
19 | 'value' => 'a brown fox',
20 | 'case_insensitive' => true,
21 | ],
22 | ],
23 | ], $query->build());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Features/HasRewrite.php:
--------------------------------------------------------------------------------
1 | rewrite = $rewrite;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildRewriteTo(array &$array): self
17 | {
18 | if (null === $this->rewrite) {
19 | return $this;
20 | }
21 |
22 | $array['rewrite'] = $this->rewrite;
23 |
24 | return $this;
25 | }
26 |
27 | public function getRewrite(): ?string
28 | {
29 | return $this->rewrite;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Query/RangeQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'range' => [
18 | 'points' => [
19 | 'gt' => 10,
20 | 'lt' => 50,
21 | 'boost' => 0.8,
22 | ],
23 | ],
24 | ], $query->build());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Features/HasOperator.php:
--------------------------------------------------------------------------------
1 | operator = $operator;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildOperatorTo(array &$array): self
17 | {
18 | if (null === $this->operator) {
19 | return $this;
20 | }
21 |
22 | $array['operator'] = $this->operator;
23 |
24 | return $this;
25 | }
26 |
27 | public function getOperator(): ?string
28 | {
29 | return $this->operator;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Features/HasFuzziness.php:
--------------------------------------------------------------------------------
1 | fuzziness = $fuzziness;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildFuzzinessTo(array &$array): self
17 | {
18 | if (null === $this->fuzziness) {
19 | return $this;
20 | }
21 |
22 | $array['fuzziness'] = $this->fuzziness;
23 |
24 | return $this;
25 | }
26 |
27 | public function getFuzziness(): ?string
28 | {
29 | return $this->fuzziness;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Query/TermQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'term' => [
18 | 'title' => [
19 | 'value' => 'a brown fox',
20 | 'boost' => 1.1,
21 | 'case_insensitive' => true,
22 | ],
23 | ],
24 | ], $query->build());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/Query/GeoDistanceQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'geo_distance' => [
18 | 'distance' => '200km',
19 | 'geolocation' => [
20 | 'lat' => 40,
21 | 'lon' => -70,
22 | ],
23 | ],
24 | ], $query->build());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Options/SourceScript.php:
--------------------------------------------------------------------------------
1 | script = $script;
16 |
17 | return $this;
18 | }
19 |
20 | public function build(): array
21 | {
22 | return [
23 | 'script' => [
24 | 'source' => $this->script,
25 | ],
26 | ];
27 | }
28 |
29 | public function getScript(): string
30 | {
31 | return $this->script;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./
6 |
7 |
8 | ./tests
9 | ./vendor
10 |
11 |
12 |
13 |
14 | ./tests/
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Features/HasCaseInsensitive.php:
--------------------------------------------------------------------------------
1 | caseInsensitive = $caseInsensitive;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildCaseInsensitiveTo(array &$array): self
17 | {
18 | if (null === $this->caseInsensitive) {
19 | return $this;
20 | }
21 |
22 | $array['case_insensitive'] = $this->caseInsensitive;
23 |
24 | return $this;
25 | }
26 |
27 | public function getCaseInsensitive(): ?bool
28 | {
29 | return $this->caseInsensitive;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Options/InlineScript.php:
--------------------------------------------------------------------------------
1 | script = $script;
16 |
17 | return $this;
18 | }
19 |
20 | public function getScript(): string
21 | {
22 | return $this->script;
23 | }
24 |
25 | public function build(): array
26 | {
27 | return [
28 | 'script' => [
29 | 'inline' => $this->script,
30 | 'lang' => 'painless',
31 | ],
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/Query/QueryStringQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'query_string' => [
18 | 'query' => 'brown fox',
19 | 'default_field' => 'test',
20 | 'default_operator' => 'AND',
21 | 'boost' => 1.1,
22 | 'minimum_should_match' => '10%',
23 | ],
24 | ], $queryStringQuery->build());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Features/HasMinimumShouldMatch.php:
--------------------------------------------------------------------------------
1 | minimumShouldMatch = $minimumShouldMatch;
12 |
13 | return $this;
14 | }
15 |
16 | public function buildMinimumShouldMatchTo(array &$array): self
17 | {
18 | if (null === $this->minimumShouldMatch) {
19 | return $this;
20 | }
21 |
22 | $array['minimum_should_match'] = $this->minimumShouldMatch;
23 |
24 | return $this;
25 | }
26 |
27 | public function getMinimumShouldMatch(): ?string
28 | {
29 | return $this->minimumShouldMatch;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Aggregation/ValueCountAggregation.php:
--------------------------------------------------------------------------------
1 | field = $field;
19 | }
20 |
21 | protected function getType(): string
22 | {
23 | return 'variable_width_histogram';
24 | }
25 |
26 | protected function buildAggregation(): array
27 | {
28 | return [
29 | 'field' => $this->field,
30 | 'buckets' => $this->buckets,
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/Query/GeoShapeQueryTest.php:
--------------------------------------------------------------------------------
1 | setRelation('outside');
17 |
18 | $this->assertEquals([
19 | 'geo_shape' => [
20 | 'coordinates' => [
21 | 'shape' => [
22 | 'type' => 'polygon',
23 | 'coordinates' => [[-70, 40]],
24 | ],
25 | 'relation' => 'outside',
26 | ],
27 | ],
28 | ], $query->build());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Query/FunctionsQuery.php:
--------------------------------------------------------------------------------
1 | weight = $weight;
18 |
19 | return $this;
20 | }
21 |
22 | public function build(): array
23 | {
24 | $functions = [];
25 | $functions['filter'] = [
26 | 'term' => [
27 | '_index' => $this->field,
28 | ],
29 | ];
30 |
31 | if (null !== $this->weight) {
32 | $functions['weight'] = $this->weight;
33 | }
34 |
35 | return $functions;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Features/HasExtendedBounds.php:
--------------------------------------------------------------------------------
1 | min = $min;
17 |
18 | return $this;
19 | }
20 |
21 | public function getMin(): ?string
22 | {
23 | return $this->min;
24 | }
25 |
26 | public function setMax(?string $max): self
27 | {
28 | $this->max = $max;
29 |
30 | return $this;
31 | }
32 |
33 | public function getMax(): ?string
34 | {
35 | return $this->max;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Aggregation/FilterAggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
11 | */
12 | public function __construct(
13 | string $name,
14 | private QueryInterface $query,
15 | array $aggregations = []
16 | ) {
17 | parent::__construct($name, $aggregations);
18 | }
19 |
20 | public function getQuery(): QueryInterface
21 | {
22 | return $this->query;
23 | }
24 |
25 | public function setQuery(QueryInterface $query): void
26 | {
27 | $this->query = $query;
28 | }
29 |
30 | protected function getType(): string
31 | {
32 | return 'filter';
33 | }
34 |
35 | protected function buildAggregation(): array
36 | {
37 | return $this->query->build();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Query/RankFeatureQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
20 | $this->boost = $boost;
21 | }
22 |
23 | public function setParams(array $params): self
24 | {
25 | $this->params = $params;
26 |
27 | return $this;
28 | }
29 |
30 | public function build(): array
31 | {
32 | $build = $this->params;
33 | $build['field'] = $this->field;
34 |
35 | $this->buildBoostTo($build);
36 |
37 | return [
38 | 'rank_feature' => $build,
39 | ];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Aggregation/NestedAggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
9 | */
10 | public function __construct(
11 | string $name,
12 | private ?string $path = null,
13 | array $aggregations = [],
14 | ) {
15 | parent::__construct($name, $aggregations);
16 | }
17 |
18 | public function setNestedPath(string $path): self
19 | {
20 | $this->path = $path;
21 |
22 | return $this;
23 | }
24 |
25 | public function getNestedPath(): string
26 | {
27 | return $this->path;
28 | }
29 |
30 | protected function getType(): string
31 | {
32 | return 'nested';
33 | }
34 |
35 | protected function buildAggregation(): array
36 | {
37 | if (null === $this->path) {
38 | return [];
39 | }
40 |
41 | return [
42 | 'path' => $this->path,
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Features/HasSorting.php:
--------------------------------------------------------------------------------
1 | >|array
11 | */
12 | protected array $sort = [];
13 |
14 | /**
15 | * Adds sort.
16 | *
17 | * @param array|string $config Can be order direction ('desc') or config (['order' => 'asc']]
18 | *
19 | * @return $this
20 | */
21 | public function addSort(string $field, string|array $config = SortDirections::ASC): self
22 | {
23 | $this->sort[$field] = $config;
24 |
25 | return $this;
26 | }
27 |
28 | /**
29 | * Adds sort settings to array if sorting was set.
30 | */
31 | protected function buildSortTo(array &$toArray): self
32 | {
33 | if (false === empty($this->sort)) {
34 | foreach ($this->sort as $sort => $config) {
35 | $toArray['sort'][$sort] = $config;
36 | }
37 | }
38 |
39 | return $this;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Erwan Richard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Query/NestedQuery.php:
--------------------------------------------------------------------------------
1 | path = $path;
18 |
19 | return $this;
20 | }
21 |
22 | public function setQuery(QueryInterface $query): self
23 | {
24 | $this->query = $query;
25 |
26 | return $this;
27 | }
28 |
29 | public function setParams(array $params): self
30 | {
31 | $this->params = $params;
32 |
33 | return $this;
34 | }
35 |
36 | public function build(): array
37 | {
38 | $build = $this->params;
39 | $build['nested'] = [
40 | 'path' => $this->path,
41 | 'query' => $this->query->build(),
42 | ];
43 |
44 | return $build;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/UPGRADE-3.0.md:
--------------------------------------------------------------------------------
1 | # Upgrade guide for 3.0
2 |
3 |
4 | - If you are type-hinting build aggregation use `AbstractAggregation` instead of `Aggregation`
5 | - If you are implementing own `Aggregation` - extend `AbstractAggregation`
6 | - If you are implementing own `Query` (filter) - implement `QueryInterface`
7 |
8 | ## Rewrite filters to Query
9 |
10 | - All files were renamed from `Filter` suffix to `Query`
11 | - namespace has been moved to `Query`
12 | - Move values from `setField` (and other "required" properties) to a constructor of the filter
13 | - Find in your IDE all usages of this text `use Erichard\ElasticQueryBuilder\Filter\`
14 | - Then find all `Filter` definitions and rewrite them to Query
15 | - Remove any lines with `use Erichard\ElasticQueryBuilder\Filter\`
16 | - Use PHPStan to find bugs after refactoring.
17 |
18 | ## New terminology
19 |
20 | - All xxxFilter classes have been renamed to xxxQuery to be more consistent with Elastic terms.
21 | - We are using strict types - ensure that values you are sending are valid.
22 | - Required properties must be defined in constructor. Optional can be in constructor too (for PHP 8.1 - named arguments)
23 |
24 |
--------------------------------------------------------------------------------
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: PHP Composer
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | permissions:
10 | contents: read
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 |
20 | - name: Validate composer.json and composer.lock
21 | run: composer validate --strict
22 |
23 | - name: Cache Composer packages
24 | id: composer-cache
25 | uses: actions/cache@v3
26 | with:
27 | path: vendor
28 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
29 | restore-keys: |
30 | ${{ runner.os }}-php-
31 |
32 | - name: Install dependencies
33 | run: composer install --prefer-dist --no-progress
34 |
35 | - name: Install php-cs-fixer
36 | run: composer install --working-dir=tools/php-cs-fixer
37 |
38 | - name: Check coding standards
39 | run: composer run-script lint:fix
40 |
41 | - name: Check PHPStan
42 | run: composer run-script lint:stan
43 |
44 | - name: Run test suite
45 | run: composer run-script test
46 |
--------------------------------------------------------------------------------
/src/Aggregation/TopHitsAggregation.php:
--------------------------------------------------------------------------------
1 | script = $script;
18 |
19 | return $this;
20 | }
21 |
22 | public function setSize(int $size): self
23 | {
24 | $this->size = $size;
25 |
26 | return $this;
27 | }
28 |
29 | protected function getType(): string
30 | {
31 | return 'top_hits';
32 | }
33 |
34 | protected function buildAggregation(): array
35 | {
36 | $data = [
37 | 'size' => $this->size,
38 | ];
39 |
40 | if (null !== $this->script) {
41 | $data['_source'] = [
42 | 'includes' => $this->script,
43 | ];
44 | }
45 |
46 | $this->buildSortTo($data);
47 |
48 | return $data;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/Query/MatchPhraseQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'match_phrase' => [
18 | 'title' => [
19 | 'query' => 'a brown fox',
20 | ],
21 | ],
22 | ], $query->build());
23 | }
24 |
25 | public function testItBuildTheQueryWithAnAnalyzer(): void
26 | {
27 | $query = new MatchPhraseQuery('title', 'a brown fox');
28 | $query->setAnalyzer('custom_analyzer');
29 |
30 | $this->assertEquals([
31 | 'match_phrase' => [
32 | 'title' => [
33 | 'query' => 'a brown fox',
34 | 'analyzer' => 'custom_analyzer',
35 | ],
36 | ],
37 | ], $query->build());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Query/FunctionsQueryTest.php:
--------------------------------------------------------------------------------
1 | [
18 | 'term' => [
19 | '_index' => 'column1',
20 | ],
21 | ],
22 | ];
23 |
24 | $this->assertEquals($response, $functionsQuery->build());
25 | }
26 |
27 | public function testFunctionsQuerySetWeight(): void
28 | {
29 | $functionsQuery = new FunctionsQuery('column1');
30 | $functionsQuery->setWeight(2.50);
31 |
32 | $response = [
33 | 'filter' => [
34 | 'term' => [
35 | '_index' => 'column1',
36 | ],
37 | ],
38 | 'weight' => 2.50,
39 | ];
40 |
41 | $this->assertEquals($response, $functionsQuery->build());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Query/MatchPhrasePrefixQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'match_phrase_prefix' => [
18 | 'title' => [
19 | 'query' => 'a brown fox',
20 | ],
21 | ],
22 | ], $query->build());
23 | }
24 |
25 | public function testItBuildTheQueryWithAnAnalyzer(): void
26 | {
27 | $query = new MatchPhrasePrefixQuery('title', 'a brown fox');
28 | $query->setAnalyzer('custom_analyzer');
29 |
30 | $this->assertEquals([
31 | 'match_phrase_prefix' => [
32 | 'title' => [
33 | 'query' => 'a brown fox',
34 | 'analyzer' => 'custom_analyzer',
35 | ],
36 | ],
37 | ], $query->build());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Query/MultiMatchQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'multi_match' => [
18 | 'fields' => ['subject', 'body'],
19 | 'query' => 'a brown fox',
20 | 'type' => 'cross_fields',
21 | 'operator' => 'AND',
22 | ],
23 | ], $query->build());
24 | }
25 |
26 | public function testItBuildTheQueryWithAFuzziness(): void
27 | {
28 | $query = new MultiMatchQuery(['subject', 'body'], 'a brown fox');
29 | $query->setFuzziness('AUTO');
30 |
31 | $this->assertEquals([
32 | 'multi_match' => [
33 | 'fields' => ['subject', 'body'],
34 | 'query' => 'a brown fox',
35 | 'fuzziness' => 'AUTO',
36 | ],
37 | ], $query->build());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Features/HasCollapse.php:
--------------------------------------------------------------------------------
1 | collapse = is_string($collapseByField)
21 | ? new Collapse($collapseByField)
22 | : $collapseByField;
23 |
24 | return $this;
25 | }
26 |
27 | public function getCollapse(): ?Collapse
28 | {
29 | return $this->collapse;
30 | }
31 |
32 | /**
33 | * Adds collapse to array if field collapsing is set.
34 | */
35 | protected function buildCollapseTo(array &$toArray): self
36 | {
37 | if (null !== $this->collapse) {
38 | $toArray['collapse'] = $this->collapse->build();
39 | }
40 |
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Query/TermsQuery.php:
--------------------------------------------------------------------------------
1 | $values
16 | */
17 | public function __construct(
18 | string $field,
19 | protected array $values,
20 | ?float $boost = null,
21 | protected array $params = [],
22 | ) {
23 | $this->field = $field;
24 | $this->boost = $boost;
25 | }
26 |
27 | public function setValues(array $values): self
28 | {
29 | $this->values = $values;
30 |
31 | return $this;
32 | }
33 |
34 | public function setParams(array $params): self
35 | {
36 | $this->params = $params;
37 |
38 | return $this;
39 | }
40 |
41 | public function build(): array
42 | {
43 | $build = $this->params;
44 | $build[$this->field] = array_values($this->values);
45 |
46 | $this->buildBoostTo($build);
47 |
48 | return [
49 | 'terms' => $build,
50 | ];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Query/WildcardQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
18 | }
19 |
20 | public function setValue(string $value): self
21 | {
22 | $this->value = $value;
23 |
24 | return $this;
25 | }
26 |
27 | public static function escapeWildcards(string $string): string
28 | {
29 | $escapeChars = ['*', '?'];
30 | foreach ($escapeChars as $escapeChar) {
31 | $string = str_replace($escapeChar, '\\'.$escapeChar, $string);
32 | }
33 |
34 | return $string;
35 | }
36 |
37 | public function setParams(array $params): self
38 | {
39 | $this->params = $params;
40 |
41 | return $this;
42 | }
43 |
44 | public function build(): array
45 | {
46 | $build = $this->params;
47 | $build['wildcard'] = [
48 | $this->field => $this->value,
49 | ];
50 |
51 | return $build;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Features/HasAggregations.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | private array $aggregations = [];
13 |
14 | public function addAggregation(AbstractAggregation $aggregation): self
15 | {
16 | $this->aggregations[] = $aggregation;
17 |
18 | return $this;
19 | }
20 |
21 | /**
22 | * @param array $aggregations
23 | */
24 | public function setAggregations(array $aggregations): self
25 | {
26 | $this->aggregations = $aggregations;
27 |
28 | return $this;
29 | }
30 |
31 | /**
32 | * @return array|null
33 | */
34 | public function getAggregations(): ?array
35 | {
36 | return $this->aggregations;
37 | }
38 |
39 | protected function buildAggregationsTo(array &$toArray): self
40 | {
41 | if (0 === count($this->aggregations)) {
42 | return $this;
43 | }
44 |
45 | $aggregations = [];
46 | foreach ($this->aggregations as $aggregation) {
47 | $aggregations[$aggregation->getName()] = $aggregation->build();
48 | }
49 |
50 | $toArray['aggs'] = $aggregations;
51 |
52 | return $this;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Aggregation/AbstractAggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
14 | */
15 | public function __construct(
16 | private string $name,
17 | array $aggregations = [],
18 | ) {
19 | $this->aggregations = $aggregations;
20 | }
21 |
22 | /**
23 | * @return array|array|array
24 | */
25 | public function build(): array
26 | {
27 | $build = $this->buildAggregation();
28 |
29 | if ([] === $build) {
30 | $build = new \stdClass();
31 | }
32 |
33 | $data = [
34 | $this->getType() => $build,
35 | ];
36 |
37 | $this->buildAggregationsTo($data);
38 |
39 | return $data;
40 | }
41 |
42 | public function setName(string $name): self
43 | {
44 | $this->name = $name;
45 |
46 | return $this;
47 | }
48 |
49 | public function getName(): string
50 | {
51 | return $this->name;
52 | }
53 |
54 | abstract protected function getType(): string;
55 |
56 | abstract protected function buildAggregation(): array;
57 | }
58 |
--------------------------------------------------------------------------------
/src/Query/GeoDistanceQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
22 | }
23 |
24 | public function setDistance(string $distance): self
25 | {
26 | $this->distance = $distance;
27 |
28 | return $this;
29 | }
30 |
31 | public function setPosition(array $position): self
32 | {
33 | $this->position = $position;
34 |
35 | return $this;
36 | }
37 |
38 | public function setParams(array $params): self
39 | {
40 | $this->params = $params;
41 |
42 | return $this;
43 | }
44 |
45 | public function build(): array
46 | {
47 | $build = $this->params;
48 | $build['geo_distance'] = [
49 | 'distance' => $this->distance,
50 | $this->field => [
51 | 'lat' => $this->position[0],
52 | 'lon' => $this->position[1],
53 | ],
54 | ];
55 |
56 | return $build;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Aggregation/CardinalityAggregation.php:
--------------------------------------------------------------------------------
1 | precisionThreshold = $precisionThreshold;
25 |
26 | return $this;
27 | }
28 |
29 | public function getPrecisionThreshold(): ?int
30 | {
31 | return $this->precisionThreshold;
32 | }
33 |
34 | protected function getType(): string
35 | {
36 | return 'cardinality';
37 | }
38 |
39 | protected function buildAggregation(): array
40 | {
41 | $build = parent::buildAggregation();
42 |
43 | if (null !== $this->precisionThreshold) {
44 | $build['precision_threshold'] = $this->precisionThreshold;
45 | }
46 |
47 | return $build;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Query/AbstractMatchQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
19 | }
20 |
21 | public function setQuery(string $query): self
22 | {
23 | $this->query = $query;
24 |
25 | return $this;
26 | }
27 |
28 | public function setAnalyzer(?string $analyzer): self
29 | {
30 | $this->analyzer = $analyzer;
31 |
32 | return $this;
33 | }
34 |
35 | public function setParams(array $params): self
36 | {
37 | $this->params = $params;
38 |
39 | return $this;
40 | }
41 |
42 | public function build(): array
43 | {
44 | $queryName = $this->getQueryName();
45 |
46 | $query = $this->params;
47 | $query[$queryName] = [
48 | $this->field => [
49 | 'query' => $this->query,
50 | ],
51 | ];
52 |
53 | if (null !== $this->analyzer) {
54 | $query[$queryName][$this->field]['analyzer'] = $this->analyzer;
55 | }
56 |
57 | return $query;
58 | }
59 |
60 | abstract public function getQueryName(): string;
61 | }
62 |
--------------------------------------------------------------------------------
/src/Query/MatchQuery.php:
--------------------------------------------------------------------------------
1 | operator = $operator;
27 | $this->minimumShouldMatch = $minimumShouldMatch;
28 | $this->fuzziness = $fuzziness;
29 | }
30 |
31 | public function getQueryName(): string
32 | {
33 | return 'match';
34 | }
35 |
36 | public function setParams(array $params): self
37 | {
38 | $this->params = $params;
39 |
40 | return $this;
41 | }
42 |
43 | public function build(): array
44 | {
45 | $build = parent::build();
46 |
47 | $this->buildOperatorTo($build[$this->getQueryName()][$this->field]);
48 | $this->buildMinimumShouldMatchTo($build[$this->getQueryName()][$this->field]);
49 | $this->buildFuzzinessTo($build[$this->getQueryName()][$this->field]);
50 |
51 | return $build;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "erichard/elasticsearch-query-builder",
3 | "license": "MIT",
4 | "type": "library",
5 | "description": "Create elastic search query with a fluent interface",
6 | "authors": [
7 | {
8 | "name": "Erwan Richard",
9 | "email": "erwan.richard@protonmail.com"
10 | }
11 | ],
12 | "autoload": {
13 | "psr-4": {
14 | "Erichard\\ElasticQueryBuilder\\": "src/"
15 | }
16 | },
17 | "autoload-dev": {
18 | "psr-4": {
19 | "Tests\\Erichard\\ElasticQueryBuilder\\": "tests/"
20 | }
21 | },
22 | "require": {
23 | "php": ">=8.0"
24 | },
25 | "require-dev": {
26 | "phpstan/phpstan": "^1.4.10",
27 | "phpstan/phpstan-deprecation-rules": "^1.0.0",
28 | "phpstan/phpstan-phpunit": "^1.0.0",
29 | "phpunit/phpunit": "^9.5.19",
30 | "rector/rector": "^0.12.17",
31 | "symplify/easy-coding-standard": "^10.1"
32 | },
33 | "scripts": {
34 | "lint:fix": "php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=./.php_cs.dist.php",
35 | "lint:stan": "./vendor/bin/phpstan",
36 | "lint": "composer lint:fix && composer lint:stan",
37 | "test": "./vendor/bin/phpunit"
38 | },
39 | "extra": {
40 | "branch-alias": {
41 | "dev-main": "3.0-dev"
42 | }
43 | },
44 | "config": {
45 | "preferred-install": "dist",
46 | "sort-packages": true,
47 | "optimize-autoloader": true,
48 | "allow-plugins": {
49 | "symfony/thanks": false
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/Aggregation/HistogramAggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
15 | */
16 | public function __construct(
17 | string $name,
18 | string $field,
19 | private int $interval,
20 | array $aggregations = [],
21 | ?string $min = null,
22 | ?string $max = null,
23 | ) {
24 | parent::__construct($name, $aggregations);
25 | $this->field = $field;
26 | $this->min = $min;
27 | $this->max = $max;
28 | }
29 |
30 | public function getInterval(): int
31 | {
32 | return $this->interval;
33 | }
34 |
35 | public function setInterval(int $interval): void
36 | {
37 | $this->interval = $interval;
38 | }
39 |
40 | protected function getType(): string
41 | {
42 | return 'histogram';
43 | }
44 |
45 | protected function buildAggregation(): array
46 | {
47 | $build = [
48 | 'field' => $this->field,
49 | 'interval' => $this->interval,
50 | ];
51 |
52 | if (null !== $this->min && null !== $this->max) {
53 | $build['extended_bounds']['min'] = $this->min;
54 | $build['extended_bounds']['max'] = $this->max;
55 | }
56 |
57 | return $build;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Query/PrefixQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
27 | $this->rewrite = $rewrite;
28 | $this->caseInsensitive = $caseInsensitive;
29 | }
30 |
31 | public function setValue(string $value): self
32 | {
33 | $this->value = $value;
34 |
35 | return $this;
36 | }
37 |
38 | public function setParams(array $params): self
39 | {
40 | $this->params = $params;
41 |
42 | return $this;
43 | }
44 |
45 | public function build(): array
46 | {
47 | $build = $this->params;
48 | $build['value'] = $this->value;
49 |
50 | $this->buildRewriteTo($build);
51 | $this->buildCaseInsensitiveTo($build);
52 |
53 | return [
54 | 'prefix' => [
55 | $this->field => $build,
56 | ],
57 | ];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Query/TermQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
27 | $this->boost = $boost;
28 | $this->caseInsensitive = $caseInsensitive;
29 | }
30 |
31 | public function setValue(string|int|float|bool $value): self
32 | {
33 | $this->value = $value;
34 |
35 | return $this;
36 | }
37 |
38 | public function setParams(array $params): self
39 | {
40 | $this->params = $params;
41 |
42 | return $this;
43 | }
44 |
45 | public function build(): array
46 | {
47 | $build = $this->params;
48 | $build[$this->field] = [
49 | 'value' => $this->value,
50 | ];
51 |
52 | $this->buildBoostTo($build[$this->field]);
53 | $this->buildCaseInsensitiveTo($build[$this->field]);
54 |
55 | return [
56 | 'term' => $build,
57 | ];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tests/Aggregation/MinAggregationTest.php:
--------------------------------------------------------------------------------
1 | setField('price');
16 |
17 | $this->assertEquals([
18 | 'min' => [
19 | 'field' => 'price',
20 | ],
21 | ], $query->build());
22 | }
23 |
24 | public function testItBuildTheAggregationUsingAScript(): void
25 | {
26 | $query = new MinAggregation('min_price');
27 | $query->setScript('doc.price.value');
28 |
29 | $this->assertEquals([
30 | 'min' => [
31 | 'script' => [
32 | 'source' => 'doc.price.value',
33 | ],
34 | ],
35 | ], $query->build());
36 | }
37 |
38 | public function testWithFieldName(): void
39 | {
40 | $query = new MinAggregation('min_price');
41 |
42 | $this->assertEquals([
43 | 'min' => [
44 | 'field' => 'min_price',
45 | ],
46 | ], $query->build());
47 | }
48 |
49 | public function testItBuildTheAggregationWithMissingValue(): void
50 | {
51 | $query = new MinAggregation('min_price', 'price');
52 | $query->setMissing(10);
53 |
54 | $this->assertEquals([
55 | 'min' => [
56 | 'field' => 'price',
57 | 'missing' => 10,
58 | ],
59 | ], $query->build());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/Aggregation/StatsAggregationTest.php:
--------------------------------------------------------------------------------
1 | setField('price');
16 |
17 | $this->assertEquals([
18 | 'stats' => [
19 | 'field' => 'price',
20 | ],
21 | ], $query->build());
22 | }
23 |
24 | public function testItBuildTheAggregationUsingAScript(): void
25 | {
26 | $query = new StatsAggregation('price');
27 | $query->setScript('doc.price.value');
28 |
29 | $this->assertEquals([
30 | 'stats' => [
31 | 'script' => [
32 | 'source' => 'doc.price.value',
33 | ],
34 | ],
35 | ], $query->build());
36 | }
37 |
38 | public function testWithFieldName(): void
39 | {
40 | $query = new StatsAggregation('price');
41 |
42 | $this->assertEquals([
43 | 'stats' => [
44 | 'field' => 'price',
45 | ],
46 | ], $query->build());
47 | }
48 |
49 | public function testItBuildTheAggregationWithMissingValue(): void
50 | {
51 | $query = new StatsAggregation('price', 'price');
52 | $query->setMissing(10);
53 |
54 | $this->assertEquals([
55 | 'stats' => [
56 | 'field' => 'price',
57 | 'missing' => 10,
58 | ],
59 | ], $query->build());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/Aggregation/SumAggregationTest.php:
--------------------------------------------------------------------------------
1 | setField('price');
16 |
17 | $this->assertEquals([
18 | 'sum' => [
19 | 'field' => 'price',
20 | ],
21 | ], $query->build());
22 | }
23 |
24 | public function testItBuildTheAggregationUsingAScript(): void
25 | {
26 | $query = new SumAggregation('sum_price');
27 | $query->setScript('doc.price.value');
28 |
29 | $this->assertEquals([
30 | 'sum' => [
31 | 'script' => [
32 | 'source' => 'doc.price.value',
33 | ],
34 | ],
35 | ], $query->build());
36 | }
37 |
38 | public function testWithFieldName(): void
39 | {
40 | $query = new SumAggregation('sum_price');
41 |
42 | $this->assertEquals([
43 | 'sum' => [
44 | 'field' => 'sum_price',
45 | ],
46 | ], $query->build());
47 | }
48 |
49 | public function testItBuildTheAggregationWithMissingValue(): void
50 | {
51 | $query = new SumAggregation('sum_price', 'price');
52 | $query->setMissing(10);
53 |
54 | $this->assertEquals([
55 | 'sum' => [
56 | 'field' => 'price',
57 | 'missing' => 10,
58 | ],
59 | ], $query->build());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Query/GeoShapeQuery.php:
--------------------------------------------------------------------------------
1 | type = $type;
24 |
25 | return $this;
26 | }
27 |
28 | public function setCoordinates(array $coordinates): self
29 | {
30 | $this->coordinates = $coordinates;
31 |
32 | return $this;
33 | }
34 |
35 | public function setField(string $field): self
36 | {
37 | $this->field = $field;
38 |
39 | return $this;
40 | }
41 |
42 | public function setRelation(string $relation): self
43 | {
44 | $this->relation = $relation;
45 |
46 | return $this;
47 | }
48 |
49 | public function setParams(array $params): self
50 | {
51 | $this->params = $params;
52 |
53 | return $this;
54 | }
55 |
56 | public function build(): array
57 | {
58 | $build = $this->params;
59 | $build['geo_shape'] = [
60 | $this->field => [
61 | 'shape' => [
62 | 'type' => $this->type,
63 | 'coordinates' => $this->coordinates,
64 | ],
65 | 'relation' => $this->relation,
66 | ],
67 | ];
68 |
69 | return $build;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Query/FunctionScoreQuery.php:
--------------------------------------------------------------------------------
1 | boost = $boost;
26 |
27 | return $this;
28 | }
29 |
30 | public function setBoostMode(string $boostMode): self
31 | {
32 | $this->boostMode = $boostMode;
33 |
34 | return $this;
35 | }
36 |
37 | public function setFunctions(array $functions): self
38 | {
39 | $this->functions = $functions;
40 |
41 | return $this;
42 | }
43 |
44 | public function build(): array
45 | {
46 | $build = [];
47 | if (null !== $this->boostMode) {
48 | $build['boost_mode'] = $this->boostMode;
49 | }
50 |
51 | if (null !== $this->functions) {
52 | $build['functions'] = $this->functions;
53 | }
54 |
55 | $build['query'] = [
56 | 'query_string' => [
57 | 'query' => $this->query,
58 | 'fields' => $this->fields,
59 | ],
60 | ];
61 |
62 | if (null !== $this->boost) {
63 | $build['query']['query_string'] = $this->boost;
64 | }
65 |
66 | return [
67 | 'function_score' => $build,
68 | ];
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Options/Collapse.php:
--------------------------------------------------------------------------------
1 |
17 | *
18 | * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html#request-body-search-inner-hits
19 | */
20 | protected array $innerHits = [];
21 |
22 | /**
23 | * The number of concurrent requests allowed to retrieve the inner_hits` per group.
24 | */
25 | protected ?int $maxConcurrentSearchers = null;
26 |
27 | public function __construct(string $field)
28 | {
29 | $this->field = $field;
30 | }
31 |
32 | public function addInnerHit(InnerHit $hit): self
33 | {
34 | $this->innerHits[] = $hit;
35 |
36 | return $this;
37 | }
38 |
39 | public function build(): array
40 | {
41 | $result = [
42 | 'field' => $this->field,
43 | ];
44 |
45 | if (null !== $this->maxConcurrentSearchers) {
46 | $result['max_concurrent_group_searches'] = $this->maxConcurrentSearchers;
47 | }
48 |
49 | if (false === empty($this->innerHits)) {
50 | $result['inner_hits'] = array_map(fn (InnerHit $hit) => $hit->build(), $this->innerHits);
51 | }
52 |
53 | return $result;
54 | }
55 |
56 | public function setMaxConcurrentSearchers(int $maxConcurrentSearchers): self
57 | {
58 | $this->maxConcurrentSearchers = $maxConcurrentSearchers;
59 |
60 | return $this;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Query/GeoBoundingBoxQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
24 | }
25 |
26 | public function build(): array
27 | {
28 | $filters = array_filter([
29 | 'top_left' => $this->pointToArray($this->topLeft),
30 | 'bottom_right' => $this->pointToArray($this->bottomRight),
31 | 'top_right' => $this->pointToArray($this->topRight),
32 | 'bottom_left' => $this->pointToArray($this->bottomLeft),
33 | ]);
34 |
35 | if (count($filters) < 2) {
36 | throw new \Exception('GeoBoundingBoxQuery needs at least 2 sides set');
37 | }
38 |
39 | return [
40 | 'geo_bounding_box' => [
41 | $this->field => $filters,
42 | ],
43 | ];
44 | }
45 |
46 | protected function pointToArray(?GpsPointEntity $entity): ?array
47 | {
48 | if (null === $entity) {
49 | return null;
50 | }
51 |
52 | return [
53 | 'lat' => $entity->lat,
54 | 'lon' => $entity->lon,
55 | ];
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Options/InnerHit.php:
--------------------------------------------------------------------------------
1 | $this->from,
34 | 'size' => $this->size,
35 | 'name' => $this->name,
36 | ]);
37 | $this->buildSortTo($array);
38 | $this->buildCollapseTo($array);
39 |
40 | return $array;
41 | }
42 |
43 | public function getFrom(): ?string
44 | {
45 | return $this->from;
46 | }
47 |
48 | public function setFrom(?string $from): void
49 | {
50 | $this->from = $from;
51 | }
52 |
53 | public function getName(): string
54 | {
55 | return $this->name;
56 | }
57 |
58 | public function setName(string $name): self
59 | {
60 | $this->name = $name;
61 |
62 | return $this;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.php_cs.dist.php:
--------------------------------------------------------------------------------
1 | in(__DIR__ . '/src')
7 | ->exclude(__DIR__ . '/tests')
8 | ;
9 |
10 | return (new PhpCsFixer\Config())
11 | ->registerCustomFixers(new PhpCsFixerCustomFixers\Fixers())
12 | ->setRiskyAllowed(true)
13 | ->setRules([
14 | '@PSR12' => true,
15 | '@PSR12:risky' => true,
16 | '@Symfony' => true,
17 | '@Symfony:risky' => true,
18 | 'linebreak_after_opening_tag' => true,
19 | 'mb_str_functions' => true,
20 | 'no_php4_constructor' => true,
21 | 'no_unreachable_default_argument_value' => true,
22 | 'no_useless_else' => true,
23 | 'no_useless_return' => true,
24 | 'php_unit_strict' => true,
25 | 'phpdoc_order' => true,
26 | 'strict_comparison' => true,
27 | 'strict_param' => true,
28 | 'concat_space' => false,
29 | 'declare_strict_types' => true,
30 | 'native_function_invocation' => ['include' => []],
31 | 'nullable_type_declaration_for_default_null_value' => ['use_nullable_type_declaration' => true],
32 | PhpCsFixerCustomFixers\Fixer\DeclareAfterOpeningTagFixer::name() => true,
33 | PhpCsFixerCustomFixers\Fixer\NoDoctrineMigrationsGeneratedCommentFixer::name() => true,
34 | PhpCsFixerCustomFixers\Fixer\NoImportFromGlobalNamespaceFixer::name() => true,
35 | //PhpCsFixerCustomFixers\Fixer\PromotedConstructorPropertyFixer::name() => true,
36 | PhpCsFixerCustomFixers\Fixer\ConstructorEmptyBracesFixer::name() => true,
37 | PhpCsFixerCustomFixers\Fixer\MultilinePromotedPropertiesFixer::name() => true,
38 | PhpCsFixerCustomFixers\Fixer\NoUselessDoctrineRepositoryCommentFixer::name() => true,
39 | ])
40 | ->setFinder($finder)
41 | ;
42 |
--------------------------------------------------------------------------------
/tests/Query/MatchQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'match' => [
18 | 'title' => [
19 | 'query' => 'a brown fox',
20 | 'operator' => 'AND',
21 | 'minimum_should_match' => '20%',
22 | ],
23 | ],
24 | ], $query->build());
25 | }
26 |
27 | public function testItBuildTheQueryWithAnAnalyzer(): void
28 | {
29 | $query = new MatchQuery('title', 'a brown fox');
30 | $query->setAnalyzer('custom_analyzer');
31 |
32 | $this->assertEquals([
33 | 'match' => [
34 | 'title' => [
35 | 'query' => 'a brown fox',
36 | 'analyzer' => 'custom_analyzer',
37 | ],
38 | ],
39 | ], $query->build());
40 | }
41 |
42 | public function testItBuildTheQueryWithBoolean(): void
43 | {
44 | $query = new MatchQuery('is_closed', true);
45 | $this->assertSame([
46 | 'match' => [
47 | 'is_closed' => [
48 | 'query' => true
49 | ],
50 | ],
51 | ], $query->build());
52 | }
53 |
54 |
55 | public function testItBuildTheQueryWithInteger(): void
56 | {
57 | $query = new MatchQuery('count', 1);
58 |
59 | $this->assertSame([
60 | 'match' => [
61 | 'count' => [
62 | 'query' => 1
63 | ],
64 | ],
65 | ], $query->build());
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Aggregation/DateHistogramAggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
18 | */
19 | public function __construct(
20 | string $nameAndField,
21 | private string $calendarInterval,
22 | ?string $field = null,
23 | array $aggregations = [],
24 | ?string $min = null,
25 | ?string $max = null,
26 | ) {
27 | parent::__construct($nameAndField, $aggregations);
28 | $this->field = $field ?? $nameAndField;
29 | $this->min = $min;
30 | $this->max = $max;
31 | }
32 |
33 | public function setCalendarInterval(string $calendarInterval): self
34 | {
35 | $this->calendarInterval = $calendarInterval;
36 |
37 | return $this;
38 | }
39 |
40 | public function getCalendarInterval(): string
41 | {
42 | return $this->calendarInterval;
43 | }
44 |
45 | protected function getType(): string
46 | {
47 | return 'date_histogram';
48 | }
49 |
50 | protected function buildAggregation(): array
51 | {
52 | $build = [
53 | 'field' => $this->field,
54 | 'calendar_interval' => $this->calendarInterval,
55 | ];
56 |
57 | if (null !== $this->min && null !== $this->max) {
58 | $build['extended_bounds']['min'] = $this->min;
59 | $build['extended_bounds']['max'] = $this->max;
60 | }
61 |
62 | return $build;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ElasticSearch Query Builder
2 | ===========================
3 |
4 | 
5 | 
6 |
7 |
8 | This is a PHP library which helps you build query for an ElasticSearch client by using a fluent interface.
9 |
10 | WARNING: This branch contains the next 3.x release. Check the [corresponding issue](https://github.com/erichard/elasticsearch-query-builder/issues/7) for the roadmap.
11 |
12 | Installation
13 | ------------
14 |
15 | ```bash
16 | composer require erichard/elasticsearch-query-builder "^3.0@beta"
17 | ```
18 |
19 | Usage
20 | -----
21 |
22 | ```php
23 |
24 | use Erichard\ElasticQueryBuilder\QueryBuilder;
25 | use Erichard\ElasticQueryBuilder\Aggregation\Aggregation;
26 | use Erichard\ElasticQueryBuilder\Filter\Filter;
27 |
28 | $qb = new QueryBuilder();
29 |
30 | $qb
31 | ->setIndex('app')
32 | ->setSize(10)
33 | ;
34 |
35 | // Add an aggregation
36 | $qb->addAggregation(Aggregation::terms('agg_name', 'my_field'));
37 | $qb->addAggregation(Aggregation::terms('agg_name_same_as_field'));
38 |
39 | // Set query
40 | $qb->setQuery(Query::terms('field', 'value'));
41 |
42 | // I am using a client from elasticsearch/elasticsearch here
43 | $results = $client->search($qb->build());
44 | ```
45 |
46 | with PHP 8.1 you can use named arguments like this:
47 |
48 | ```php
49 | $query = new BoolQuery(must: [
50 | new RangeQuery(
51 | field: 'price',
52 | gte: 100
53 | ),
54 | new RangeQuery(
55 | field: 'stock',
56 | gte: 10
57 | ),
58 | ]);
59 | ```
60 |
61 | or with the factory
62 |
63 | ```php
64 | $query = Query::bool(must: [
65 | Query::range(
66 | field: 'price',
67 | gte: 100
68 | ),
69 | Query::range(
70 | field: 'stock',
71 | gte: 10
72 | ),
73 | ]);
74 | ```
75 |
76 | Contribution
77 | ------------
78 |
79 | - Use PHPCS fixer and PHPStan
80 | - `composer lint`
81 | - Update tests (PHPUnit)
82 | - `composer test`
83 |
84 |
--------------------------------------------------------------------------------
/tests/Aggregation/AvgAggregationTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
18 | 'avg' => [
19 | 'field' => 'avg_price',
20 | ],
21 | ], $query->build());
22 | }
23 |
24 | public function testItBuildTheAggregationUsingAField(): void
25 | {
26 | $query = new AvgAggregation('avg_price');
27 | $query->setField('price');
28 |
29 | $this->assertEquals([
30 | 'avg' => [
31 | 'field' => 'price',
32 | ],
33 | ], $query->build());
34 | }
35 |
36 | public function testItBuildTheAggregationUsingAScript(): void
37 | {
38 | $query = new AvgAggregation('avg_price', new SourceScript('doc.price.value'));
39 |
40 | $this->assertEquals([
41 | 'avg' => [
42 | 'script' => [
43 | 'source' => 'doc.price.value',
44 | ],
45 | ],
46 | ], $query->build());
47 | }
48 |
49 | public function testItBuildTheAggregationUsingAScriptViaSet(): void
50 | {
51 | $query = new AvgAggregation('avg_price');
52 | $query->setScript('doc.price.value');
53 |
54 | $this->assertEquals([
55 | 'avg' => [
56 | 'script' => [
57 | 'source' => 'doc.price.value',
58 | ],
59 | ],
60 | ], $query->build());
61 | }
62 |
63 | public function testItBuildTheAggregationWithMissingValue(): void
64 | {
65 | $query = new AvgAggregation('avg_price');
66 | $query->setField('price');
67 | $query->setMissing(10);
68 |
69 | $this->assertEquals([
70 | 'avg' => [
71 | 'field' => 'price',
72 | 'missing' => 10,
73 | ],
74 | ], $query->build());
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/Aggregation/MaxAggregationTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
18 | 'max' => [
19 | 'field' => 'max_price',
20 | ],
21 | ], $query->build());
22 | }
23 |
24 | public function testItBuildTheAggregationUsingAField(): void
25 | {
26 | $query = new MaxAggregation('max_price');
27 | $query->setField('price');
28 |
29 | $this->assertEquals([
30 | 'max' => [
31 | 'field' => 'price',
32 | ],
33 | ], $query->build());
34 | }
35 |
36 | public function testItBuildTheAggregationUsingAScript(): void
37 | {
38 | $query = new MaxAggregation('max_price', new SourceScript('doc.price.value'));
39 |
40 | $this->assertEquals([
41 | 'max' => [
42 | 'script' => [
43 | 'source' => 'doc.price.value',
44 | ],
45 | ],
46 | ], $query->build());
47 | }
48 |
49 | public function testItBuildTheAggregationUsingAScriptViaSet(): void
50 | {
51 | $query = new MaxAggregation('max_price');
52 | $query->setScript('doc.price.value');
53 |
54 | $this->assertEquals([
55 | 'max' => [
56 | 'script' => [
57 | 'source' => 'doc.price.value',
58 | ],
59 | ],
60 | ], $query->build());
61 | }
62 |
63 | public function testItBuildTheAggregationWithMissingValue(): void
64 | {
65 | $query = new MaxAggregation('max_price');
66 | $query->setField('price');
67 | $query->setMissing(10);
68 |
69 | $this->assertEquals([
70 | 'max' => [
71 | 'field' => 'price',
72 | 'missing' => 10,
73 | ],
74 | ], $query->build());
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/Query/FunctionScoreQueryTest.php:
--------------------------------------------------------------------------------
1 | [
20 | 'query' => [
21 | 'query_string' => [
22 | 'query' => '(*name*) OR name',
23 | 'fields' => ['column1', 'column2'],
24 | ],
25 | ],
26 | ],
27 | ];
28 |
29 | $this->assertEquals($response, $functionScoreQuery->build());
30 | }
31 |
32 | public function testBuildFunctionScoreQuerySetParams(): void
33 | {
34 | $fields = ['column1', 'column2'];
35 | $query = '(*name*) OR name';
36 |
37 | $functionScoreQuery = new FunctionScoreQuery($fields, $query);
38 | $functionScoreQuery->setBoostMode('multiply');
39 | $functionScoreQuery->setFunctions($this->functions());
40 |
41 | $response = [
42 | 'function_score' => [
43 | 'boost_mode' => 'multiply',
44 | 'functions' => [
45 | 'filter' => [
46 | 'term' => [
47 | '_index' => 'column2',
48 | ],
49 | ],
50 | 'weight' => 2.50,
51 | ],
52 | 'query' => [
53 | 'query_string' => [
54 | 'query' => '(*name*) OR name',
55 | 'fields' => ['column1', 'column2'],
56 | ],
57 | ],
58 | ],
59 | ];
60 |
61 | $this->assertEquals($response, $functionScoreQuery->build());
62 | }
63 |
64 | private function functions(): array
65 | {
66 | $functions = new FunctionsQuery('column2');
67 | $functions->setWeight(2.50);
68 |
69 | return $functions->build();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/Aggregation/DateHistogramAggregationTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
17 | 'date_histogram' => [
18 | 'field' => 'price_evolution',
19 | 'calendar_interval' => '1d',
20 | ],
21 | ], $aggregation->build());
22 | }
23 |
24 | public function testWithDifferentName(): void
25 | {
26 | $aggregation = new DateHistogramAggregation('price_evolution', '1d', 'price');
27 |
28 | $this->assertEquals([
29 | 'date_histogram' => [
30 | 'field' => 'price',
31 | 'calendar_interval' => '1d',
32 | ],
33 | ], $aggregation->build());
34 | }
35 |
36 | public function testItBuildTheAggregationWithSet(): void
37 | {
38 | $aggregation = new DateHistogramAggregation('price_evolution', '1d', 'price');
39 |
40 | $aggregation->setField('price2');
41 | $aggregation->setCalendarInterval('2d');
42 |
43 | $this->assertEquals([
44 | 'date_histogram' => [
45 | 'field' => 'price2',
46 | 'calendar_interval' => '2d',
47 | ],
48 | ], $aggregation->build());
49 | }
50 |
51 | public function testWithExtendedBounds(): void
52 | {
53 | $nameAndField = 'per_day';
54 | $calendarInterval = 'day';
55 | $field = 'date';
56 | $min = '2022-01-10';
57 | $max = '2022-01-20';
58 |
59 | $aggregation = new DateHistogramAggregation($nameAndField, $calendarInterval, $field, [], $min, $max);
60 |
61 | $this->assertEquals([
62 | 'date_histogram' => [
63 | 'field' => $field,
64 | 'calendar_interval' => $calendarInterval,
65 | 'extended_bounds' => [
66 | 'min' => $min,
67 | 'max' => $max,
68 | ],
69 | ],
70 | ], $aggregation->build());
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Query/MultiMatchQuery.php:
--------------------------------------------------------------------------------
1 | operator = $operator;
32 | $this->boost = $boost;
33 | $this->minimumShouldMatch = $minimumShouldMatch;
34 | $this->fuzziness = $fuzziness;
35 | }
36 |
37 | public function setFields(array $fields): self
38 | {
39 | $this->fields = $fields;
40 |
41 | return $this;
42 | }
43 |
44 | public function setQuery(string $query): self
45 | {
46 | $this->query = $query;
47 |
48 | return $this;
49 | }
50 |
51 | public function setType(string $type): self
52 | {
53 | $this->type = $type;
54 |
55 | return $this;
56 | }
57 |
58 | public function setParams(array $params): self
59 | {
60 | $this->params = $params;
61 |
62 | return $this;
63 | }
64 |
65 | public function build(): array
66 | {
67 | $data = [
68 | 'query' => $this->query,
69 | 'fields' => $this->fields,
70 | ];
71 |
72 | if (null !== $this->type) {
73 | $data['type'] = $this->type;
74 | }
75 |
76 | $this->buildOperatorTo($data);
77 | $this->buildBoostTo($data);
78 | $this->buildMinimumShouldMatchTo($data);
79 | $this->buildFuzzinessTo($data);
80 |
81 | $build = $this->params;
82 | $build['multi_match'] = $data;
83 |
84 | return $build;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/tests/Query/GeoBoundingBoxQueryTest.php:
--------------------------------------------------------------------------------
1 | expectExceptionMessage('GeoBoundingBoxQuery needs at least 2 sides set');
16 | (new GeoBoundingBoxQuery('test'))->build();
17 | }
18 |
19 | public function testBuildFailsOnOneFilter(): void
20 | {
21 | $this->expectExceptionMessage('GeoBoundingBoxQuery needs at least 2 sides set');
22 | (new GeoBoundingBoxQuery(field: 'test', topLeft: new GpsPointEntity(1.1, 2.1)))->build();
23 | }
24 |
25 | public function testBuildTopLeftBottomRight(): void
26 | {
27 | $result = (new GeoBoundingBoxQuery(
28 | field: 'test',
29 | topLeft: new GpsPointEntity(1.1, 2.1),
30 | bottomRight: new GpsPointEntity(2.1, 3.1),
31 | ))->build();
32 |
33 | $this->assertEquals([
34 | 'geo_bounding_box' => [
35 | 'test' => [
36 | 'top_left' => [
37 | 'lat' => 1.1,
38 | 'lon' => 2.1,
39 | ],
40 | 'bottom_right' => [
41 | 'lat' => 2.1,
42 | 'lon' => 3.1,
43 | ],
44 | ],
45 | ],
46 | ], $result);
47 | }
48 |
49 | public function testBuildTopRightBottomLeft(): void
50 | {
51 | $result = (new GeoBoundingBoxQuery(
52 | field: 'test',
53 | topRight: new GpsPointEntity(1.1, 2.1),
54 | bottomLeft: new GpsPointEntity(2.1, 3.1),
55 | ))->build();
56 |
57 | $this->assertEquals([
58 | 'geo_bounding_box' => [
59 | 'test' => [
60 | 'top_right' => [
61 | 'lat' => 1.1,
62 | 'lon' => 2.1,
63 | ],
64 | 'bottom_left' => [
65 | 'lat' => 2.1,
66 | 'lon' => 3.1,
67 | ],
68 | ],
69 | ],
70 | ], $result);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Aggregation/RangesAggregation.php:
--------------------------------------------------------------------------------
1 | $ranges pass desired ranges that will be converted to
15 | * linear range
16 | * @param array $aggregations
17 | * @param bool $equalConditionOnToRange Set to true if you want to do a histogram with 0
18 | * - 10, 10 - 15, and correctly count the number
19 | * (entry with 10 will be in first and seconds
20 | * segment
21 | */
22 | public function __construct(
23 | string $name,
24 | string $field,
25 | private array $ranges,
26 | array $aggregations = [],
27 | private bool $equalConditionOnToRange = false
28 | ) {
29 | parent::__construct($name, $aggregations);
30 | $this->field = $field;
31 | }
32 |
33 | public function getRanges(): array
34 | {
35 | return $this->ranges;
36 | }
37 |
38 | public function setRanges(array $ranges): self
39 | {
40 | $this->ranges = $ranges;
41 |
42 | return $this;
43 | }
44 |
45 | protected function getType(): string
46 | {
47 | return 'range';
48 | }
49 |
50 | protected function buildAggregation(): array
51 | {
52 | $ranges = [];
53 | $prevValue = 0;
54 | foreach ($this->ranges as $range) {
55 | $to = $range + ($this->equalConditionOnToRange ? 1 : 0); // To value is not included - increase it by 1
56 | $ranges[] = [
57 | 'from' => $prevValue,
58 | 'to' => $to,
59 | 'key' => $range,
60 | ];
61 | $prevValue = $range; // Do not use increased value
62 | }
63 |
64 | // Append "others"
65 | $ranges[] = [
66 | 'key' => $prevValue.'-*',
67 | 'from' => $prevValue,
68 | ];
69 |
70 | return [
71 | 'field' => $this->field,
72 | 'ranges' => $ranges,
73 | ];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Aggregation/MetricAggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
19 | */
20 | public function __construct(
21 | string $nameAndField,
22 | string|SourceScript|Field|null $fieldOrSource = null,
23 | array $aggregations = []
24 | ) {
25 | parent::__construct($nameAndField, $aggregations);
26 |
27 | if (null === $fieldOrSource) {
28 | $fieldOrSource = $nameAndField;
29 | }
30 |
31 | if (is_string($fieldOrSource)) {
32 | $this->field = new Field($fieldOrSource);
33 | } elseif ($fieldOrSource instanceof Field) {
34 | $this->field = $fieldOrSource;
35 | } elseif ($fieldOrSource instanceof SourceScript) {
36 | $this->script = $fieldOrSource;
37 | } else {
38 | throw new QueryException('Invalid field or source argument in metric aggregation');
39 | }
40 | }
41 |
42 | public function setField(string|Field $field): self
43 | {
44 | $this->script = null;
45 | $this->field = is_string($field) ? new Field($field) : $field;
46 |
47 | return $this;
48 | }
49 |
50 | public function setScript(string|SourceScript $script): self
51 | {
52 | $this->field = null;
53 | $this->script = is_string($script) ? new SourceScript($script) : $script;
54 |
55 | return $this;
56 | }
57 |
58 | public function getField(): ?Field
59 | {
60 | return $this->field;
61 | }
62 |
63 | public function setMissing(?int $missing): self
64 | {
65 | $this->missing = $missing;
66 |
67 | return $this;
68 | }
69 |
70 | protected function buildAggregation(): array
71 | {
72 | $term = [];
73 | if (null !== $this->script) {
74 | $term = $this->script->build();
75 | } elseif (null !== $this->field) {
76 | $term = $this->field->build();
77 | }
78 |
79 | if (null !== $this->missing) {
80 | $term['missing'] = $this->missing;
81 | }
82 |
83 | return $term;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Query/RangeQuery.php:
--------------------------------------------------------------------------------
1 | field = $field;
28 | $this->format = $format;
29 | $this->boost = $boost;
30 | }
31 |
32 | public function gt(int|float|string|null $value): self
33 | {
34 | $this->gt = $value;
35 |
36 | return $this;
37 | }
38 |
39 | public function lt(int|float|string|null $value): self
40 | {
41 | $this->lt = $value;
42 |
43 | return $this;
44 | }
45 |
46 | public function gte(int|float|string|null $value): self
47 | {
48 | $this->gte = $value;
49 |
50 | return $this;
51 | }
52 |
53 | public function lte(int|float|string|null $value): self
54 | {
55 | $this->lte = $value;
56 |
57 | return $this;
58 | }
59 |
60 | public function setParams(array $params): self
61 | {
62 | $this->params = $params;
63 |
64 | return $this;
65 | }
66 |
67 | public function build(): array
68 | {
69 | $query = $this->params;
70 |
71 | if (null !== $this->gt) {
72 | $query['gt'] = $this->gt;
73 | }
74 |
75 | if (null !== $this->lt) {
76 | $query['lt'] = $this->lt;
77 | }
78 |
79 | if (null !== $this->gte) {
80 | $query['gte'] = $this->gte;
81 | }
82 |
83 | if (null !== $this->lte) {
84 | $query['lte'] = $this->lte;
85 | }
86 |
87 | if (empty($query)) {
88 | throw new QueryException('Empty RangeQuery');
89 | }
90 |
91 | $this->buildFormatTo($query);
92 | $this->buildBoostTo($query);
93 |
94 | return [
95 | 'range' => [
96 | $this->field => $query,
97 | ],
98 | ];
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/Query/QueryStringQuery.php:
--------------------------------------------------------------------------------
1 | boost = $boost;
30 | $this->minimumShouldMatch = $minimumShouldMatch;
31 | $this->rewrite = $rewrite;
32 | $this->fuzziness = $fuzziness;
33 | }
34 |
35 | public function setQuery(string $query): self
36 | {
37 | $this->query = $query;
38 |
39 | return $this;
40 | }
41 |
42 | public function setDefaultField(?string $defaultField): self
43 | {
44 | $this->defaultField = $defaultField;
45 |
46 | return $this;
47 | }
48 |
49 | public function setDefaultOperator(?string $defaultOperator): self
50 | {
51 | $this->defaultOperator = $defaultOperator;
52 |
53 | return $this;
54 | }
55 |
56 | public function setFields(?array $fields): self
57 | {
58 | $this->fields = $fields;
59 |
60 | return $this;
61 | }
62 |
63 | public function setParams(array $params): self
64 | {
65 | $this->params = $params;
66 |
67 | return $this;
68 | }
69 |
70 | public function build(): array
71 | {
72 | $build = $this->params;
73 | $build['query'] = $this->query;
74 |
75 | if (null !== $this->defaultField) {
76 | $build['default_field'] = $this->defaultField;
77 | }
78 |
79 | if (null !== $this->defaultOperator) {
80 | $build['default_operator'] = $this->defaultOperator;
81 | }
82 |
83 | if (null !== $this->fields) {
84 | $build['fields'] = $this->fields;
85 | }
86 |
87 | $this->buildBoostTo($build);
88 | $this->buildMinimumShouldMatchTo($build);
89 | $this->buildRewriteTo($build);
90 | $this->buildFuzzinessTo($build);
91 |
92 | return [
93 | 'query_string' => $build,
94 | ];
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/QueryBuilderTest.php:
--------------------------------------------------------------------------------
1 | setSource(false);
17 |
18 | $query = $queryBuilder->build();
19 |
20 | $this->assertFalse($query['_source']);
21 | }
22 |
23 | public function testItSetTheSource(): void
24 | {
25 | $queryBuilder = new QueryBuilder();
26 |
27 | $queryBuilder->setSource('obj.*');
28 |
29 | $query = $queryBuilder->build();
30 |
31 | $this->assertEquals('obj.*', $query['_source']);
32 | }
33 |
34 | public function testItSetTheIndex(): void
35 | {
36 | $queryBuilder = new QueryBuilder();
37 |
38 | $queryBuilder->setIndex('index1');
39 |
40 | $query = $queryBuilder->build();
41 |
42 | $this->assertEquals('index1', $query['index']);
43 | }
44 |
45 | public function testItSetTheSize(): void
46 | {
47 | $queryBuilder = new QueryBuilder();
48 |
49 | $queryBuilder->setSize(50);
50 |
51 | $query = $queryBuilder->build();
52 |
53 | $this->assertEquals(50, $query['size']);
54 | }
55 |
56 | public function testItSetTheFrom(): void
57 | {
58 | $queryBuilder = new QueryBuilder();
59 |
60 | $queryBuilder->setFrom(50);
61 |
62 | $query = $queryBuilder->build();
63 |
64 | $this->assertEquals(50, $query['from']);
65 | }
66 |
67 | public function testItSetThePitAsString(): void
68 | {
69 | $queryBuilder = new QueryBuilder();
70 |
71 | $queryBuilder->setPit('pit-as-string');
72 |
73 | $query = $queryBuilder->build();
74 |
75 | $this->assertEquals(['id' => 'pit-as-string'], $query['body']['pit']);
76 | }
77 |
78 | public function testItSetThePitAsArray(): void
79 | {
80 | $queryBuilder = new QueryBuilder();
81 |
82 | $queryBuilder->setPit(['id' => 'pit-as-array', 'keep_alive' => '1m']);
83 |
84 | $query = $queryBuilder->build();
85 |
86 | $this->assertEquals(['id' => 'pit-as-array', 'keep_alive' => '1m'], $query['body']['pit']);
87 | }
88 |
89 | public function testItAllowToSort(): void
90 | {
91 | $queryBuilder = new QueryBuilder();
92 |
93 | $queryBuilder->addSort('field', [
94 | 'order' => 'desc',
95 | ]);
96 | $queryBuilder->addSort('field2', [
97 | 'order' => 'asc',
98 | ]);
99 |
100 | $query = $queryBuilder->build();
101 |
102 | $this->assertEquals([
103 | 'field' => [
104 | 'order' => 'desc',
105 | ],
106 | 'field2' => [
107 | 'order' => 'asc',
108 | ],
109 | ], $query['body']['sort']);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/tests/Aggregation/CardinalityAggregationTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
18 | 'cardinality' => [
19 | 'field' => 'city',
20 | ],
21 | ], $query->build());
22 | }
23 |
24 | public function testItBuildTheAggregationUsingAFieldViaSet(): void
25 | {
26 | $query = new CardinalityAggregation('city');
27 | $query->setScript('test2');
28 | $query->setField('test');
29 |
30 | $this->assertEquals([
31 | 'cardinality' => [
32 | 'field' => 'test',
33 | ],
34 | ], $query->build());
35 | }
36 |
37 | public function testItBuildTheAggregationUsingAScriptViaSet(): void
38 | {
39 | $query = new CardinalityAggregation('city');
40 | $query->setScript('doc.city.value');
41 |
42 | $this->assertEquals([
43 | 'cardinality' => [
44 | 'script' => [
45 | 'source' => 'doc.city.value',
46 | ],
47 | ],
48 | ], $query->build());
49 | }
50 |
51 | public function testItBuildTheAggregationUsingAScript(): void
52 | {
53 | $query = new CardinalityAggregation('city', new SourceScript('asd'));
54 |
55 | $this->assertEquals([
56 | 'cardinality' => [
57 | 'script' => [
58 | 'source' => 'asd',
59 | ],
60 | ],
61 | ], $query->build());
62 | }
63 |
64 | public function testItBuildTheAggregationUsingAFieldAndDifferentName(): void
65 | {
66 | $query = new CardinalityAggregation('city', 'test');
67 |
68 | $this->assertEquals([
69 | 'cardinality' => [
70 | 'field' => 'test',
71 | ],
72 | ], $query->build());
73 | }
74 |
75 | public function testItBuildTheAggregationWithMissingValue(): void
76 | {
77 | $query = new CardinalityAggregation('city');
78 | $query->setField('city');
79 | $query->setMissing(10);
80 |
81 | $this->assertEquals([
82 | 'cardinality' => [
83 | 'field' => 'city',
84 | 'missing' => 10,
85 | ],
86 | ], $query->build());
87 | }
88 |
89 | public function testItBuildTheAggregationWithAPrecisionThreshold(): void
90 | {
91 | $query = new CardinalityAggregation('city');
92 | $query->setField('city');
93 | $query->setPrecisionThreshold(100);
94 |
95 | $this->assertEquals([
96 | 'cardinality' => [
97 | 'field' => 'city',
98 | 'precision_threshold' => 100,
99 | ],
100 | ], $query->build());
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Aggregation/TermsAggregation.php:
--------------------------------------------------------------------------------
1 | field = new Field($fieldOrSource);
36 | } elseif ($fieldOrSource instanceof Field) {
37 | $this->field = $fieldOrSource;
38 | } elseif ($fieldOrSource instanceof InlineScript) {
39 | $this->script = $fieldOrSource;
40 | } else {
41 | throw new QueryException('Invalid field or source argument in metric aggregation');
42 | }
43 | }
44 |
45 | public function setSize(int $size): self
46 | {
47 | $this->size = $size;
48 |
49 | return $this;
50 | }
51 |
52 | public function setOrder(string|null $orderField, string $orderValue = SortDirections::ASC): self
53 | {
54 | $this->orderField = $orderField;
55 | $this->orderValue = $orderValue;
56 |
57 | return $this;
58 | }
59 |
60 | public function setInclude(array|string|null $include): self
61 | {
62 | $this->include = $include;
63 |
64 | return $this;
65 | }
66 |
67 | public function setExclude(array|string|null $exclude): self
68 | {
69 | $this->exclude = $exclude;
70 |
71 | return $this;
72 | }
73 |
74 | protected function buildAggregation(): array
75 | {
76 | $build = [];
77 | if (null !== $this->script) {
78 | $build = [
79 | 'script' => $this->script->build(),
80 | ];
81 | } elseif (null !== $this->field) {
82 | $build = $this->field->build() + [
83 | 'size' => $this->size,
84 | ];
85 | }
86 |
87 | if (null !== $this->orderField) {
88 | $build['order'] = [
89 | $this->orderField => $this->orderValue,
90 | ];
91 | }
92 |
93 | if (null !== $this->include) {
94 | $build['include'] = $this->include;
95 | }
96 |
97 | if (null !== $this->exclude) {
98 | $build['exclude'] = $this->exclude;
99 | }
100 |
101 | return $build;
102 | }
103 |
104 | protected function getType(): string
105 | {
106 | return 'terms';
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/Query/BoolQuery.php:
--------------------------------------------------------------------------------
1 | $must
12 | * @param array $mustNot
13 | * @param array $should
14 | * @param array $filter
15 | */
16 | public function __construct(
17 | private array $must = [],
18 | private array $mustNot = [],
19 | private array $should = [],
20 | private array $filter = [],
21 | private array $params = [],
22 | ) {}
23 |
24 | public function addMust(QueryInterface $query): self
25 | {
26 | if ($query === $this) {
27 | throw new QueryException('You are trying to add self to a bool query');
28 | }
29 |
30 | $this->must[] = $query;
31 |
32 | return $this;
33 | }
34 |
35 | public function addMustNot(QueryInterface $query): self
36 | {
37 | if ($query === $this) {
38 | throw new QueryException('You are trying to add self to a bool query');
39 | }
40 |
41 | $this->mustNot[] = $query;
42 |
43 | return $this;
44 | }
45 |
46 | public function addShould(QueryInterface $query): self
47 | {
48 | if ($query === $this) {
49 | throw new QueryException('You are trying to add self to a bool query');
50 | }
51 |
52 | $this->should[] = $query;
53 |
54 | return $this;
55 | }
56 |
57 | public function addFilter(QueryInterface $query): self
58 | {
59 | if ($query === $this) {
60 | throw new QueryException('You are trying to add self to a bool query');
61 | }
62 |
63 | $this->filter[] = $query;
64 |
65 | return $this;
66 | }
67 |
68 | public function isEmpty(): bool
69 | {
70 | return empty($this->must)
71 | && empty($this->mustNot)
72 | && empty($this->should)
73 | && empty($this->filter);
74 | }
75 |
76 | public function setParams(array $params): self
77 | {
78 | $this->params = $params;
79 |
80 | return $this;
81 | }
82 |
83 | public function build(): array
84 | {
85 | $query = $this->params;
86 |
87 | $this
88 | ->buildQueries($query, 'should', $this->should)
89 | ->buildQueries($query, 'filter', $this->filter)
90 | ->buildQueries($query, 'must_not', $this->mustNot)
91 | ->buildQueries($query, 'must', $this->must);
92 |
93 | if ((is_countable($query) ? count($query) : 0) === 0) {
94 | throw new QueryException('Empty BoolQuery');
95 | }
96 |
97 | return [
98 | 'bool' => $query,
99 | ];
100 | }
101 |
102 | /**
103 | * @param array $queries
104 | *
105 | * @return $this
106 | */
107 | protected function buildQueries(array &$query, string $name, array $queries): self
108 | {
109 | if ([] === $queries) {
110 | return $this;
111 | }
112 |
113 | $query[$name] = [];
114 |
115 | foreach ($queries as $filter) {
116 | $query[$name][] = $filter->build();
117 | }
118 |
119 | return $this;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/Query/Query.php:
--------------------------------------------------------------------------------
1 | $values
11 | */
12 | public static function terms(string $field, array $values): TermsQuery
13 | {
14 | return new TermsQuery($field, $values);
15 | }
16 |
17 | public static function term(string $field, string|int|float|bool $value): TermQuery
18 | {
19 | return new TermQuery($field, $value);
20 | }
21 |
22 | public static function wildcard(string $field, string $value): WildcardQuery
23 | {
24 | return new WildcardQuery($field, $value);
25 | }
26 |
27 | /**
28 | * @param array $must
29 | * @param array $mustNot
30 | * @param array $should
31 | * @param array $filter
32 | */
33 | public static function bool(
34 | array $must = [],
35 | array $mustNot = [],
36 | array $should = [],
37 | array $filter = [],
38 | ): BoolQuery {
39 | return new BoolQuery($must, $mustNot, $should, $filter);
40 | }
41 |
42 | public static function range(
43 | string $field,
44 | int|float|string|null $lt = null,
45 | int|float|string|null $gt = null,
46 | int|float|string|null $lte = null,
47 | int|float|string|null $gte = null,
48 | ): RangeQuery {
49 | return new RangeQuery($field, $lt, $gt, $lte, $gte);
50 | }
51 |
52 | public static function nested(?string $path, QueryInterface $query): NestedQuery
53 | {
54 | return new NestedQuery($path, $query);
55 | }
56 |
57 | public static function match(string $field, string $query): MatchQuery
58 | {
59 | return new MatchQuery($field, $query);
60 | }
61 |
62 | public static function matchPhrase(string $field, string $query): MatchPhraseQuery
63 | {
64 | return new MatchPhraseQuery($field, $query);
65 | }
66 |
67 | public static function matchPhrasePrefix(string $field, string $query): MatchPhrasePrefixQuery
68 | {
69 | return new MatchPhrasePrefixQuery($field, $query);
70 | }
71 |
72 | public static function multiMatch(array $fields, string $query): MultiMatchQuery
73 | {
74 | return new MultiMatchQuery($fields, $query);
75 | }
76 |
77 | public static function functionScoreQuery(array $fields, string $query): FunctionScoreQuery
78 | {
79 | return new FunctionScoreQuery($fields, $query);
80 | }
81 |
82 | public static function functionsQuery(string $field): FunctionsQuery
83 | {
84 | return new FunctionsQuery($field);
85 | }
86 |
87 | public static function GeoBoundingBoxQuery(string $field): GeoBoundingBoxQuery
88 | {
89 | return new GeoBoundingBoxQuery($field);
90 | }
91 |
92 | /**
93 | * @param float[]|int[] $position
94 | */
95 | public static function geoDistance(string $field, string $distance, array $position): GeoDistanceQuery
96 | {
97 | return new GeoDistanceQuery($distance, $field, $position);
98 | }
99 |
100 | /**
101 | * @param mixed[]|float[]|int[] $coordinates
102 | */
103 | public static function geoShape(string $field, string $type, array $coordinates): GeoShapeQuery
104 | {
105 | return new GeoShapeQuery($field, $type, $coordinates);
106 | }
107 |
108 | public static function prefix(string $field, string $value): PrefixQuery
109 | {
110 | return new PrefixQuery($field, $value);
111 | }
112 |
113 | public static function queryString(string $query, ?string $defaultField = null): QueryStringQuery
114 | {
115 | return new QueryStringQuery($query, $defaultField);
116 | }
117 |
118 | public static function rankFeature(string $field): RankFeatureQuery
119 | {
120 | return new RankFeatureQuery($field);
121 | }
122 |
123 | public static function exists(string $field): ExistsQuery
124 | {
125 | return new ExistsQuery($field);
126 | }
127 |
128 | /**
129 | * @param mixed[]|string[] $fields
130 | */
131 | public static function simpleQueryString(array $fields, string $query): SimpleQueryStringQuery
132 | {
133 | return new SimpleQueryStringQuery($fields, $query);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/tests/Query/BoolQueryTest.php:
--------------------------------------------------------------------------------
1 | expectException(QueryException::class);
19 |
20 | $boolQuery->build();
21 | }
22 |
23 | public function testAddFilterWithSameObject(): void
24 | {
25 | $this->expectExceptionMessage('You are trying to add self to a bool query');
26 | $boolQuery = new BoolQuery();
27 |
28 | $boolQuery->addFilter($boolQuery);
29 | $boolQuery->build();
30 | }
31 |
32 | public function testAddShouldWithSameObject(): void
33 | {
34 | $this->expectExceptionMessage('You are trying to add self to a bool query');
35 | $boolQuery = new BoolQuery();
36 |
37 | $boolQuery->addShould($boolQuery);
38 | $boolQuery->build();
39 | }
40 |
41 | public function testAddMustNotWithSameObject(): void
42 | {
43 | $this->expectExceptionMessage('You are trying to add self to a bool query');
44 | $boolQuery = new BoolQuery();
45 |
46 | $boolQuery->addMustNot($boolQuery);
47 | $boolQuery->build();
48 | }
49 |
50 | public function testAddMustWithSameObject(): void
51 | {
52 | $this->expectExceptionMessage('You are trying to add self to a bool query');
53 | $boolQuery = new BoolQuery();
54 |
55 | $boolQuery->addMust($boolQuery);
56 | $boolQuery->build();
57 | }
58 |
59 | public function testItAddAMustClause(): void
60 | {
61 | $boolQuery = new BoolQuery();
62 |
63 | $boolQuery->addMust(Query::term('field', 'value'));
64 |
65 | $query = $boolQuery->build();
66 |
67 | $this->assertEquals([
68 | 'bool' => [
69 | 'must' => [
70 | [
71 | 'term' => [
72 | 'field' => [
73 | 'value' => 'value',
74 | ],
75 | ],
76 | ],
77 | ],
78 | ],
79 | ], $query);
80 | }
81 |
82 | public function testItAddAMustNotClause(): void
83 | {
84 | $boolQuery = new BoolQuery();
85 |
86 | $boolQuery->addMustNot(Query::term('field', 'value'));
87 |
88 | $query = $boolQuery->build();
89 |
90 | $this->assertEquals([
91 | 'bool' => [
92 | 'must_not' => [
93 | [
94 | 'term' => [
95 | 'field' => [
96 | 'value' => 'value',
97 | ],
98 | ],
99 | ],
100 | ],
101 | ],
102 | ], $query);
103 | }
104 |
105 | public function testItAddAShouldClause(): void
106 | {
107 | $boolQuery = new BoolQuery();
108 |
109 | $boolQuery->addShould(Query::term('field', 'value'));
110 |
111 | $query = $boolQuery->build();
112 |
113 | $this->assertEquals([
114 | 'bool' => [
115 | 'should' => [
116 | [
117 | 'term' => [
118 | 'field' => [
119 | 'value' => 'value',
120 | ],
121 | ],
122 | ],
123 | ],
124 | ],
125 | ], $query);
126 | }
127 |
128 | public function testItAddAFilterClause(): void
129 | {
130 | $boolQuery = new BoolQuery();
131 |
132 | $boolQuery->addFilter(Query::term('field', 'value'));
133 |
134 | $query = $boolQuery->build();
135 |
136 | $this->assertEquals([
137 | 'bool' => [
138 | 'filter' => [
139 | [
140 | 'term' => [
141 | 'field' => [
142 | 'value' => 'value',
143 | ],
144 | ],
145 | ],
146 | ],
147 | ],
148 | ], $query);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/tests/Query/SimpleQueryStringQueryTest.php:
--------------------------------------------------------------------------------
1 | assertEquals([
31 | 'simple_query_string' =>
32 | [
33 | 'query' => '~brown fox',
34 | 'fields' =>
35 | [
36 | 'subject',
37 | 'body',
38 | ],
39 | 'flags' => 'ALL',
40 | 'fuzzy_transpositions' => true,
41 | 'fuzzy_max_expansions' => 50,
42 | 'fuzzy_prefix_length' => 0,
43 | 'default_operator' => 'or',
44 | 'analyzer' => 'standard',
45 | 'lenient' => false,
46 | 'quote_field_suffix' => '',
47 | 'analyze_wildcard' => false,
48 | 'auto_generate_synonyms_phrase_query' => true,
49 | 'minimum_should_match' => '1%',
50 | ],
51 | ], $query->build());
52 | }
53 |
54 | public function testItBuildTheQueryWithAFuzziness(): void
55 | {
56 | $query = new SimpleQueryStringQuery(
57 | ['subject', 'body'],
58 | '~brown fox',
59 | 'ALL',
60 | true,
61 | 50,
62 | 0,
63 | "1%",
64 | "or",
65 | "standard",
66 | false,
67 | "",
68 | false,
69 | true
70 |
71 | );
72 | $query->setDefaultOperator('and');
73 | $this->assertEquals([
74 | 'simple_query_string' =>
75 | [
76 | 'query' => '~brown fox',
77 | 'fields' =>
78 | [
79 | 'subject',
80 | 'body',
81 | ],
82 | 'flags' => 'ALL',
83 | 'fuzzy_transpositions' => true,
84 | 'fuzzy_max_expansions' => 50,
85 | 'fuzzy_prefix_length' => 0,
86 | 'default_operator' => 'and',
87 | 'analyzer' => 'standard',
88 | 'lenient' => false,
89 | 'quote_field_suffix' => '',
90 | 'analyze_wildcard' => false,
91 | 'auto_generate_synonyms_phrase_query' => true,
92 | 'minimum_should_match' => '1%',
93 | ],
94 | ], $query->build());
95 | }
96 |
97 | public function testItBuildTheQueryWithBoost(): void
98 | {
99 | $query = new SimpleQueryStringQuery(
100 | ['subject', 'body'],
101 | '~brown fox',
102 | 'ALL',
103 | true,
104 | 50,
105 | 0,
106 | "1%",
107 | "or",
108 | "standard",
109 | false,
110 | "",
111 | false,
112 | true
113 |
114 | );
115 | $query->setBoost(3);
116 |
117 | $this->assertEquals([
118 | 'simple_query_string' =>
119 | [
120 | 'query' => '~brown fox',
121 | 'fields' =>
122 | [
123 | 'subject',
124 | 'body',
125 | ],
126 | 'flags' => 'ALL',
127 | 'fuzzy_transpositions' => true,
128 | 'fuzzy_max_expansions' => 50,
129 | 'fuzzy_prefix_length' => 0,
130 | 'default_operator' => 'or',
131 | 'analyzer' => 'standard',
132 | 'lenient' => false,
133 | 'quote_field_suffix' => '',
134 | 'analyze_wildcard' => false,
135 | 'auto_generate_synonyms_phrase_query' => true,
136 | 'minimum_should_match' => '1%',
137 | 'boost' => 3,
138 | ],
139 | ], $query->build());
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/Aggregation/Aggregation.php:
--------------------------------------------------------------------------------
1 | $aggregations
14 | */
15 | public static function terms(
16 | string $name,
17 | string|Field|InlineScript $fieldOrSource,
18 | array $aggregations = [],
19 | ): TermsAggregation {
20 | return new TermsAggregation($name, $fieldOrSource, $aggregations);
21 | }
22 |
23 | /**
24 | * @param array $aggregations
25 | */
26 | public static function dateHistogram(
27 | string $nameAndField,
28 | string $calendarInterval,
29 | ?string $field = null,
30 | array $aggregations = [],
31 | ): DateHistogramAggregation {
32 | return new DateHistogramAggregation($nameAndField, $calendarInterval, $field, $aggregations);
33 | }
34 |
35 | /**
36 | * @param array $aggregations
37 | */
38 | public static function nested(string $name, string $path, array $aggregations = []): NestedAggregation
39 | {
40 | return new NestedAggregation($name, $path, $aggregations);
41 | }
42 |
43 | /**
44 | * @param array $aggregations
45 | */
46 | public static function reverseNested(string $name, ?string $path = null, array $aggregations = []): ReverseNestedAggregation
47 | {
48 | return new ReverseNestedAggregation($name, $path, $aggregations);
49 | }
50 |
51 | /**
52 | * @param array $aggregations
53 | */
54 | public static function filter(string $name, QueryInterface $query, array $aggregations = []): FilterAggregation
55 | {
56 | return new FilterAggregation($name, $query, $aggregations);
57 | }
58 |
59 | /**
60 | * @param array $aggregations
61 | */
62 | public static function cardinality(
63 | string $nameAndField,
64 | string|SourceScript|Field|null $fieldOrSource = null,
65 | array $aggregations = [],
66 | ): CardinalityAggregation {
67 | return new CardinalityAggregation($nameAndField, $fieldOrSource, $aggregations);
68 | }
69 |
70 | /**
71 | * @param array $aggregations
72 | */
73 | public static function max(
74 | string $nameAndField,
75 | string|SourceScript|Field|null $fieldOrSource = null,
76 | array $aggregations = [],
77 | ): MaxAggregation {
78 | return new MaxAggregation($nameAndField, $fieldOrSource, $aggregations);
79 | }
80 |
81 | /**
82 | * @param array $aggregations
83 | */
84 | public static function min(
85 | string $nameAndField,
86 | string|SourceScript|Field|null $fieldOrSource = null,
87 | array $aggregations = [],
88 | ): MinAggregation {
89 | return new MinAggregation($nameAndField, $fieldOrSource, $aggregations);
90 | }
91 |
92 | /**
93 | * @param array $aggregations
94 | */
95 | public static function sum(
96 | string $nameAndField,
97 | string|SourceScript|Field|null $fieldOrSource = null,
98 | array $aggregations = [],
99 | ): SumAggregation {
100 | return new SumAggregation($nameAndField, $fieldOrSource, $aggregations);
101 | }
102 |
103 | /**
104 | * @param array $aggregations
105 | */
106 | public static function topHits(string $name, array $aggregations = []): TopHitsAggregation
107 | {
108 | return new TopHitsAggregation($name, $aggregations);
109 | }
110 |
111 | public static function histogram(string $name, string $field, int $interval): HistogramAggregation
112 | {
113 | return new HistogramAggregation($name, $field, $interval);
114 | }
115 |
116 | /**
117 | * @param array $ranges pass desired ranges that will be converted to linear range
118 | */
119 | public static function ranges(string $name, string $field, array $ranges): RangesAggregation
120 | {
121 | return new RangesAggregation($name, $field, $ranges);
122 | }
123 |
124 | public static function widthHistogram(string $name, string $field, int $buckets): WidthHistogramAggregation
125 | {
126 | return new WidthHistogramAggregation($name, $field, $buckets);
127 | }
128 |
129 | public static function stats(string $name): StatsAggregation
130 | {
131 | return new StatsAggregation($name);
132 | }
133 |
134 | public static function valueCount(string $name): ValueCountAggregation
135 | {
136 | return new ValueCountAggregation($name);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/QueryBuilder.php:
--------------------------------------------------------------------------------
1 | source = $source;
41 |
42 | return $this;
43 | }
44 |
45 | public function setIndex(string $index): self
46 | {
47 | $this->index = $index;
48 |
49 | return $this;
50 | }
51 |
52 | public function setFrom(int $from): self
53 | {
54 | $this->from = $from;
55 |
56 | return $this;
57 | }
58 |
59 | public function setSize(int $size): self
60 | {
61 | $this->size = $size;
62 |
63 | return $this;
64 | }
65 |
66 | public function setPit(string|array|null $pit): self
67 | {
68 | if (is_array($pit) && !isset($pit['id'])) {
69 | throw new \InvalidArgumentException('Parameter "pit" is not valid. Value with key "id" is not set.');
70 | }
71 |
72 | $this->pit = $pit;
73 |
74 | return $this;
75 | }
76 |
77 | public function setSearchAfter(?array $searchAfter): self
78 | {
79 | $this->searchAfter = $searchAfter;
80 |
81 | return $this;
82 | }
83 |
84 | public function setQuery(QueryInterface $query): self
85 | {
86 | $this->query = $query;
87 |
88 | return $this;
89 | }
90 |
91 | public function setPostFilter(QueryInterface $query): self
92 | {
93 | $this->postFilter = $query;
94 |
95 | return $this;
96 | }
97 |
98 | public function setFields(?array $fields): self
99 | {
100 | $this->fields = $fields;
101 |
102 | return $this;
103 | }
104 |
105 | public function setHighlight(?array $highlight): self
106 | {
107 | $this->highlight = $highlight;
108 |
109 | return $this;
110 | }
111 |
112 | public function setParams(array $params): self
113 | {
114 | $this->params = $params;
115 |
116 | return $this;
117 | }
118 |
119 | public function build(): array
120 | {
121 | $query = $this->params;
122 | if (false === isset($query['body'])) {
123 | $query['body'] = [];
124 | }
125 |
126 | if (null !== $this->index) {
127 | $query['index'] = $this->index;
128 | }
129 |
130 | if (null !== $this->from) {
131 | $query['from'] = $this->from;
132 | }
133 |
134 | if (null !== $this->size) {
135 | $query['size'] = $this->size;
136 | }
137 |
138 | if (null !== $this->pit) {
139 | if (is_string($this->pit)) {
140 | $query['body']['pit'] = ['id' => $this->pit];
141 | } else {
142 | $query['body']['pit'] = $this->pit;
143 | }
144 | }
145 |
146 | if (null !== $this->searchAfter) {
147 | $query['body']['search_after'] = $this->searchAfter;
148 | }
149 |
150 | if (null !== $this->source) {
151 | $query['_source'] = $this->source;
152 | }
153 |
154 | if (null !== $this->query) {
155 | $query['body']['query'] = $this->query->build();
156 | }
157 |
158 | if (null !== $this->postFilter) {
159 | $query['body']['post_filter'] = $this->postFilter->build();
160 | }
161 |
162 | if (null !== $this->fields) {
163 | $query['body']['fields'] = $this->fields;
164 | }
165 |
166 | if (null !== $this->highlight) {
167 | $query['body']['highlight'] = $this->highlight;
168 | }
169 |
170 | $this->buildSortTo($query['body'])
171 | ->buildAggregationsTo($query['body'])
172 | ->buildCollapseTo($query['body']);
173 |
174 | return $query;
175 | }
176 |
177 | public function getSource(): bool|array|string|null
178 | {
179 | return $this->source;
180 | }
181 |
182 | public function getFrom(): ?int
183 | {
184 | return $this->from;
185 | }
186 |
187 | public function getSize(): ?int
188 | {
189 | return $this->size;
190 | }
191 |
192 | public function getPit(): string|array|null
193 | {
194 | return $this->pit;
195 | }
196 |
197 | public function getSearchAfter(): ?array
198 | {
199 | return $this->searchAfter;
200 | }
201 |
202 | public function getQuery(): ?QueryInterface
203 | {
204 | return $this->query;
205 | }
206 |
207 | public function getPostFilter(): ?QueryInterface
208 | {
209 | return $this->postFilter;
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/Query/SimpleQueryStringQuery.php:
--------------------------------------------------------------------------------
1 | minimumShouldMatch = $minimumShouldMatch;
34 | }
35 |
36 | public function setFlags(string|null $flags): self
37 | {
38 | $this->flags = $flags;
39 |
40 | return $this;
41 | }
42 |
43 | public function setFuzzyTranspositions(bool|null $fuzzyTranspositions): self
44 | {
45 | $this->fuzzyTranspositions = $fuzzyTranspositions;
46 |
47 | return $this;
48 | }
49 |
50 | public function setFuzzyMaxExpansions(int|null $fuzzyMaxExpansions): self
51 | {
52 | $this->fuzzyMaxExpansions = $fuzzyMaxExpansions;
53 |
54 | return $this;
55 | }
56 |
57 | public function setFuzzyPrefixLength(int|null $fuzzyPrefixLength): self
58 | {
59 | $this->fuzzyPrefixLength = $fuzzyPrefixLength;
60 |
61 | return $this;
62 | }
63 |
64 | public function setDefaultOperator(string|null $defaultOperator): self
65 | {
66 | $this->defaultOperator = $defaultOperator;
67 |
68 | return $this;
69 | }
70 |
71 | public function setAnalyzer(string|null $analyzer): self
72 | {
73 | $this->analyzer = $analyzer;
74 |
75 | return $this;
76 | }
77 |
78 | public function setLenient(bool|null $lenient): self
79 | {
80 | $this->lenient = $lenient;
81 |
82 | return $this;
83 | }
84 |
85 | public function setQuoteFieldSuffix(string|null $quoteFieldSuffix): self
86 | {
87 | $this->quoteFieldSuffix = $quoteFieldSuffix;
88 |
89 | return $this;
90 | }
91 |
92 | public function setAnalyzeWildCard(bool|null $analyzeWildCard): self
93 | {
94 | $this->analyzeWildCard = $analyzeWildCard;
95 |
96 | return $this;
97 | }
98 |
99 | public function setAutoGenerateSynonymsPhraseQuery(bool|null $autoGenerateSynonymsPhraseQuery): self
100 | {
101 | $this->autoGenerateSynonymsPhraseQuery = $autoGenerateSynonymsPhraseQuery;
102 |
103 | return $this;
104 | }
105 |
106 | public function setFields(array $fields): self
107 | {
108 | $this->fields = $fields;
109 |
110 | return $this;
111 | }
112 |
113 | public function setQuery(string $query): self
114 | {
115 | $this->query = $query;
116 |
117 | return $this;
118 | }
119 |
120 | public function setParams(array $params): self
121 | {
122 | $this->params = $params;
123 |
124 | return $this;
125 | }
126 |
127 | public function build(): array
128 | {
129 | $data = [
130 | 'query' => $this->query,
131 | 'fields' => $this->fields,
132 | ];
133 | if (null !== $this->flags) {
134 | $data['flags'] = $this->flags;
135 | }
136 | if (null !== $this->fuzzyTranspositions) {
137 | $data['fuzzy_transpositions'] = $this->fuzzyTranspositions;
138 | }
139 |
140 | if (null !== $this->fuzzyMaxExpansions) {
141 | $data['fuzzy_max_expansions'] = $this->fuzzyMaxExpansions;
142 | }
143 |
144 | if (null !== $this->fuzzyPrefixLength) {
145 | $data['fuzzy_prefix_length'] = $this->fuzzyPrefixLength;
146 | }
147 |
148 | if (null !== $this->defaultOperator) {
149 | $data['default_operator'] = $this->defaultOperator;
150 | }
151 |
152 | if (null !== $this->analyzer) {
153 | $data['analyzer'] = $this->analyzer;
154 | }
155 |
156 | if (null !== $this->lenient) {
157 | $data['lenient'] = $this->lenient;
158 | }
159 |
160 | if (null !== $this->quoteFieldSuffix) {
161 | $data['quote_field_suffix'] = $this->quoteFieldSuffix;
162 | }
163 |
164 | if (null !== $this->analyzeWildCard) {
165 | $data['analyze_wildcard'] = $this->analyzeWildCard;
166 | }
167 | if (null !== $this->autoGenerateSynonymsPhraseQuery) {
168 | $data['auto_generate_synonyms_phrase_query'] = $this->autoGenerateSynonymsPhraseQuery;
169 | }
170 |
171 | $this->buildMinimumShouldMatchTo($data);
172 | $this->buildBoostTo($data);
173 |
174 | $build = $this->params;
175 | $build['simple_query_string'] = $data;
176 |
177 | return $build;
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/tests/Aggregation/RangesAggregationTest.php:
--------------------------------------------------------------------------------
1 | [
22 | 'field' => 'option_236',
23 | 'ranges' => [
24 | 0 => [
25 | 'from' => 0,
26 | 'to' => 10,
27 | 'key' => 10,
28 | ],
29 | 1 => [
30 | 'from' => 10,
31 | 'to' => 50,
32 | 'key' => 50,
33 | ],
34 | 2 => [
35 | 'from' => 50,
36 | 'to' => 100,
37 | 'key' => 100,
38 | ],
39 | 3 => [
40 | 'from' => 100,
41 | 'to' => 200,
42 | 'key' => 200,
43 | ],
44 | 4 => [
45 | 'from' => 200,
46 | 'to' => 350,
47 | 'key' => 350,
48 | ],
49 | 5 => [
50 | 'from' => 350,
51 | 'to' => 500,
52 | 'key' => 500,
53 | ],
54 | 6 => [
55 | 'from' => 500,
56 | 'to' => 750,
57 | 'key' => 750,
58 | ],
59 | 7 => [
60 | 'from' => 750,
61 | 'to' => 1000,
62 | 'key' => 1000,
63 | ],
64 | 8 => [
65 | 'from' => 1000,
66 | 'to' => 1500,
67 | 'key' => 1500,
68 | ],
69 | 9 => [
70 | 'from' => 1500,
71 | 'to' => 2500,
72 | 'key' => 2500,
73 | ],
74 | 10 => [
75 | 'key' => '2500-*',
76 | 'from' => 2500,
77 | ],
78 | ],
79 | ],
80 | ];
81 | $built = $instance->build();
82 | $this->assertEquals($expected, $built);
83 | }
84 |
85 | public function testGetBeachDistancesRangesAggregationWithEqualRanges(): void
86 | {
87 | $instance = new RangesAggregation(
88 | 'beach',
89 | 'option_236',
90 | [10, 50, 100, 200, 350, 500, 750, 1000, 1500, 2500],
91 | [],
92 | true
93 | );
94 |
95 | $expected = [
96 | 'range' => [
97 | 'field' => 'option_236',
98 | 'ranges' => [
99 | 0 => [
100 | 'from' => 0,
101 | 'to' => 11,
102 | 'key' => 10,
103 | ],
104 | 1 => [
105 | 'from' => 10,
106 | 'to' => 51,
107 | 'key' => 50,
108 | ],
109 | 2 => [
110 | 'from' => 50,
111 | 'to' => 101,
112 | 'key' => 100,
113 | ],
114 | 3 => [
115 | 'from' => 100,
116 | 'to' => 201,
117 | 'key' => 200,
118 | ],
119 | 4 => [
120 | 'from' => 200,
121 | 'to' => 351,
122 | 'key' => 350,
123 | ],
124 | 5 => [
125 | 'from' => 350,
126 | 'to' => 501,
127 | 'key' => 500,
128 | ],
129 | 6 => [
130 | 'from' => 500,
131 | 'to' => 751,
132 | 'key' => 750,
133 | ],
134 | 7 => [
135 | 'from' => 750,
136 | 'to' => 1001,
137 | 'key' => 1000,
138 | ],
139 | 8 => [
140 | 'from' => 1000,
141 | 'to' => 1501,
142 | 'key' => 1500,
143 | ],
144 | 9 => [
145 | 'from' => 1500,
146 | 'to' => 2501,
147 | 'key' => 2500,
148 | ],
149 | 10 => [
150 | 'key' => '2500-*',
151 | 'from' => 2500,
152 | ],
153 | ],
154 | ],
155 | ];
156 | $built = $instance->build();
157 | $this->assertEquals($expected, $built);
158 | }
159 | }
160 |
--------------------------------------------------------------------------------