Chazt3n
Chazt3n

Reputation: 1651

MongoDb update multiple Specific Embedded documents within the same array

I have a use case where a view allows the user to update multiple objects and submit at once, how can I make this atomic?

{_id: parent,
 childrenA: [
   {_id: child1, property: "update-me", property2: "leave-alone"},
   {_id: child2, property: "leave-alone", property2: "update-me"}
 ],
 propertyA: "update-me",
 propertyB: "leave-alone", //someone else needs to be able to update this concurrently with this change.
 childrenB:[
   {property: "update-me", property2: "leave-alone"},
   {property: "leave-alone", property2: "update-me"}
 ],

}

property may or may not be another array of nested objects. Is any of this possible programmatically?

EDIT: I need to mention that I cannot reliably update the entire document in some cases, embedded documents can be replaced (address, maybe)

however, I need to aggregate a list of changes e.g. [{"child[Id=child1].FirstName", "newName"},{"child[Id=child3].LastName", "newName"} (not necessarily that syntax, but a change dictionary)

Upvotes: 3

Views: 2153

Answers (2)

Roshan Vishva
Roshan Vishva

Reputation: 133

You can use the following form version 3.4

db.Collection.findAndModify({
query: {  "_id" : "parent"},
update: { $set: {propertyA: "update-me" , "childrenA.$[childrenAelemnt].property" : "update-me" , "childrenB.$[childrenB2elemnt].property2" : "update-me"
},
arrayFilters: [ {"childrenAelemnt._id": "child1"},{"childrenBelemnt.property2": "leave-alone"} , {"childrenB2elemnt.property": "leave-alone"} ]})

If there is an ID field in childrenB array. it would have been little easier and syntax would have been little consistent

Upvotes: 1

Dĵ ΝιΓΞΗΛψΚ
Dĵ ΝιΓΞΗΛψΚ

Reputation: 5669

it could be done with one limitation to the best of my knowledge. someone correct me if i'm wrong please. here's the update command:

db.Parents.update(
    {
        "_id": ObjectId("5cf7391a1c86292244c4424e"),
        "ChildrenA": {
            "$elemMatch": {
                "_id": ObjectId("5cf7391a1c86292244c4424c")
            }
        }
    },
    {
        "$set": {
            "ChildrenA.$.Property": "UPDATED",
            "PropertyA": "UPDATED",
            "ChildrenB.0.Property": "UPDATED",
            "ChildrenB.1.Property2": "UPDATED"
        }
    }
)

as you can see you have to use $elemMatch to target a nested child by ID. and from what i can tell you can only have one $elemMatch in a single update command (correct me if i'm wrong).

here's the c# code that generated the above update command. it is using MongoDB.Entities which is a convenience library which i'm the author of.

using MongoDB.Entities;

namespace StackOverflow
{
    public class Program
    {
        public class Parent : Entity
        {
            public ChildA[] ChildrenA { get; set; }
            public string PropertyA { get; set; }
            public string PropertyB { get; set; }
            public ChildB[] ChildrenB { get; set; }
        }

        public class ChildA : Entity
        {
            public string Property { get; set; }
            public string Property2 { get; set; }
        }

        public class ChildB
        {
            public string Property { get; set; }
            public string Property2 { get; set; }
        }

        static void Main(string[] args)
        {
            new DB("test");

            var childA = new ChildA { Property = "update-me", Property2 = "leave-me-alone" };
            var childB = new ChildA { Property = "leave-alone", Property2 = "update-me" };
            childA.Save(); childB.Save();

            var parent = new Parent
            {
                ChildrenA = new[] { childA, childB },
                PropertyA = "update-me",
                PropertyB = "leave-me-alone",
                ChildrenB = new[] {
                new ChildB{ Property = "update-me", Property2 = "leave-me-alone"},
                new ChildB{ Property = "leave-alone", Property2 = "update-me"}
                }
            };
            parent.Save();

            DB.Update<Parent>()
              .Match(
                f => f.Eq(p => p.ID, parent.ID) &
                f.ElemMatch(
                    x => x.ChildrenA, 
                    x => x.ID == childA.ID))
              .Modify(x => x.ChildrenA[-1].Property, "UPDATED")
              .Modify(x => x.PropertyA, "UPDATED")
              .Modify(x => x.ChildrenB[0].Property, "UPDATED")
              .Modify(x => x.ChildrenB[1].Property2, "UPDATED")
              .Execute();
        }
    }
}

Upvotes: 1

Related Questions