6

I have a mongodb document with the following structure

> db.user.find().limit(1);
{ "_id" : "1", "foo" : { "bars" : [
    {
        "name" : "bar1"
    },
    {
        "name" : "bar2"
    },
], ... }, ... }

I want to add a new property to each bar. I've got my script iterating over the bars array, but I can't get the new property in there, how can I do this?

var users = db.user.find({"foo.bars":{$exists:true}});

users.forEach(function(user) {
    user.foo.bars.forEach(function(bar)
    {
       printjson(bar);
       //how can I specify the current 'bar' in this update?
       //db.experience.update({_id: user._id}, {$set: {"what goes here?" : "newbarValue"}});
    });
});
2
  • Are you sure that " want to add a new property to each bar" actually worked? Especially what do you want with 'user' inside {"user.foo.bars.${bar}? And what should the '${bar}' do? Can it be that you are trying to invent new syntax without having checked how things actually work? Commented Jul 20, 2011 at 4:19
  • "user.foo.bars.${bar}" is indicative - it's not valid syntax, you will notice its commented out. I was trying to express my intent. Commented Jul 20, 2011 at 4:32

3 Answers 3

10

So says the preacher man:

var users = db.user.find({"foo.bars":{$exists:true}});

users.forEach(function(user) {
    var id = user._id;

    var foo_bars = user.foo.bars;
    var new_foo_bars = [];

    for(var i = 0; i < foo_bars.length; i++) {
        var foo_bar = foo_bars[i];
        foo_bar['newkey'] = 'newvalue';
        new_foo_bars.push(foo_bar);
    }

    db.user.update({"_id":id}, {$set:{"foo.bars":new_foo_bars}});
});
Sign up to request clarification or add additional context in comments.

2 Comments

AFAIK, using forEach does not scale well, it would lock the entire database for as long as the update runs.
great answer but is there a way to avoid using foreach ? i am using the above method to retrieve a variable from another collection and add it to the result set of a collection before rendering the template. In sql i would do a join by id is there something similar for mongo?
1

I have noticed that you are scrolling through the array on the client side there in JS.

If you were to form a new "bars" array from the old one then push it in as a whole new value this would mean you only do one DB call and the code is quite elegant.

If MongoDB does not support it normally it is better to just do the work on the client side.

1 Comment

right -- you can add a single item under one of the array elements with $set by not to all of them. so do it client side. you can run the client script on the server via localhost and it should then perform pretty well.
0

You have to update each element of the nested document individually. Using the positional operator '$' will not work since it will only work on the first match (as documented).

5 Comments

What is not useful here? You just can't do it differently.
"You have to update each element of the nested document individually. " is essentially what I was asking how to do - restating it as an answer was not useful. Pointing me at the relevant documentation or providing a line of pseudo code would have been wonderful. Spent an hour trying to nut this one out before coming here - so I was dissapointed it gets down voted and suggested that Ive not already attempted to self solve with doco.
you can just use hard indices in array $set operations. So db.user.update({_id: user._id}, {$set: {'foo.bars.0.someValue' : "newbarValue"}}) works. All you have to do is replace 0 with a counter that you increase in your inner for loop if i understand your problem correctly.
@Remon. Thanks, it's a good suggestion. If I hard code an index in there, as you say it does work, but I can't seem to get update(...) to work when I try to use either a variable index in that string, or extracting a variable that builds "foo.bars." + i + ".someValue" and uses that variable in the update(...) function.
Yes you can't do it in JS (or at least I don't know how either, not an expert) I don't think but that's only a problem in the shell or if your using node. Most other languages will do that just fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.