Skip to content

Commit

Permalink
Fix query preparation when in elemMatch (#2299)
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus authored Apr 13, 2021
1 parent 6e42eed commit 05c9a25
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 25 deletions.
77 changes: 52 additions & 25 deletions lib/Doctrine/ODM/MongoDB/Query/Expr.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,7 @@ public function addAnd($expression, ...$expressions): self

$this->query['$and'] = array_merge(
$this->query['$and'],
array_map(
static function ($expression) {
return $expression instanceof Expr ? $expression->getQuery() : $expression;
},
func_get_args()
)
func_get_args()
);

return $this;
Expand All @@ -123,9 +118,7 @@ public function addNor($expression, ...$expressions): self

$this->query['$nor'] = array_merge(
$this->query['$nor'],
array_map(static function ($expression) {
return $expression instanceof Expr ? $expression->getQuery() : $expression;
}, func_get_args())
func_get_args()
);

return $this;
Expand All @@ -148,9 +141,7 @@ public function addOr($expression, ...$expressions): self

$this->query['$or'] = array_merge(
$this->query['$or'],
array_map(static function ($expression) {
return $expression instanceof Expr ? $expression->getQuery() : $expression;
}, func_get_args())
func_get_args()
);

return $this;
Expand All @@ -175,12 +166,8 @@ public function addOr($expression, ...$expressions): self
*/
public function addToSet($valueOrExpression): self
{
if ($valueOrExpression instanceof Expr) {
$valueOrExpression = $valueOrExpression->getQuery();
}

$this->requiresCurrentField();
$this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
$this->newObj['$addToSet'][$this->currentField] = static::convertExpression($valueOrExpression, $this->class);

return $this;
}
Expand Down Expand Up @@ -414,7 +401,7 @@ public function each(array $values): self
*/
public function elemMatch($expression): self
{
return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
return $this->operator('$elemMatch', $expression);
}

/**
Expand Down Expand Up @@ -601,7 +588,7 @@ public function getQuery(): array
{
return $this->dm->getUnitOfWork()
->getDocumentPersister($this->class->name)
->prepareQueryOrNewObj($this->query);
->prepareQueryOrNewObj($this->convertExpressions($this->query));
}

/**
Expand Down Expand Up @@ -878,7 +865,7 @@ public function nearSphere($x, $y = null): self
*/
public function not($expression): self
{
return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
return $this->operator('$not', $expression);
}

/**
Expand Down Expand Up @@ -978,12 +965,8 @@ public function position(int $position): self
*/
public function pull($valueOrExpression): self
{
if ($valueOrExpression instanceof Expr) {
$valueOrExpression = $valueOrExpression->getQuery();
}

$this->requiresCurrentField();
$this->newObj['$pull'][$this->currentField] = $valueOrExpression;
$this->newObj['$pull'][$this->currentField] = static::convertExpression($valueOrExpression, $this->class);

return $this;
}
Expand Down Expand Up @@ -1420,4 +1403,48 @@ private function wrapEqualityCriteria(): void

$query = ['$in' => [$query]];
}

private function convertExpressions(array $query, ?ClassMetadata $classMetadata = null): array
{
if ($classMetadata === null) {
$classMetadata = $this->class;
}

$convertedQuery = [];
foreach ($query as $key => $value) {
if (is_string($key) && $classMetadata->hasAssociation($key)) {
$targetDocument = $classMetadata->getAssociationTargetClass($key);

if ($targetDocument) {
$fieldMetadata = $this->dm->getClassMetadata($targetDocument);
}
}

if (is_array($value)) {
$convertedQuery[$key] = $this->convertExpressions($value, $fieldMetadata ?? $classMetadata);
continue;
}

$convertedQuery[$key] = static::convertExpression($value, $fieldMetadata ?? $classMetadata);
}

return $convertedQuery;
}

/**
* Converts expression objects to query arrays. Non-expression values are
* returned unmodified.
*
* @param Expr|mixed $expression
*/
private static function convertExpression($expression, ClassMetadata $classMetadata)
{
if (! $expression instanceof Expr) {
return $expression;
}

$expression->setClassMetadata($classMetadata);

return $expression->getQuery();
}
}
57 changes: 57 additions & 0 deletions tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1674Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Doctrine\ODM\MongoDB\Tests\Functional\Ticket;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\ODM\MongoDB\Tests\BaseTest;
use MongoDB\BSON\ObjectId;

class GH1674Test extends BaseTest
{
public function testElemMatchUsesCorrectMapping()
{
$builder = $this->dm->createQueryBuilder(GH1674Document::class);
$builder
->field('embedded')
->elemMatch(
$builder->expr()
->field('id')
->equals(1)
);

$this->assertSame(
[
'embedded' => [
'$elemMatch' => ['id' => '1'],
],
],
$builder->getQueryArray()
);
}
}

/** @ODM\Document */
class GH1674Document
{
/** @ODM\Id */
protected $id;

/** @ODM\EmbedMany(targetDocument=GH1674Embedded::class) */
protected $embedded;

public function __construct()
{
$this->id = new ObjectId();
$this->embedded = new ArrayCollection();
}
}

/** @ODM\EmbeddedDocument */
class GH1674Embedded
{
/** @ODM\Field */
public $id = [];
}

0 comments on commit 05c9a25

Please sign in to comment.