Add support for using $text operator in a aggregation with a filter#2739
Add support for using $text operator in a aggregation with a filter#2739GromNaN merged 2 commits intodoctrine:2.11.xfrom
$text operator in a aggregation with a filter#2739Conversation
| if (isset($pipeline[0]['$match'])) { | ||
| // Merge filters into the first $match stage with a logical $and | ||
| $pipeline[0]['$match'] = [ | ||
| '$and' => [ |
There was a problem hiding this comment.
Starting a separate comment thread for this. Apologies for not asking this sooner, but do you know why the $geoNear stage is able to use:
$this->applyFilters($pipeline[0]['$geoNear']['query']);Since that merges the filter and discriminator criteria into the query, I'm curious as to why we could do the following instead of $and:
$pipeline[0]['$match'] = $this->applyFilters($pipeline[0]['$match']);There was a problem hiding this comment.
Excellent point, I probably didn't go far enough in my exploration. In fact, CriteriaMerger already does the job very well.
mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/CriteriaMerger.php
Lines 27 to 41 in fd0ad45
I modifies the implementation to match with $geoNear. Good news, I didn't have to touch the tests and it cleanly drops empty $match criteria.
GromNaN
left a comment
There was a problem hiding this comment.
It's getting bigger than expected, but that's no bad thing.
| $stage = ['near' => [0, 0], 'spherical' => false, 'distanceField' => null, 'query' => (object) [], 'num' => 1]; | ||
| self::assertEquals([['$geoNear' => $stage]], $builder->getPipeline()); |
There was a problem hiding this comment.
This was an error: $geoNear.query does not accept an empty array
MongoServerError[TypeMismatch]: query must be an object
There was a problem hiding this comment.
Does this suggest that the geoNear() builder method never worked in previous versions when query criteria was omitted? That seems like something worthy of its own ticket for changelog visibility, even if you want to keep the fix in this PR.
| $query = $documentPersister->addFilterToPreparedQuery($query); | ||
|
|
||
| return $query; | ||
| return $query === [] ? (object) $query : $query; |
There was a problem hiding this comment.
I have to juggle between array and empty stdClass to ensure that we have a map when converting to BSON.
| } | ||
|
|
||
| if ($this->getStage(0) instanceof Stage\GeoNear) { | ||
| if (isset($pipeline[0]['$geoNear'])) { |
There was a problem hiding this comment.
This is more efficient and more resilient if we introduce changes on the $pipeline array like #2740.
jmikola
left a comment
There was a problem hiding this comment.
You can decide if it's worth reporting a separate issue for the outstanding geoNear() bug.
| $stage = ['near' => [0, 0], 'spherical' => false, 'distanceField' => null, 'query' => (object) [], 'num' => 1]; | ||
| self::assertEquals([['$geoNear' => $stage]], $builder->getPipeline()); |
There was a problem hiding this comment.
Does this suggest that the geoNear() builder method never worked in previous versions when query criteria was omitted? That seems like something worthy of its own ticket for changelog visibility, even if you want to keep the fix in this PR.
| 'stype' => 'server_guest', | ||
| '$text' => ['$search' => 'Paul'], | ||
| ], | ||
| ['filtered' => true], |
There was a problem hiding this comment.
The query here seems odd, but I understand that's because addDiscriminatorToPreparedQuery() sets the key directly and addFilterToPreparedQuery() uses CriteriaMerger, which will always adds $and for 2+ criteria.
$text operator in a aggregation with a filter$text operator in a aggregation with a filter and fix $geoNear stage with empty query
$text operator in a aggregation with a filter and fix $geoNear stage with empty query$text operator in a aggregation with a filter
Summary
The
$textoperator must be in the first$matchstage. Instead of prepending the pipeline with an other$matchstage for the filter, the builder merge them using a logical$and.We cannot detect when
$textis used as this can be deep in the$matchquery, so the behavior is for all the pipelines.