Skip to content

Commit 73e5f5d

Browse files
committed
PHPLIB-831: Allow $$exists within $$unsetOrMatches
1 parent 3370bfc commit 73e5f5d

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

tests/UnifiedSpecTests/Constraint/Matches.php

+13
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,19 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $
240240
{
241241
$name = self::getOperatorName($operator);
242242

243+
if ($name === '$$exists') {
244+
assertIsBool($operator['$$exists'], '$$exists requires bool');
245+
246+
/* If we get to this point, the field itself must already exist so
247+
* we need only fail if $$exists is false. */
248+
if ($operator['$$exists'] === false) {
249+
$key = ltrim(strrchr($keyPath, '.'), '.');
250+
self::failAt(sprintf('$actual has unexpected key "%s"', $key), $keyPath);
251+
}
252+
253+
return;
254+
}
255+
243256
if ($name === '$$type') {
244257
assertThat(
245258
$operator['$$type'],

tests/UnifiedSpecTests/Constraint/MatchesTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,26 @@ public function testOperatorUnsetOrMatches(): void
119119
$this->assertResult(false, $c, ['x' => ['y' => 1, 'z' => 2]], 'value does not match (embedded)');
120120
}
121121

122+
public function testOperatorUnsetOrMatchesWithNestedOperator(): void
123+
{
124+
// Nested $$unsetOrMatches is redundant, but should behave the same as if it was omitted
125+
$c = new Matches(['x' => ['$$unsetOrMatches' => ['$$unsetOrMatches' => ['y' => 1]]]]);
126+
$this->assertResult(true, $c, new stdClass(), 'missing value is considered unset (embedded)');
127+
$this->assertResult(false, $c, ['x' => null], 'null value is not considered unset (embedded)');
128+
$this->assertResult(true, $c, ['x' => ['y' => 1]], 'value matches (embedded)');
129+
$this->assertResult(false, $c, ['x' => ['y' => 1, 'z' => 2]], 'value does not match (embedded)');
130+
131+
$c = new Matches(['x' => ['$$unsetOrMatches' => ['$$exists' => true]]]);
132+
$this->assertResult(true, $c, new stdClass(), 'missing value is considered unset (embedded)');
133+
$this->assertResult(true, $c, ['x' => null], 'null value is not considered unset (embedded)');
134+
$this->assertResult(true, $c, ['x' => ['y' => 1]], 'non-null value is not considered unset (embedded)');
135+
136+
$c = new Matches(['x' => ['$$unsetOrMatches' => ['$$exists' => false]]]);
137+
$this->assertResult(true, $c, new stdClass(), 'missing value is considered unset (embedded)');
138+
$this->assertResult(false, $c, ['x' => null], 'null value is not considered unset (embedded)');
139+
$this->assertResult(false, $c, ['x' => ['y' => 1]], 'non-null value is not considered unset (embedded)');
140+
}
141+
122142
public function testOperatorSessionLsid(): void
123143
{
124144
$session = $this->manager->startSession();

0 commit comments

Comments
 (0)