Best way to replace a particular array element/value

4,345 views
Skip to first unread message

Projapati

unread,
Apr 15, 2011, 7:22:55 PM4/15/11
to mongodb-user
I have some strings in an array within my document.
I need to replace a particular value from the array.

Currently I am pulling out the current value and then pushing the new
value. It works.
db.col.update({_id:0},{$pull:{"field":"old_value"}})
db.col.update({_id:0},{$push:{"field":"new_value"}})

This involves 2 update operations.

Can it be done in atomic way like a[idx] = "new_value" like we do in C/
C#?

I tried $elemMatch but the positional operator $ seems not working.

Thanks

Gaetan Voyer-Perrault

unread,
Apr 15, 2011, 7:38:13 PM4/15/11
to mongod...@googlegroups.com
> Can it be done in atomic way like a[idx] = "new_value" like we do in C/C#?

No this cannot be done atomically at the moment.

There are several JIRA tickets related to such a feature:
https://jira.mongodb.org/browse/SERVER-991

To follow the status or vote on these, you will need to log into JIRA with your account.
You can click on the "Vote for it" or "Watch it" links on the left side of page.
If you do not have an account, you can create one for free.

- Gates



--
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To post to this group, send email to mongod...@googlegroups.com.
To unsubscribe from this group, send email to mongodb-user...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mongodb-user?hl=en.


Projapati

unread,
Apr 15, 2011, 8:06:51 PM4/15/11
to mongodb-user
Thanks. Voted the jiras up.

On Apr 15, 4:38 pm, Gaetan Voyer-Perrault <ga...@10gen.com> wrote:
> > Can it be done in atomic way like a[idx] = "new_value" like we do in C/C#?
>
> No this cannot be done atomically at the moment.
>
> There are several JIRA tickets related to such a feature:https://jira.mongodb.org/browse/SERVER-991
> <https://jira.mongodb.org/browse/SERVER-991>https://jira.mongodb.org/browse/SERVER-1050

Keith Branton

unread,
Apr 16, 2011, 12:11:54 AM4/16/11
to mongod...@googlegroups.com
This depends on what your data looks like, but if it resembles this:

> db.a.insert({_id:0,field:["one_value","old_value","another_value"]})
> db.a.find()
{ "_id" : 0, "field" : [ "one_value", "old_value", "another_value" ] }

then this update:

> db.a.update({_id:0,field:"old_value"},{$set:{"field.$":"new_value"}})

results in this:

> db.a.find()                                                          
{ "_id" : 0, "field" : [ "one_value", "new_value", "another_value" ] }

does that help?

If not then an example of the data your are trying to manipulate would be useful.


Projapati

unread,
Apr 16, 2011, 2:02:47 AM4/16/11
to mongodb-user
Keith,
I am pretty sure I tried this before posting with my tags & it didn't
work.
In general, your answers are great. I will retry this and let you
know.

Thanks
Message has been deleted

Projapati

unread,
Apr 16, 2011, 3:22:26 AM4/16/11
to mongodb-user
It works! I am not sure what I missed earlier.
One side effect of this approach is that it will allow duplicate tags.
If there is a tag "abc" and user replaces "xyz" with "abc", it will
create 2 "abc" tags.

On the other hand if I do with 2 updates, the problem is not there.
db.albums.update({_id:0},{$pull:{"tags":"xyz"}})
db.albums.update({_id:0},{$addToSet:{"tags":"abc"}})

Keith Branton

unread,
Apr 16, 2011, 11:43:29 AM4/16/11
to mongod...@googlegroups.com
That's true, though with that approach, unless you are only executing the second update if the first update actually pulled a value, then your second update will add the value regardless, even if the first value wasn't in the array before. 

Not sure if that's a behavior you want, but my method (which works much more like a[idx] = "new_value") wouldn't do that.

It all depends on what you really need.

Keith Branton

unread,
Apr 16, 2011, 12:11:22 PM4/16/11
to mongod...@googlegroups.com
Maybe doing it like this would be better:

update({id, array has old_item, array doesn't have new_item},{$set old item to new item})
update({id, array has old_item and new_item},{$pull old_item})

it's still two updates, but the difference is that only one would actually change the document, so the other would be very fast since it wouldn't do anything. Actually you could detect if the first one does update a record and not bother running the second if it does, but you could also just run these fire and forget if you don't care to wait.

so your code would look like:

> db.a.remove()                                                                   
> db.a.insert({_id:0,field:["one_value","old_value","old_value2","another_value"]})  
> db.a.find()                                                                      
{ "_id" : 0, "field" : [ "one_value", "old_value", "old_value2", "another_value" ] }

> db.a.update({_id:0,field:{$in:["old_value"],$ne:"new_value"}},{$set:{"field.$":"new_value"}})
> db.a.update({_id:0,field:{$in:["old_value","new_value"]}}, {$pull:{field:"old_value"}})      
> db.a.find()                                                                                  
{ "_id" : 0, "field" : [ "one_value", "new_value", "old_value2", "another_value" ] }

> db.a.update({_id:0,field:{$in:["old_value2"],$ne:"new_value"}},{$set:{"field.$":"new_value"}})
> db.a.update({_id:0,field:{$in:["old_value2","new_value"]}}, {$pull:{field:"old_value2"}})     
> db.a.find()                                                                              
{ "_id" : 0, "field" : [ "one_value", "new_value", "another_value" ] }

but this approach doesn't add new_value if it's not already in the list, so it depends if you want that too (I'm not sure I can see why you would want to add a tag and optionally replace an existing tag in the same operation in a tagging use case)
Reply all
Reply to author
Forward
0 new messages