3

I have a collection structure like so:

albums: {
    2oBkjqYFwf3vrgDj4: {
        _id: "2oBkjqYFwf3vrgDj4",
        titles: [
            {
                titleText: "i am an album"
            },
            {
                titleText: "this is my other title"
            }
        ]
    }
}

I want to do something like the below to update where the titleText is equal to something, then change it:

db.albums.update({"_id": "2oBkjqYFwf3vrgDj4", "titles": {$elemMatch: {"titleText": "i am an album"}}},
    {$set: {
        "titles.titleText": "i am not an album"
    }
)

I know I could do a foreach, but this seems like a lot of wasted resources as I plan on having an index on titles.titleText.

Is there something I'm missing, or is there not a simple way to do this? I'm using Meteor, but I don't think it should change any of the logic if there is a way to do this in MongoDB.

Thanks everyone!

Jordan
  • 1,003
  • 2
  • 12
  • 24
  • Possible duplicate of [Update an subdocument contained in an array contained in a MongoDB document](http://stackoverflow.com/questions/13777097/update-an-subdocument-contained-in-an-array-contained-in-a-mongodb-document) – 1615903 Jan 19 '16 at 09:14

3 Answers3

8

Turns out my question is a repost of link

This issue can be solved in MongoDB by doing,

db.albums.update({
        "_id": "2oBkjqYFwf3vrgDj4",
        "titles.titleText": "i am an album"
    },
    {$set:
        {"titles.$.titleText": "i am not an album"}
    }
)

When the previous question was posted the positional operator wasn't supported by minimongo. As of now mongo selectors can't be used from client side code , for security reasons.

The method above would have to be called from a Meteor.method() on the server.

Community
  • 1
  • 1
Jordan
  • 1,003
  • 2
  • 12
  • 24
0

With the meteor collection api you can use the update. documentation here. You provide a selector and then a modifier. You can also do the same thing directly with mongo. The general syntax is below but I haven't run it myself. I don't know if there exists a fancier way similar to your code that is perhaps DRY'er, but should get the job done.

Albums.update(
    {_id: "2oBkjqYFwf3vrgDj4", "titles.titleText": "i am an album"},
    {$set:{"titles.titleText": "i am not an album"}}
};
Shwaydogg
  • 2,499
  • 27
  • 28
  • It works, but you need to know the index in order for `$set` to work. So I would have to do `{$set:{"titles.0.titleText": "i am not an album"}}`, but this would mean I have to get the index before I can proceed to update it. Hopefully someone knows of a simpler way. – Jordan Apr 06 '14 at 21:11
  • I think you'll either need some js to find the index or you'd need to structure your db differently: have a collection for Albums and a collection for titles. Have one reference the ids of the other and ensureIndex() for fast cross referencing if needed. I doubt this is the solution you'll choose to take, but I think it's the only one that exists given the parameters without additional js. You'd have to consider how often you'd be taking this action and how long it'll take. I'd be happy to be proven wrong though! – Shwaydogg Apr 06 '14 at 21:27
  • Yeah, I knew someone would recommend restructuring, but this is definitely the fastest way for me to structure in terms of queries. I would be normalizing in way that I don't need, simply because there is not simply way to update a subdocument. – Jordan Apr 06 '14 at 21:32
  • If order doesn't matter you can $pull and $push. http://docs.mongodb.org/manual/reference/operator/update-array/ – Shwaydogg Apr 06 '14 at 21:34
  • Actually looks like this is it: http://docs.mongodb.org/manual/reference/operator/update/positional/#up._S_ checkout the last example. – Shwaydogg Apr 06 '14 at 21:35
  • Just found out how to do the query, going to post in answers, my question is a repost of [link](http://stackoverflow.com/questions/13777097/update-an-subdocument-contained-in-an-array-contained-in-a-mongodb-document?rq=1) – Jordan Apr 06 '14 at 21:38
  • Yep that's the same as above. – Shwaydogg Apr 06 '14 at 21:40
0

$set doesnt work nicely on the clientside. A nice work around is (foundt his in the parties) example.

step 1: get the array for your items.
    var album = albums.findOne().services;
step 2: lets find the index we want using _indexOf and _pluck
                 var indexToUpdate = _.indexOf(_.pluck('insert your array', 'insert your column name'), 'insert the one you want to update');

                var modifier = { $set: {} };
                modifier.$set["services." + indexToUpdate + ".youfieldinarray"] = 'your new value'.
step 3: update your item object      
albumns.update(Business.findOne()._id, modifier);

Not sure i would do this in huge collections but for smallers ones clientside it works well.

Piotr Stulinski
  • 9,241
  • 8
  • 31
  • 46