Skip to content

Commit e60831e

Browse files
authored
Merge pull request #53 from dam-bal/nested-query-improvements
Nested query improvements
2 parents b3a9379 + 8e0c562 commit e60831e

File tree

5 files changed

+324
-11
lines changed

5 files changed

+324
-11
lines changed

Diff for: README.md

+23
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,29 @@ The following query types are available:
121121
);
122122
```
123123

124+
##### `NestedQuery` `InnerHits`
125+
126+
[https://www.elastic.co/guide/en/elasticsearch/reference/current/inner-hits.html](https://www.elastic.co/guide/en/elasticsearch/reference/current/inner-hits.html)
127+
128+
```php
129+
$nestedQuery = \Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery::create(
130+
'comments',
131+
\Spatie\ElasticsearchQueryBuilder\Queries\TermsQuery::create('comments.published', true)
132+
);
133+
134+
$nestedQuery->innerHits(
135+
\Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery\InnerHits::create('top_three_liked_comments')
136+
->size(3)
137+
->addSort(
138+
\Spatie\ElasticsearchQueryBuilder\Sorts\Sort::create(
139+
'comments.likes',
140+
\Spatie\ElasticsearchQueryBuilder\Sorts\Sort::DESC
141+
)
142+
)
143+
->fields(['comments.content', 'comments.author', 'comments.likes'])
144+
);
145+
```
146+
124147
#### `RangeQuery`
125148

126149
[https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html)

Diff for: src/Queries/NestedQuery.php

+42-11
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,63 @@
22

33
namespace Spatie\ElasticsearchQueryBuilder\Queries;
44

5+
use Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery\InnerHits;
6+
57
class NestedQuery implements Query
68
{
7-
protected string $path;
8-
9-
protected Query $query;
9+
public const SCORE_MODE_AVG = 'avg';
10+
public const SCORE_MODE_MAX = 'max';
11+
public const SCORE_MODE_MIN = 'min';
12+
public const SCORE_MODE_NONE = 'none';
13+
public const SCORE_MODE_SUM = 'sum';
1014

1115
public static function create(string $path, Query $query): self
1216
{
1317
return new self($path, $query);
1418
}
1519

1620
public function __construct(
17-
string $path,
18-
Query $query
21+
protected string $path,
22+
protected Query $query,
23+
protected ?string $scoreMode = null,
24+
protected ?bool $ignoreUnmapped = null,
25+
protected ?InnerHits $innerHits = null
1926
) {
20-
$this->path = $path;
21-
$this->query = $query;
27+
}
28+
29+
public function scoreMode(string $scoreMode): self
30+
{
31+
$this->scoreMode = $scoreMode;
32+
33+
return $this;
34+
}
35+
36+
public function ignoreUnmapped(bool $ignoreUnmapped): self
37+
{
38+
$this->ignoreUnmapped = $ignoreUnmapped;
39+
40+
return $this;
41+
}
42+
43+
public function innerHits(InnerHits $innerHits): self
44+
{
45+
$this->innerHits = $innerHits;
46+
47+
return $this;
2248
}
2349

2450
public function toArray(): array
2551
{
2652
return [
27-
'nested' => [
28-
'path' => $this->path,
29-
'query' => $this->query->toArray(),
30-
],
53+
'nested' => array_filter(
54+
[
55+
'path' => $this->path,
56+
'query' => $this->query->toArray(),
57+
'score_mode' => $this->scoreMode,
58+
'ignore_unmapped' => $this->ignoreUnmapped,
59+
'inner_hits' => $this->innerHits?->toArray()
60+
]
61+
),
3162
];
3263
}
3364
}

Diff for: src/Queries/NestedQuery/InnerHits.php

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery;
4+
5+
use Spatie\ElasticsearchQueryBuilder\SortCollection;
6+
use Spatie\ElasticsearchQueryBuilder\Sorts\Sort;
7+
8+
class InnerHits
9+
{
10+
public static function create(string $name): self
11+
{
12+
return new InnerHits($name);
13+
}
14+
15+
/**
16+
* @param string[]|null $fields
17+
*/
18+
public function __construct(
19+
protected string $name,
20+
protected ?int $from = null,
21+
protected ?int $size = null,
22+
protected ?SortCollection $sorts = null,
23+
protected ?array $fields = null
24+
) {
25+
}
26+
27+
public function from(int $from): self
28+
{
29+
$this->from = $from;
30+
31+
return $this;
32+
}
33+
34+
public function size(int $size): self
35+
{
36+
$this->size = $size;
37+
38+
return $this;
39+
}
40+
41+
/**
42+
* @param string[] $fields
43+
*/
44+
public function fields(array $fields): self
45+
{
46+
$this->fields = $fields;
47+
48+
return $this;
49+
}
50+
51+
public function addSort(Sort $sort): self
52+
{
53+
if (! $this->sorts) {
54+
$this->sorts = new SortCollection();
55+
}
56+
57+
$this->sorts->add($sort);
58+
59+
return $this;
60+
}
61+
62+
public function toArray(): array
63+
{
64+
return array_filter(
65+
[
66+
'from' => $this->from,
67+
'size' => $this->size,
68+
'name' => $this->name,
69+
'sort' => $this->sorts?->toArray(),
70+
'_source' => $this->fields
71+
]
72+
);
73+
}
74+
}

Diff for: tests/Queries/NestedQuery/InnerHitsTest.php

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Spatie\ElasticsearchQueryBuilder\Tests\Queries\NestedQuery;
4+
5+
use Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery\InnerHits;
6+
use PHPUnit\Framework\TestCase;
7+
use Spatie\ElasticsearchQueryBuilder\SortCollection;
8+
use Spatie\ElasticsearchQueryBuilder\Sorts\Sort;
9+
10+
class InnerHitsTest extends TestCase
11+
{
12+
private InnerHits $innerHits;
13+
14+
protected function setUp(): void
15+
{
16+
$this->innerHits = new InnerHits('test');
17+
}
18+
19+
public function testToArrayBuildsCorrectInnerHits(): void
20+
{
21+
$this->assertEquals(
22+
[
23+
'name' => 'test',
24+
],
25+
$this->innerHits->toArray()
26+
);
27+
}
28+
29+
public function testToArrayBuildsCorrectInnerHitsWithFrom(): void
30+
{
31+
$this->assertEquals(
32+
[
33+
'name' => 'test',
34+
'from' => 123,
35+
],
36+
$this->innerHits->from(123)->toArray()
37+
);
38+
}
39+
40+
public function testToArrayBuildsCorrectInnerHitsWithSize(): void
41+
{
42+
$this->assertEquals(
43+
[
44+
'name' => 'test',
45+
'size' => 123,
46+
],
47+
$this->innerHits->size(123)->toArray()
48+
);
49+
}
50+
51+
public function testToArrayBuildsCorrectInnerHitsWithSorts(): void
52+
{
53+
$sortMock = $this->createMock(Sort::class);
54+
55+
$sortMock
56+
->method('toArray')
57+
->willReturn(['field' => ['order' => Sort::ASC]]);
58+
59+
$this->assertEquals(
60+
[
61+
'name' => 'test',
62+
'sort' => [
63+
[
64+
'field' => [
65+
'order' => Sort::ASC,
66+
]
67+
]
68+
]
69+
],
70+
$this->innerHits->addSort($sortMock)->toArray()
71+
);
72+
}
73+
74+
public function testAddSortAddsSortToSorts(): void
75+
{
76+
$sortMock = $this->createMock(Sort::class);
77+
78+
$sortMock
79+
->method('toArray')
80+
->willReturn(['sort']);
81+
82+
$sortsMock = $this->createMock(SortCollection::class);
83+
84+
$sortsMock
85+
->expects($this->once())
86+
->method('add')
87+
->with($sortMock);
88+
89+
$innerHits = new InnerHits('test', sorts: $sortsMock);
90+
91+
$innerHits->addSort($sortMock);
92+
}
93+
}

Diff for: tests/Queries/NestedQueryTest.php

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace Spatie\ElasticsearchQueryBuilder\Tests\Queries;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery;
7+
use Spatie\ElasticsearchQueryBuilder\Queries\NestedQuery\InnerHits;
8+
use Spatie\ElasticsearchQueryBuilder\Queries\Query;
9+
10+
class NestedQueryTest extends TestCase
11+
{
12+
private NestedQuery $nestedQuery;
13+
14+
protected function setUp(): void
15+
{
16+
$queryMock = $this->createMock(Query::class);
17+
18+
$queryMock
19+
->method('toArray')
20+
->willReturn(['query']);
21+
22+
$this->nestedQuery = new NestedQuery('path', $queryMock);
23+
}
24+
25+
public function testToArrayBuildsCorrectNestedQuery(): void
26+
{
27+
$this->assertEquals(
28+
[
29+
'nested' => [
30+
'path' => 'path',
31+
'query' => ['query']
32+
]
33+
],
34+
$this->nestedQuery->toArray()
35+
);
36+
}
37+
38+
public function testToArrayBuildsCorrectNestedQueryWithScoreMode(): void
39+
{
40+
$this->assertEquals(
41+
[
42+
'nested' => [
43+
'path' => 'path',
44+
'query' => ['query'],
45+
'score_mode' => NestedQuery::SCORE_MODE_MIN,
46+
]
47+
],
48+
$this->nestedQuery->scoreMode(NestedQuery::SCORE_MODE_MIN)->toArray()
49+
);
50+
}
51+
52+
public function testToArrayBuildsCorrectNestedQueryWithIgnoreUnmapped(): void
53+
{
54+
$this->assertEquals(
55+
[
56+
'nested' => [
57+
'path' => 'path',
58+
'query' => ['query'],
59+
'ignore_unmapped' => true,
60+
]
61+
],
62+
$this->nestedQuery->ignoreUnmapped(true)->toArray()
63+
);
64+
}
65+
66+
public function testToArrayBuildsCorrectNestedQueryWithInnerHits(): void
67+
{
68+
$innerHitsMock = $this->createMock(InnerHits::class);
69+
$innerHitsMock
70+
->method('toArray')
71+
->willReturn(
72+
[
73+
'size' => 10,
74+
'name' => 'test'
75+
]
76+
);
77+
78+
$this->assertEquals(
79+
[
80+
'nested' => [
81+
'path' => 'path',
82+
'query' => ['query'],
83+
'inner_hits' => [
84+
'size' => 10,
85+
'name' => 'test'
86+
]
87+
]
88+
],
89+
$this->nestedQuery->innerHits($innerHitsMock)->toArray()
90+
);
91+
}
92+
}

0 commit comments

Comments
 (0)