简体   繁体   中英

how to push a dictionary to a nested array with mongodb?

i have data that looks like this in my database

> db.whocs_up.find()
{ "_id" : ObjectId("52ce212cb17120063b9e3869"), "project" : "asnclkdacd", "users" : [ ] }

and i tried to add to the 'users' array like thus:

> db.whocs_up.update({'users.user': 'usex', 'project' : 'asnclkdacd'  },{ '$addToSet': { 'users': {'user':'userx', 'lastactivity' :2387843543}}},true)

but i get the following error:

Cannot apply $addToSet modifier to non-array

same thing happens with push operator, what im i doing wrong? im on 2.4.8

i tried to follow this example from here: MongoDB - Update objects in a document's array (nested updating)

db.bar.update( {user_id : 123456, "items.item_name" : {$ne : "my_item_two" }} , 
            {$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
            false , 
            true)

the python tag is because i was working with python when i ran into this, but it does nto work on the mongo shell as you can see

EDIT ============================== GOT IT TO WORK

apparently if i modify the update from

db.whocs_up.update({'users.user': 'usex', 'project' : 'asnclkdacd'  },{ '$addToSet': { 'users': {'user':'userx', 'lastactivity' :2387843543}}},true)

to this:

db.whocs_up.update({'project' : 'asnclkdacd'  },{ '$addToSet': { 'users': {'user':'userx', 'lastactivity' :2387843543}}},true)

it works, but can anyone explain why the two do not achieve the same thing, in my understanding they should have referenced the same document and hence done the same thing, What does the addition of 'users.user': 'userx' change in the update? does it refer to some inner document in the array rather than the document as a whole?

This is a known bug in MongoDB (SERVER-3946) . Currently, an update with $push / $addToSet with a query on the same field does not work as expected.

In the general case, there are a couple of workarounds:

  1. Restructure your update operation to not have to query on a field that is also to be updated using $push / $addToSet (as you have done above).
  2. Use the $all operator in the query, supplying a single-value array containing the lookup value. eg instead of this:

     db.foo.update({ x : "a" }, { $addToSet : { x : "b" } }, true) 

    do this:

     db.foo.update({ x : { $all : ["a"] } }, { $addToSet : { x : "b" } } , true) 

In your specific case, I think you need to re-evaluate the operation you're trying to do. The update operation you have above will add a new array entry for each unique (user, lastactivity) pair, which is probably not what you want. I assume you want a unique entry for each user.

Consider changing your schema so that you have one document per user:

{ 
    _id : "userx",
    project : "myproj",
    lastactivity : 123,
    ...
}

The update operation then becomes something like:

db.users.update({ _id : "userx" }, { $set : { lastactivity : 456 } })

All users in a given project may still be looked up efficiently by adding a secondary index on project.

This schema also avoids the unbounded document growth of the above schema, which is better for performance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM