简体   繁体   中英

Casbah MongoDB, how to both add and remove values to an array in a single operation, to multiple documents?

After searching, I was unable to figure out how to perform multiple updates to a single field.

I have a document with a "tags" array field. Every document will have random tags before I begin the update. In a single operation, I want to add some tags and remove some tags.

The following update operator returns an error "Invalid modifier specified: $and"

    updateOperators: { "$and" : [
      { "$addToSet" : { "tags" : { "$each" : [ "tag_1" , "tag_2"]}}},
      { "$pullAll" : { "tags" : [ "tag_2", "tag_3"]}}]}  

    collection.update(query, updateOperators, multi=true)  

How do I both add and remove values to an array in a single operation, to multiple documents?

You don't need the $and with the update query, but you cannot update two fields at the same time with an update - as you would see if you tried the following in the shell:

db.test.update({}, { "$addToSet" : { "tags" : { "$each" : [ "tag_1" , "tag_2"]}}, 
                     "$pullAll"  : { "tags" : [ "tag_2", "tag_3"] }}, true, false)

You would get a Cannot update 'tags' and 'tags' at the same time error message. So how to achieve this? Well with this schema you would need to do it in multiple operations, you could use the new bulk operation api as shown below (shell):

var bulk = db.coll.initializeOrderedBulkOp();
bulk.find({ "tags": 1 }).updateOne({ "$addToSet": { "$each" : [ "tag_1" , "tag_2"]}});
bulk.find({ "tags": 1 }).updateOne({ "$pullAll": { "tags": [ "tag_2", "tag_3"] } });
bulk.execute();

Or in Casbah with the dsl helpers:

val bulk = collection.initializeOrderedBulkOperation
bulk.find(MongoDBObject("tags" -> 1)).updateOne($addToSet("tags") $each("tag_1", tag_2"))
bulk.find(MongoDBObject("tags" -> 1)).updateOne($pullAll("tags" -> ("tags_2", "tags_3")))
bulk.execute()

Its not atomic and there is no guarantee that nothing else will try to modify, but it is as close as you will currently get.

Mongo does atomic updates so you could just construct the tags you want in the array and then replace the entire array.

I would advise against using an array to store these values all together as this is an "unbound" array of tags. Unbound arrays cause movement on disk and that causes indexes to be updated and the OS and mongo to do work.

Instead you should store each tag as a seperate document in a different collection and " bucket " them based on the _id of the related document.

Example

{_id : <_id> <key> <value>} - single docuemnt

This will allow you to query for all the tags for a single user with db.collection.find({_id : /^<_id>/}) and bucket the results.

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