Skip to content

Commit 9d46af7

Browse files
authored
PHPLIB-1282 Add factories for system variables (mongodb#5)
1 parent cf5b119 commit 9d46af7

File tree

3 files changed

+207
-2
lines changed

3 files changed

+207
-2
lines changed

src/Builder/Variable.php

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Builder;
6+
7+
use MongoDB\Builder\Expression\ResolvesToAny;
8+
use MongoDB\Builder\Expression\ResolvesToArray;
9+
use MongoDB\Builder\Expression\ResolvesToDate;
10+
use MongoDB\Builder\Expression\ResolvesToObject;
11+
use MongoDB\Builder\Expression\ResolvesToTimestamp;
12+
use MongoDB\Builder\Type\ExpressionInterface;
13+
14+
/**
15+
* Enum for system variables that can be used in aggregation expressions.
16+
*
17+
* @see https://www.mongodb.com/docs/manual/reference/aggregation-variables/
18+
*/
19+
enum Variable
20+
{
21+
/**
22+
* A variable that returns the current datetime value.
23+
* NOW returns the same value for all members of the deployment and remains the same throughout all stages of the
24+
* aggregation pipeline.
25+
*
26+
* New in MongoDB 4.2.
27+
*/
28+
public static function now(): ResolvesToDate
29+
{
30+
return new Expression\Variable('NOW');
31+
}
32+
33+
/**
34+
* A variable that returns the current timestamp value.
35+
* CLUSTER_TIME is only available on replica sets and sharded clusters.
36+
* CLUSTER_TIME returns the same value for all members of the deployment and remains the same throughout all stages
37+
* of the pipeline.
38+
*
39+
* New in MongoDB 4.2.
40+
*/
41+
public static function clusterTime(): ResolvesToTimestamp
42+
{
43+
return new Expression\Variable('CLUSTER_TIME');
44+
}
45+
46+
/**
47+
* References the root document, i.e. the top-level document, currently being processed in the aggregation pipeline
48+
* stage.
49+
*/
50+
public static function root(): ResolvesToObject
51+
{
52+
return new Expression\Variable('ROOT');
53+
}
54+
55+
/**
56+
* References the start of the field path being processed in the aggregation pipeline stage.
57+
* Unless documented otherwise, all stages start with CURRENT the same as ROOT.
58+
* CURRENT is modifiable. However, since $<field> is equivalent to $$CURRENT.<field>, rebinding
59+
* CURRENT changes the meaning of $ accesses.
60+
*/
61+
public static function current(string $fieldPath = ''): ResolvesToAny
62+
{
63+
return new Expression\Variable('CURRENT' . ($fieldPath ? '.' . $fieldPath : ''));
64+
}
65+
66+
/**
67+
* A variable which evaluates to the missing value. Allows for the conditional exclusion of fields. In a $project,
68+
* a field set to the variable REMOVE is excluded from the output.
69+
* Can be used with $cond operator for conditionally exclude fields.
70+
*
71+
* @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#std-label-remove-example
72+
*/
73+
public static function remove(): ResolvesToAny
74+
{
75+
return new Expression\Variable('REMOVE');
76+
}
77+
78+
/**
79+
* One of the allowed results of a $redact expression.
80+
*
81+
* $redact returns the fields at the current document level, excluding embedded documents. To include embedded
82+
* documents and embedded documents within arrays, apply the $cond expression to the embedded documents to determine
83+
* access for these embedded documents.
84+
*
85+
* @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#mongodb-pipeline-pipe.-redact
86+
*/
87+
public static function descend(): ExpressionInterface
88+
{
89+
return new Expression\Variable('DESCEND');
90+
}
91+
92+
/**
93+
* One of the allowed results of a $redact expression.
94+
*
95+
* $redact excludes all fields at this current document/embedded document level, without further inspection of any
96+
* of the excluded fields. This applies even if the excluded field contains embedded documents that may have
97+
* different access levels.
98+
*
99+
* @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#mongodb-pipeline-pipe.-redact
100+
*/
101+
public static function prune(): ExpressionInterface
102+
{
103+
return new Expression\Variable('PRUNE');
104+
}
105+
106+
/**
107+
* One of the allowed results of a $redact expression.
108+
*
109+
* $redact returns or keeps all fields at this current document/embedded document level, without further inspection
110+
* of the fields at this level. This applies even if the included field contains embedded documents that may have
111+
* different access levels.
112+
*
113+
* @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#mongodb-pipeline-pipe.-redact
114+
*/
115+
public static function keep(): ExpressionInterface
116+
{
117+
return new Expression\Variable('KEEP');
118+
}
119+
120+
/**
121+
* A variable that stores the metadata results of an Atlas Search query. In all supported aggregation pipeline
122+
* stages, a field set to the variable $$SEARCH_META returns the metadata results for the query.
123+
* For an example of its usage, see Atlas Search facet and count.
124+
*
125+
* @see https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#metadata-result-types
126+
*/
127+
public static function searchMeta(): ResolvesToObject
128+
{
129+
return new Expression\Variable('SEARCH_META');
130+
}
131+
132+
/**
133+
* Returns the roles assigned to the current user.
134+
* For use cases that include USER_ROLES, see the find, aggregation, view, updateOne, updateMany, and findAndModify
135+
* examples.
136+
*
137+
* New in MongoDB 7.0.
138+
*/
139+
public static function userRoles(): ResolvesToArray
140+
{
141+
return new Expression\Variable('USER_ROLES');
142+
}
143+
144+
/**
145+
* User-defined variable that can be used to store any BSON type.
146+
*
147+
* @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/let/
148+
*/
149+
public static function variable(string $name): Expression\Variable
150+
{
151+
return new Expression\Variable($name);
152+
}
153+
}

tests/Builder/BuilderEncoderTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use MongoDB\Builder\Projection;
1313
use MongoDB\Builder\Query;
1414
use MongoDB\Builder\Stage;
15+
use MongoDB\Builder\Variable;
1516
use PHPUnit\Framework\TestCase;
1617

1718
use function array_is_list;
@@ -252,8 +253,8 @@ public function testRedactStage(): void
252253
Stage::redact(
253254
Expression::cond(
254255
if: Expression::eq(Expression::fieldPath('level'), 5),
255-
then: Expression::variable('PRUNE'),
256-
else: Expression::variable('DESCEND'),
256+
then: Variable::prune(),
257+
else: Variable::descend(),
257258
),
258259
),
259260
);

