From ea051265542f8cef9a30036044fd7fbf4d409b4c Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Wed, 31 Mar 2021 10:37:32 +0200 Subject: [PATCH] Fix preparation of $elemMatch operators in queries --- .../MongoDB/Persisters/DocumentPersister.php | 7 ++- .../Functional/DocumentPersisterTest.php | 16 +++---- .../Tests/Functional/Ticket/GH2251Test.php | 48 +++++++++++++++++++ 3 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2251Test.php diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php index ca45cbcba4..dc481a414f 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -1321,7 +1321,12 @@ private function prepareQueryExpression(array $expression, ClassMetadata $class) continue; } - // Recursively process expressions within a $not operator + // Recursively process expressions within a $not or $elemMatch operator + if ($k === '$elemMatch' && is_array($v)) { + $expression[$k] = $this->prepareQueryOrNewObj($v, false); + continue; + } + if ($k === '$not' && is_array($v)) { $expression[$k] = $this->prepareQueryExpression($v, $class); continue; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php index d72f41f5dd..bc69e0d16d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php @@ -198,7 +198,7 @@ public function testPrepareQueryOrNewObjWithHashIdAndInOperators($hashId) $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['_id' => ['$elemMatch' => $hashId]]; - $expected = ['_id' => ['$elemMatch' => (object) $hashId]]; + $expected = ['_id' => ['$elemMatch' => $hashId]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -208,7 +208,7 @@ public function testPrepareQueryOrNewObjWithHashIdAndInOperators($hashId) $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['_id' => ['$not' => ['$elemMatch' => $hashId]]]; - $expected = ['_id' => ['$not' => ['$elemMatch' => (object) $hashId]]]; + $expected = ['_id' => ['$not' => ['$elemMatch' => $hashId]]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -396,7 +396,7 @@ public function testPrepareQueryOrNewObjWithSimpleReferenceToTargetDocumentWithH $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['simpleRef' => ['$elemMatch' => $hashId]]; - $expected = ['simpleRef' => ['$elemMatch' => (object) $hashId]]; + $expected = ['simpleRef' => ['$elemMatch' => $hashId]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -406,7 +406,7 @@ public function testPrepareQueryOrNewObjWithSimpleReferenceToTargetDocumentWithH $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['simpleRef' => ['$not' => ['$elemMatch' => $hashId]]]; - $expected = ['simpleRef' => ['$not' => ['$elemMatch' => (object) $hashId]]]; + $expected = ['simpleRef' => ['$not' => ['$elemMatch' => $hashId]]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -473,7 +473,7 @@ public function testPrepareQueryOrNewObjWithDBRefReferenceToTargetDocumentWithHa $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['complexRef.id' => ['$elemMatch' => $hashId]]; - $expected = ['complexRef.$id' => ['$elemMatch' => (object) $hashId]]; + $expected = ['complexRef.$id' => ['$elemMatch' => $hashId]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -483,7 +483,7 @@ public function testPrepareQueryOrNewObjWithDBRefReferenceToTargetDocumentWithHa $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['complexRef.id' => ['$not' => ['$elemMatch' => $hashId]]]; - $expected = ['complexRef.$id' => ['$not' => ['$elemMatch' => (object) $hashId]]]; + $expected = ['complexRef.$id' => ['$not' => ['$elemMatch' => $hashId]]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -587,7 +587,7 @@ public function testPrepareQueryOrNewObjWithEmbeddedReferenceToTargetDocumentWit $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['embeddedRef.id' => ['$elemMatch' => $hashId]]; - $expected = ['embeddedRef.id' => ['$elemMatch' => (object) $hashId]]; + $expected = ['embeddedRef.id' => ['$elemMatch' => $hashId]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); @@ -597,7 +597,7 @@ public function testPrepareQueryOrNewObjWithEmbeddedReferenceToTargetDocumentWit $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); $value = ['embeddedRef.id' => ['$not' => ['$elemMatch' => $hashId]]]; - $expected = ['embeddedRef.id' => ['$not' => ['$elemMatch' => (object) $hashId]]]; + $expected = ['embeddedRef.id' => ['$not' => ['$elemMatch' => $hashId]]]; $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2251Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2251Test.php new file mode 100644 index 0000000000..47ee6f0035 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2251Test.php @@ -0,0 +1,48 @@ +dm->createQueryBuilder(User::class); + + $objectIds = [ + new ObjectId('5fae9a775ef4492e3c72b3f3'), + new ObjectId('5fae9a775ef4492e3c72b3f4'), + ]; + + $notIn = $builder->expr()->notIn($objectIds); + $elemMatch = $builder->expr() + ->field($fieldName) + ->elemMatch($notIn); + + $builder->addNor( + $elemMatch + ); + + $this->assertSame( + [ + '$nor' => [ + [ + $fieldName => [ + '$elemMatch' => ['$nin' => $objectIds], + ], + ], + ], + ], + $builder->getQueryArray() + ); + } +}