[SERVER-11537] Incosistent behavior with $or and $elemMatch Created: 03/Nov/13  Updated: 10/Dec/14  Resolved: 08/Aug/14

Status: Closed
Project: Core Server
Component/s: Querying, Write Ops
Affects Version/s: 2.4.8, 2.6.3
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: NOVALUE Mitar Assignee: David Storch
Resolution: Duplicate Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Mac OS X 10.7.5.


Issue Links:
Duplicate
duplicates SERVER-14662 Positional projection queries (and po... Closed
Related
related to SERVER-12981 $or in $elemMatch works inconsistentl... Closed
related to SERVER-14662 Positional projection queries (and po... Closed
is related to SERVER-11536 $elemMatch matches too many elements Closed
is related to SERVER-14837 Positional Operator matching wrong ne... Closed
Operating System: ALL
Steps To Reproduce:

If you do queries:

> db.test.insert({nested: [{a: 1, slug: 'aaa'}, {a: 1, slug: 'aaa'}, {a: 2, slug: 'aaa'}]})
> db.test.find()
{ "_id" : ObjectId("52763c5cb6786ce4ff406cbf"), "nested" : [ { "a" : 1, "slug" : "aaa" }, { "a" : 1, "slug" : "aaa" }, { "a" : 2, "slug" : "aaa" } ] }
> db.test.update({'nested.a': 1, $or: [{'nested': {$elemMatch: {a: 1, slug: {$exists: true}}}}]}, {$unset: {'nested.$.slug': ''}})
> db.test.find()
{ "_id" : ObjectId("52763c5cb6786ce4ff406cbf"), "nested" : [ { "a" : 1 }, { "a" : 1, "slug" : "aaa" }, { "a" : 2, "slug" : "aaa" } ] }
> db.test.update({'nested.a': 1, $or: [{'nested': {$elemMatch: {a: 1, slug: {$exists: true}}}}]}, {$unset: {'nested.$.slug': ''}})
> db.test.find()
{ "_id" : ObjectId("52763c5cb6786ce4ff406cbf"), "nested" : [ { "a" : 1 }, { "a" : 1, "slug" : "aaa" }, { "a" : 2, "slug" : "aaa" } ] }

I would expect that the second update will remove slug from second a == 1 array element. But it does not. On the other hand, if I run:

db.test.update({'nested.a': 1, 'nested': {$elemMatch: {a: 1, slug: {$exists: true}}}}, {$unset: {'nested.$.slug': ''}})

Second slug is correctly removed.

Participants:

 Description   

There seems to be inconsistency when using $or in combination with $elemMatch. See steps to reproduce.



 Comments   
Comment by David Storch [ 08/Aug/14 ]

Hi mitar,

Thanks very much for reporting this issue. We have identified this to be a duplicate of SERVER-14662. I will give a brief explanation of what is happening here, but there is much more detail in the comments of SERVER-14662.

For both positional projections and positional updates, the query system must identify the array element to which the projection or update should be applied. (Both uses of the "$" operator share an implementation, which means that this issue affects projections as well as the positional update case reported here.) The problem is that when there are multiple predicates over the array being projected/updated, it is undefined which the query system should use to determine the positional match. Consider the example from the "Repro Steps" box above:

db.test.update({'nested.a': 1, 'nested': {$elemMatch: {a: 1, slug: {$exists: true}}}}, {$unset: {'nested.$.slug': ''}})

The query part of this update has two predicates over "nested", as indicated below:

db.test.update({'nested.a': 1,         \\ predicate #1
                'nested': {$elemMatch: \\ predicate #2
                          {a: 1, slug: {$exists: true}}}},
                {$unset: {'nested.$.slug': ''}})

The query system arbitrarily chooses to use predicate #1 in order to determine the positional match, rather than predicate #2. This is the underlying cause of the behavior reported here.

I'm going to resolve this ticket as a duplicate, but please feel free to reach out with any further questions or concerns.

Best,
Dave

Comment by NOVALUE Mitar [ 03/Nov/13 ]

And a similar query with $and works as well:

db.test.update({'nested.a': 1, $and: [{'nested': {$elemMatch: {a: 1, slug: {$ne: 'bbb'}}}}]}, {$set: {'nested.$.slug': 'bbb'}})

Comment by NOVALUE Mitar [ 03/Nov/13 ]

db.test.update({'nested.a': 1, 'nested': {$elemMatch: {$or: [{a: 1, slug: {$exists: true}}]}}}, {$unset: {'nested.$.slug': ''}})

works as well.

Generated at Thu Feb 08 03:26:04 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.