tests/Builder/VariableTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Tests\Builder;
6+
7+
use Generator;
8+
use MongoDB\Builder\Expression;
9+
use MongoDB\Builder\Variable;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class VariableTest extends TestCase
13+
{
14+
/** @dataProvider provideVariableBuilders */
15+
public function testSystemVariables($factory): void
16+
{
17+
$variable = $factory();
18+
$this->assertInstanceOf(Expression\Variable::class, $variable);
19+
$this->assertStringStartsNotWith('$$', $variable->name);
20+
}
21+
22+
public function provideVariableBuilders(): Generator
23+
{
24+
yield 'now' => [fn () => Variable::now()];
25+
yield 'clusterTime' => [fn () => Variable::clusterTime()];
26+
yield 'root' => [fn () => Variable::root()];
27+
yield 'current' => [fn () => Variable::current()];
28+
yield 'remove' => [fn () => Variable::remove()];
29+
yield 'descend' => [fn () => Variable::descend()];
30+
yield 'prune' => [fn () => Variable::prune()];
31+
yield 'keep' => [fn () => Variable::keep()];
32+
yield 'searchMeta' => [fn () => Variable::searchMeta()];
33+
yield 'userRoles' => [fn () => Variable::userRoles()];
34+
}
35+
36+
public function testCurrent(): void
37+
{
38+
$variable = Variable::current();
39+
$this->assertInstanceOf(Expression\Variable::class, $variable);
40+
$this->assertSame('CURRENT', $variable->name);
41+
42+
$variable = Variable::current('foo');
43+
$this->assertInstanceOf(Expression\Variable::class, $variable);
44+
$this->assertSame('CURRENT.foo', $variable->name);
45+
}
46+
47+
public function testCustomVariable(): void
48+
{
49+
$this->assertInstanceOf(Expression\Variable::class, Variable::variable('foo'));
50+
}
51+
}

0 commit comments

Comments
 (0)