[SERVER-6982] positional operator does not work with negated match expressions Created: 09/Sep/12  Updated: 06/Dec/22  Resolved: 17/Jan/18

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

Type: Bug Priority: Major - P3
Reporter: Aaron Staple Assignee: Backlog - Query Team (Inactive)
Resolution: Duplicate Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
depends on SERVER-27089 Extend the update subsystem to suppor... Closed
Duplicate
duplicates SERVER-27089 Extend the update subsystem to suppor... Closed
Related
related to DOCS-2580 Positional operator page should list ... Closed
Assigned Teams:
Query
Backwards Compatibility: Fully Compatible
Operating System: ALL
Participants:

 Description   

The positional field name is not set when matching a negated expession (eg $ne, $nin, $not). So the $ positional operator does not work for these match expressions.

Test

c = db.c;
c.drop();
 
// $ operator works with equality match.                                                                 
c.save( { a:[ 1, 2 ] } );
c.update( { a:1 }, { $set:{ 'a.$':3 } } );
printjson( c.findOne() );
 
// $ operator does not work with $ne match.                                                              
c.update( { a:{ $ne:10 } }, { $set:{ 'a.$':5 } } );
printjson( db.getLastError() ); // The '$' field name is not populated.                                  
printjson( c.findOne() );
 
// $ operator does not work with $nin match.                                                             
c.update( { a:{ $nin:[ 10, 20 ] } }, { $set:{ 'a.$':5 } } );
printjson( db.getLastError() ); // The '$' field name is not populated.                                  
printjson( c.findOne() );
 
// $ operator does not work with $not match.                                                             
c.update( { a:{ $not:{ $gt:7 } } }, { $set:{ 'a.$':5 } } );
printjson( db.getLastError() ); // The '$' field name is not populated.                                  
printjson( c.findOne() );



 Comments   
Comment by Asya Kamsky [ 17/Jan/18 ]

This issue is resolved via SERVER-27089

Comment by Asya Kamsky [ 26/Feb/16 ]

Shoot, it appears I may have been looking at the wrong line (I was also testing $not expressions) so you're right, the $elemMatch syntax does allow it to work.

Comment by David Storch [ 25/Feb/16 ]

asya, I'm not sure what you mean. There's still a difference between $not/$ne inside an $elemMatch and $not/$ne without $elemMatch on 3.2.1:

> db.c.drop()
true
> db.c.insert({a: [1, 2, 3]})
WriteResult({ "nInserted" : 1 })
> db.c.update({a: {$elemMatch: {$ne: 99}}}, {$set: {"a.$": 5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.c.find()
{ "_id" : ObjectId("56cf6a36d5b88ea2026365d4"), "a" : [ 5, 2, 3 ] }
> db.c.update({a: {$ne: 99}}, {$set: {"a.$": 6}})
WriteResult({
	"nMatched" : 0,
	"nUpserted" : 0,
	"nModified" : 0,
	"writeError" : {
		"code" : 16837,
		"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: a.$"
	}
})

Do you have an example of the case you were referring to?

Comment by Asya Kamsky [ 25/Feb/16 ]

Both $ne and $elemMatch now (in 3.2.1) give the same error:

"The positional operator did not find the match needed from the query. Unexpanded update: a.$"

I think that's correct - we probably should not match on un-equality for positional match.

Comment by Asya Kamsky [ 23/Jan/14 ]

Opened DOCS-2580 to document limitation.

Comment by Asya Kamsky [ 23/Jan/14 ]

http://docs.mongodb.org/manual/reference/operator/update/positional/
should have a note about this.

However, the update does work if the negated expression is wrapped in $elemMatch:

"Positional updates don't work with negated match expressions unless $elemMatch is used".

c.update( { a:{ $not:{ $gt:7 } } }, { $set:{ 'a.$':5 } } );
printjson( db.getLastError() ); // The '$' field name is not populated.                                  
printjson( c.findOne() );

Result is array [5, 2]

Tested in both 2.4.8 and 2.5.4

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