[SERVER-8095] Update with positional operator does not update index Created: 07/Jan/13  Updated: 07/Mar/14  Resolved: 10/Jan/13

Status: Closed
Project: Core Server
Component/s: Index Maintenance, Write Ops
Affects Version/s: 2.2.2
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Dave Longley Assignee: Thomas Rueckstiess
Resolution: Duplicate Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
duplicates SERVER-7511 update code does not properly determi... Closed
Operating System: ALL
Steps To Reproduce:

MongoDB shell version: 2.2.2
connecting to: test
> use test;
switched to db test
> db.test.insert({'array': [

{'a': 'b'}

]});
> db.test.find(

{'array.a': 'b'}

);
{ "_id" : ObjectId("50eae4ba4c08c986d476d22b"), "array" : [

{ "a" : "b" }

] }
> db.test.update(

{'array.a': 'b'}

, {$set: {'array.$':

{'a': 'c'}

}});
> db.test.find(

{'array.a': 'c'}

);
{ "_id" : ObjectId("50eae4ba4c08c986d476d22b"), "array" : [

{ "a" : "c" }

] }
> db.test.ensureIndex(

{'array.a': 1}

);
> db.test.find(

{'array.a': 'c'}

);
{ "_id" : ObjectId("50eae4ba4c08c986d476d22b"), "array" : [

{ "a" : "c" }

] }
> db.test.update(

{'array.a': 'c'}

, {$set: {'array.$':

{'a': 'b'}

}});
> db.test.find(

{'array.a': 'b'}

);
// NOTE: Document NOT found here when it should be
> db.test.dropIndex('array.a_1');

{ "nIndexesWas" : 2, "ok" : 1 }

> db.test.find(

{'array.a': 'b'}

);
{ "_id" : ObjectId("50eae4ba4c08c986d476d22b"), "array" : [

{ "a" : "b" }

] }

Participants:

 Description   

When performing an update using the positional operator on a field that contains an indexed subfield, the index is not updated. The result is that the document cannot be found via the index after the update. If you drop the index, the document can be found.



 Comments   
Comment by David I. Lehn [ 07/Jan/13 ]

In case anyone else comes across this bug, a workaround is to use a bogus subproperty such as 'data' just to store your real object:

// SERVER-8095: Update with positional operator does not update index
// Check $set on a full object as a property of another object in an array.
// This is a workaround to avoid the failure in update_arraymatch11.
 
t = db.jstests_update_arraymatch12;
t.drop();
 
t.ensureIndex( {'array.data.name': 1} );
t.insert( {'array': [{'data': {'name': 'old'}}]} );
assert( t.findOne({'array.data.name': 'old'}) );
t.update( {'array.data.name': 'old'}, {$set: {'array.$.data': {'name': 'new'}}} );
assert( t.findOne({'array.data.name': 'new'}) );
assert( !t.findOne({'array.data.name': 'old'}) );

Comment by Thomas Rueckstiess [ 07/Jan/13 ]

Thanks, Dave and David, for reporting this bug and the jstest. I'm currently trying to confirm that this is the same as SERVER-7511. If it turns out to be, we will close this issue and continue updating the preceding SERVER-7511 ticket instead.

Comment by Thomas Rueckstiess [ 07/Jan/13 ]

This issue is very similar (if not the same) to SERVER-7511. See causes and workarounds on the other ticket.

Comment by David I. Lehn [ 07/Jan/13 ]

I wrote a couple tests similar to mongo/jstests/update_arraymatch#.js to help debug this. 2.0.7 and 2.2.2 indexing works fine in some cases when using the positional operator:

  • OK: if you $inc a simple value in an array (ie, $inc a counter like [0])
  • OK: if you $inc a property of an object in an array (ie, $inc 'count' in [ {count:0}

    ])

  • OK: if you $set a property of an object in an array (ie, $set 'a' in [ {a: 'b'}

    ]

However, it fails when you $set a whole object instead of just a property. The index is not updated, the new data can't be found, and you will get the new data when you query for the old data! Below is the failing test case with a check to also make sure old data is not found. I can make a pull request for this and the others if needed.

// SERVER-8095: Update with positional operator does not update index
// Check $set on a full object in an array.
 
t = db.jstests_update_arraymatch11;
t.drop();
 
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'name': 'old'}]} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {'array.name': 'old'}, {$set: {'array.$': {'name': 'new'}}} );
assert( t.findOne({'array.name': 'new'}) );   
assert( !t.findOne({'array.name': 'old'}) );   

Comment by Dave Longley [ 07/Jan/13 ]

May be related to: https://jira.mongodb.org/browse/SERVER-5067

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