[SERVER-29347] Why is MongoDB treating queries differently when served from same index? Created: 24/May/17 Updated: 12/Jul/17 Resolved: 09/Jun/17 |
|
| Status: | Closed |
| Project: | Core Server |
| Component/s: | Querying |
| Affects Version/s: | 3.4.2 |
| Fix Version/s: | None |
| Type: | Question | Priority: | Major - P3 |
| Reporter: | Michal Bigos | Assignee: | Mark Agarunov |
| Resolution: | Done | Votes: | 0 |
| Labels: | None | ||
| Remaining Estimate: | Not Specified | ||
| Time Spent: | Not Specified | ||
| Original Estimate: | Not Specified | ||
| Issue Links: |
|
||||||||
| Participants: | |||||||||
| Description |
|
I have a collection where my documents looks like:
I have defined an index as:
When I do a query like:
The explain plan looks like:
It does IXSCAN followed by FETCH stage with filter. I've already created an question about the filter here.
i.e. expression `'name.de': { $exists: true }` should be still subset of `partialFilterExpression`. As stated in documentation: > To use the partial index, a query must contain the filter expression (or a modified filter expression that specifies a subset of the filter expression) as part of its query condition. But the explain plan looks like this:
As you can see index is used, but the whole filtering is happening in FETCH stage. Question is: *why the filtering is done in FETCH stage and what is so different between these 2 queries that MongoDB them differently?* Additionaly, sort query with `$exists` as:
Behaves the same, whole filtering and sorting is done after IXSCAN stage:
It even produces the incorrect results, while index is not used for sorting. |
| Comments |
| Comment by Mark Agarunov [ 09/Jun/17 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Hello michal bigos Thank you for the response. The behavior you are seeing is likely due to the issues detailed in SERVER-23808 and SERVER-26580. Please watch those tickets for updates on this issue. Thanks, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Michal Bigos [ 29/May/17 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
In relation to, already closed issue, with the same meaning, but different model (see https://jira.mongodb.org/browse/SERVER-29346). I'm tending to use the model:
Here I can define index like this:
The query defined as:
makes COLLSCAN, which is caused by missing 'name.locale': 'de' in filter, thus not matching the partialFilterExpression. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Michal Bigos [ 29/May/17 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
First of all thanks, yes you're right `$elemMatch` is what I want. I also undestand the `$exists` constraint as legit.
MongoDB already knows when searching for matching index that `"name.de": {$exists: true}` part of the filter matches `partialFilterExpression` and thus everything which comes from this matching index already fullfils the condition, thus condition can be eliminated and query served as:
i.e. from selected index and without filter for `$exists`. I'm not sure how `$elemMatch` is served internally, but it seems to remain in FETCH stage filter. *What is the reason for that?*
*Is there any way how to work around this?* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by David Storch [ 24/May/17 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
You may get the behavior you want if you use $elemMatch:
Note that this changes the semantics of the query such that the same subdocument in the names array must simultaneously match both the conditions on name.de and name.text. Looking at your schema, it looks like this might be the intended meaning of the query? Also note that the bounds associated with an {$exists: true} predicate will always be [MinKey, MaxKey]. Furthermore, we always must fetch the documents and apply the {$exists: true}. This is because literal null values and missing values both generate the same index keys, so we must fetch the document in order to distinguish between these two cases. (Literal nulls match the {$exists: true} but missing values do not.) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Michal Bigos [ 24/May/17 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Using the index without collation as: db.names.createIndex( { 'name.de': 1, 'name.text': 1 }, { name: 'name_de', partialFilterExpression: { 'name.de': { $exists: true }} }); yields the same behaviour. |