Skip to content

Commit 7c3375d

Browse files
authored
Allow to merge an array of stages into a pipeline (#57)
* Allow to merge an array of stages into a pipeline * Improve types * Add test to create pipeline from array
1 parent fe8f353 commit 7c3375d

File tree

3 files changed

+83
-49
lines changed

3 files changed

+83
-49
lines changed

psalm-baseline.xml

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="5.21.1@8c473e2437be8b6a8fd8f630f0f11a16b114c494">
2+
<files psalm-version="5.22.2@d768d914152dbbf3486c36398802f74e80cfde48">
33
<file src="src/Builder/Encoder/AbstractExpressionEncoder.php">
44
<MixedAssignment>
5-
<code>$val</code>
6-
<code>$val</code>
7-
<code>$value[$key]</code>
5+
<code><![CDATA[$val]]></code>
6+
<code><![CDATA[$val]]></code>
7+
<code><![CDATA[$value[$key]]]></code>
88
</MixedAssignment>
99
</file>
1010
<file src="src/Builder/Encoder/CombinedFieldQueryEncoder.php">
1111
<MixedAssignment>
12-
<code>$filter</code>
13-
<code>$filterValue</code>
12+
<code><![CDATA[$filter]]></code>
13+
<code><![CDATA[$filterValue]]></code>
1414
</MixedAssignment>
1515
</file>
1616
<file src="src/Builder/Encoder/FieldPathEncoder.php">
@@ -20,105 +20,111 @@
2020
</file>
2121
<file src="src/Builder/Encoder/OperatorEncoder.php">
2222
<MixedAssignment>
23-
<code>$result</code>
24-
<code>$result[]</code>
25-
<code>$val</code>
26-
<code>$val</code>
27-
<code>$val</code>
28-
<code>$val</code>
29-
<code>$val</code>
23+
<code><![CDATA[$result]]></code>
24+
<code><![CDATA[$result[]]]></code>
25+
<code><![CDATA[$val]]></code>
26+
<code><![CDATA[$val]]></code>
27+
<code><![CDATA[$val]]></code>
28+
<code><![CDATA[$val]]></code>
29+
<code><![CDATA[$val]]></code>
3030
</MixedAssignment>
3131
</file>
3232
<file src="src/Builder/Encoder/OutputWindowEncoder.php">
3333
<MixedArgument>
34-
<code>$result</code>
34+
<code><![CDATA[$result]]></code>
3535
</MixedArgument>
3636
</file>
37+
<file src="src/Builder/Encoder/PipelineEncoder.php">
38+
<MixedAssignment>
39+
<code><![CDATA[$encoded[]]]></code>
40+
<code><![CDATA[$stage]]></code>
41+
</MixedAssignment>
42+
</file>
3743
<file src="src/Builder/Encoder/QueryEncoder.php">
3844
<MixedArgument>
3945
<code><![CDATA[$this->recursiveEncode($value)]]></code>
4046
</MixedArgument>
4147
<MixedAssignment>
42-
<code>$subValue</code>
43-
<code>$value</code>
48+
<code><![CDATA[$subValue]]></code>
49+
<code><![CDATA[$value]]></code>
4450
</MixedAssignment>
4551
</file>
4652
<file src="src/Builder/Projection/ElemMatchOperator.php">
4753
<MixedArgumentTypeCoercion>
48-
<code>$query</code>
54+
<code><![CDATA[$query]]></code>
4955
</MixedArgumentTypeCoercion>
5056
</file>
5157
<file src="src/Builder/Query.php">
5258
<ArgumentTypeCoercion>
53-
<code>$query</code>
59+
<code><![CDATA[$query]]></code>
5460
</ArgumentTypeCoercion>
5561
</file>
5662
<file src="src/Builder/Query/ElemMatchOperator.php">
5763
<MixedArgumentTypeCoercion>
58-
<code>$query</code>
64+
<code><![CDATA[$query]]></code>
5965
</MixedArgumentTypeCoercion>
6066
</file>
6167
<file src="src/Builder/Stage/AddFieldsStage.php">
6268
<PropertyTypeCoercion>
63-
<code>$expression</code>
69+
<code><![CDATA[$expression]]></code>
6470
</PropertyTypeCoercion>
6571
<TooManyTemplateParams>
66-
<code>stdClass</code>
72+
<code><![CDATA[stdClass]]></code>
6773
</TooManyTemplateParams>
6874
</file>
6975
<file src="src/Builder/Stage/FacetStage.php">
7076
<PropertyTypeCoercion>
71-
<code>$facet</code>
77+
<code><![CDATA[$facet]]></code>
7278
</PropertyTypeCoercion>
7379
<TooManyTemplateParams>
74-
<code>stdClass</code>
80+
<code><![CDATA[stdClass]]></code>
7581
</TooManyTemplateParams>
7682
</file>
7783
<file src="src/Builder/Stage/GeoNearStage.php">
7884
<MixedArgumentTypeCoercion>
79-
<code>$query</code>
85+
<code><![CDATA[$query]]></code>
8086
</MixedArgumentTypeCoercion>
8187
</file>
8288
<file src="src/Builder/Stage/GraphLookupStage.php">
8389
<MixedArgumentTypeCoercion>
84-
<code>$restrictSearchWithMatch</code>
90+
<code><![CDATA[$restrictSearchWithMatch]]></code>
8591
</MixedArgumentTypeCoercion>
8692
</file>
8793
<file src="src/Builder/Stage/GroupStage.php">
8894
<PropertyTypeCoercion>
89-
<code>$field</code>
95+
<code><![CDATA[$field]]></code>
9096
</PropertyTypeCoercion>
9197
<TooManyTemplateParams>
92-
<code>stdClass</code>
98+
<code><![CDATA[stdClass]]></code>
9399
</TooManyTemplateParams>
94100
</file>
95101
<file src="src/Builder/Stage/MatchStage.php">
96102
<MixedArgumentTypeCoercion>
97-
<code>$query</code>
103+
<code><![CDATA[$query]]></code>
98104
</MixedArgumentTypeCoercion>
99105
</file>
100106
<file src="src/Builder/Stage/ProjectStage.php">
101107
<PropertyTypeCoercion>
102-
<code>$specification</code>
108+
<code><![CDATA[$specification]]></code>
103109
</PropertyTypeCoercion>
104110
<TooManyTemplateParams>
105-
<code>stdClass</code>
111+
<code><![CDATA[stdClass]]></code>
106112
</TooManyTemplateParams>
107113
</file>
108114
<file src="src/Builder/Stage/SetStage.php">
109115
<PropertyTypeCoercion>
110-
<code>$field</code>
116+
<code><![CDATA[$field]]></code>
111117
</PropertyTypeCoercion>
112118
<TooManyTemplateParams>
113-
<code>stdClass</code>
119+
<code><![CDATA[stdClass]]></code>
114120
</TooManyTemplateParams>
115121
</file>
116122
<file src="src/Builder/Stage/SortStage.php">
117123
<PropertyTypeCoercion>
118-
<code>$sort</code>
124+
<code><![CDATA[$sort]]></code>
119125
</PropertyTypeCoercion>
120126
<TooManyTemplateParams>
121-
<code>stdClass</code>
127+
<code><![CDATA[stdClass]]></code>
122128
</TooManyTemplateParams>
123129
</file>
124130
<file src="src/Builder/Type/OutputWindow.php">
@@ -129,8 +135,8 @@
129135
</file>
130136
<file src="src/Builder/Type/QueryObject.php">
131137
<MixedAssignment>
132-
<code>$queries[$fieldPath]</code>
133-
<code>$query</code>
138+
<code><![CDATA[$queries[$fieldPath]]]></code>
139+
<code><![CDATA[$query]]></code>
134140
</MixedAssignment>
135141
<RedundantConditionGivenDocblockType>
136142
<code><![CDATA[count($queriesOrArrayOfQueries) === 1 &&

src/Builder/Pipeline.php

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,31 @@
88
use IteratorAggregate;
99
use MongoDB\Builder\Type\StageInterface;
1010
use MongoDB\Exception\InvalidArgumentException;
11-
use Traversable;
11+
use stdClass;
1212

1313
use function array_is_list;
1414
use function array_merge;
15+
use function is_array;
1516

1617
/**
1718
* An aggregation pipeline consists of one or more stages that process documents.
1819
*
1920
* @see https://www.mongodb.com/docs/manual/core/aggregation-pipeline/
2021
*
2122
* @psalm-immutable
22-
* @implements IteratorAggregate<StageInterface>
23+
* @psalm-type stage = StageInterface|array<string,mixed>|stdClass
24+
* @implements IteratorAggregate<stage>
2325
*/
24-
class Pipeline implements IteratorAggregate
26+
final class Pipeline implements IteratorAggregate
2527
{
26-
/** @var StageInterface[] */
2728
private readonly array $stages;
2829

29-
/** @no-named-arguments */
30-
public function __construct(StageInterface|Pipeline ...$stagesOrPipelines)
30+
/**
31+
* @psalm-param stage|list<stage> ...$stagesOrPipelines
32+
*
33+
* @no-named-arguments
34+
*/
35+
public function __construct(StageInterface|Pipeline|array|stdClass ...$stagesOrPipelines)
3136
{
3237
if (! array_is_list($stagesOrPipelines)) {
3338
throw new InvalidArgumentException('Named arguments are not supported for pipelines');
@@ -36,7 +41,9 @@ public function __construct(StageInterface|Pipeline ...$stagesOrPipelines)
3641
$stages = [];
3742

3843
foreach ($stagesOrPipelines as $stageOrPipeline) {
39-
if ($stageOrPipeline instanceof Pipeline) {
44+
if (is_array($stageOrPipeline) && array_is_list($stageOrPipeline)) {
45+
$stages = array_merge($stages, $stageOrPipeline);
46+
} elseif ($stageOrPipeline instanceof Pipeline) {
4047
$stages = array_merge($stages, $stageOrPipeline->stages);
4148
} else {
4249
$stages[] = $stageOrPipeline;
@@ -46,8 +53,7 @@ public function __construct(StageInterface|Pipeline ...$stagesOrPipelines)
4653
$this->stages = $stages;
4754
}
4855

49-
/** @return Traversable<StageInterface> */
50-
public function getIterator(): Traversable
56+
public function getIterator(): ArrayIterator
5157
{
5258
return new ArrayIterator($this->stages);
5359
}

tests/Builder/PipelineTest.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,46 @@ class PipelineTest extends TestCase
1717
{
1818
public function testEmptyPipeline(): void
1919
{
20-
$pipeline = new Pipeline();
20+
$pipeline = new Pipeline([]);
2121

2222
$this->assertSame([], iterator_to_array($pipeline));
2323
}
2424

25+
public function testFromArray(): void
26+
{
27+
$pipeline = new Pipeline(
28+
['$match' => ['tag' => 'foo']],
29+
[
30+
['$sort' => ['_id' => 1]],
31+
['$skip' => 10],
32+
],
33+
['$limit' => 5],
34+
);
35+
36+
$expected = [
37+
['$match' => ['tag' => 'foo']],
38+
['$sort' => ['_id' => 1]],
39+
['$skip' => 10],
40+
['$limit' => 5],
41+
];
42+
43+
$this->assertSame($expected, iterator_to_array($pipeline));
44+
}
45+
2546
public function testMergingPipeline(): void
2647
{
2748
$stages = array_map(
2849
fn (int $i) => $this->createMock(StageInterface::class),
29-
range(0, 5),
50+
range(0, 7),
3051
);
3152

3253
$pipeline = new Pipeline(
3354
$stages[0],
3455
$stages[1],
3556
new Pipeline($stages[2], $stages[3]),
36-
$stages[4],
37-
new Pipeline($stages[5]),
57+
[$stages[4], $stages[5]],
58+
new Pipeline($stages[6]),
59+
[$stages[7]],
3860
);
3961

4062
$this->assertSame($stages, iterator_to_array($pipeline));

0 commit comments

Comments
 (0)