Skip to content

Commit df4e324

Browse files
Spamerczclaude
andcommitted
feat(filter): expand FilterCollection to full bool body
The filter container previously only exposed must(), so filter contexts could not express must_not / should / filter clauses without dropping into raw arrays. FilterCollection now mirrors QueryCollection: must(), should(), mustNot(), filter() — each returns the appropriate typed collection. The bool body emits all four arms when populated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 958e306 commit df4e324

2 files changed

Lines changed: 109 additions & 8 deletions

File tree

src/Filter/FilterCollection.php

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@
88
class FilterCollection implements FilterInterface
99
{
1010

11+
private \Spameri\ElasticQuery\Query\MustCollection $mustCollection;
12+
13+
private \Spameri\ElasticQuery\Query\ShouldCollection $shouldCollection;
14+
15+
private \Spameri\ElasticQuery\Query\MustNotCollection $mustNotCollection;
16+
17+
private \Spameri\ElasticQuery\Query\MustCollection $filterCollection;
18+
19+
1120
public function __construct(
12-
private \Spameri\ElasticQuery\Query\MustCollection|null $mustCollection = null,
21+
\Spameri\ElasticQuery\Query\MustCollection|null $mustCollection = null,
22+
\Spameri\ElasticQuery\Query\ShouldCollection|null $shouldCollection = null,
23+
\Spameri\ElasticQuery\Query\MustNotCollection|null $mustNotCollection = null,
24+
\Spameri\ElasticQuery\Query\MustCollection|null $filterCollection = null,
1325
)
1426
{
15-
if ($this->mustCollection === null) {
16-
$this->mustCollection = new \Spameri\ElasticQuery\Query\MustCollection();
17-
}
27+
$this->mustCollection = $mustCollection ?? new \Spameri\ElasticQuery\Query\MustCollection();
28+
$this->shouldCollection = $shouldCollection ?? new \Spameri\ElasticQuery\Query\ShouldCollection();
29+
$this->mustNotCollection = $mustNotCollection ?? new \Spameri\ElasticQuery\Query\MustNotCollection();
30+
$this->filterCollection = $filterCollection ?? new \Spameri\ElasticQuery\Query\MustCollection();
1831
}
1932

2033

@@ -24,21 +37,58 @@ public function must(): \Spameri\ElasticQuery\Query\MustCollection
2437
}
2538

2639

40+
public function should(): \Spameri\ElasticQuery\Query\ShouldCollection
41+
{
42+
return $this->shouldCollection;
43+
}
44+
45+
46+
public function mustNot(): \Spameri\ElasticQuery\Query\MustNotCollection
47+
{
48+
return $this->mustNotCollection;
49+
}
50+
51+
52+
public function filter(): \Spameri\ElasticQuery\Query\MustCollection
53+
{
54+
return $this->filterCollection;
55+
}
56+
57+
2758
public function key(): string
2859
{
2960
return '';
3061
}
3162

3263

64+
/**
65+
* @return array<string, array<string, mixed>>
66+
*/
3367
public function toArray(): array
3468
{
35-
$array = [];
36-
/** @var \Spameri\ElasticQuery\Query\LeafQueryInterface $item */
69+
$bool = [];
70+
3771
foreach ($this->mustCollection as $item) {
38-
$array['bool']['must'][] = $item->toArray();
72+
$bool['must'][] = $item->toArray();
73+
}
74+
75+
foreach ($this->shouldCollection as $item) {
76+
$bool['should'][] = $item->toArray();
77+
}
78+
79+
foreach ($this->mustNotCollection as $item) {
80+
$bool['must_not'][] = $item->toArray();
81+
}
82+
83+
foreach ($this->filterCollection as $item) {
84+
$bool['filter'][] = $item->toArray();
85+
}
86+
87+
if ($bool === []) {
88+
return [];
3989
}
4090

41-
return $array;
91+
return ['bool' => $bool];
4292
}
4393

4494
}

tests/SpameriTests/ElasticQuery/Filter/FilterCollection.phpt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,57 @@ class FilterCollection extends \Tester\TestCase
195195
}
196196

197197

198+
public function testShouldArm(): void
199+
{
200+
$filter = new \Spameri\ElasticQuery\Filter\FilterCollection();
201+
$filter->should()->add(new \Spameri\ElasticQuery\Query\Term('status', 'a'));
202+
$filter->should()->add(new \Spameri\ElasticQuery\Query\Term('status', 'b'));
203+
204+
$array = $filter->toArray();
205+
206+
\Tester\Assert::count(2, $array['bool']['should']);
207+
}
208+
209+
210+
public function testMustNotArm(): void
211+
{
212+
$filter = new \Spameri\ElasticQuery\Filter\FilterCollection();
213+
$filter->mustNot()->add(new \Spameri\ElasticQuery\Query\Term('deleted', true));
214+
215+
$array = $filter->toArray();
216+
217+
\Tester\Assert::count(1, $array['bool']['must_not']);
218+
}
219+
220+
221+
public function testFilterArm(): void
222+
{
223+
$filter = new \Spameri\ElasticQuery\Filter\FilterCollection();
224+
$filter->filter()->add(new \Spameri\ElasticQuery\Query\Term('region', 'eu'));
225+
226+
$array = $filter->toArray();
227+
228+
\Tester\Assert::count(1, $array['bool']['filter']);
229+
}
230+
231+
232+
public function testAllArmsCombined(): void
233+
{
234+
$filter = new \Spameri\ElasticQuery\Filter\FilterCollection();
235+
$filter->must()->add(new \Spameri\ElasticQuery\Query\Term('m', 'x'));
236+
$filter->should()->add(new \Spameri\ElasticQuery\Query\Term('s', 'y'));
237+
$filter->mustNot()->add(new \Spameri\ElasticQuery\Query\Term('mn', 'z'));
238+
$filter->filter()->add(new \Spameri\ElasticQuery\Query\Term('f', 'w'));
239+
240+
$array = $filter->toArray();
241+
242+
\Tester\Assert::count(1, $array['bool']['must']);
243+
\Tester\Assert::count(1, $array['bool']['should']);
244+
\Tester\Assert::count(1, $array['bool']['must_not']);
245+
\Tester\Assert::count(1, $array['bool']['filter']);
246+
}
247+
248+
198249
public function testComplexFilterScenario(): void
199250
{
200251
$filter = new \Spameri\ElasticQuery\Filter\FilterCollection();

0 commit comments

Comments
 (0)