16

I have this data in Mongo:

{
    "_id" : ObjectId("505fd43fdbed3dd93f0ae088"),
    "categoryName" : "Cat 1",
    "services" : [
        {
            "serviceName" : "Svc 1",
            "input" : [
                { "quantity" : 10, "note" : "quantity = 10" }, 
                { "quantity" : 20, "note" : "quantity = 20" }
            ]
        },
        {
            "serviceName" : "Svc 2",
            "input" : [
                { "quantity" : 30, "note" : "quantity = 30" }, 
                { "quantity" : 40, "note" : "quantity = 40" }
            ]
        }
    ]
}

Now I want to update a quantity for "Svc 1":

{ "quantity" : 10, "note" : "quantity = 10" }

Like:

{"quantity": 100, "note": "changed to 100"}

How can I do with Mongo?`

As I know, operational operator only supports for the first array, someone advised to use index of an element of the sub sub array, but the problem is that how can know that index at run time? (I'm using native C# driver of MongoDB)

Thanks in advance for your helps!

Johnny

2 Answers 2

16

Since you have an array within an array, there isn't any easy way to reference the nested subarray unless you know the position in the array you want to update.

So, for example, you could update the first input for 'Svc 1' with the C# equivalent of the below.

db.services.update(

    // Criteria
    {
        '_id' : ObjectId("505fd43fdbed3dd93f0ae088"),
        'services.serviceName' : 'Svc 1'
    },

    // Updates
    {
        $set : {
            'services.$.input.0.quantity' : 100,
            'services.$.input.0.note' : 'Quantity updated to 100'
        }
    }
)

Notice the use of the $ (positional update operator) in services.$.input...

If you don't know the position for the nested input array, you will have to fetch the matching services, iterate the input array in your application code, then $set the updated array.

Alternatively, you could modify your nested array to use an embedded document instead, eg:

{
    "categoryName" : "Cat 1",
    "services" : [
        {
            "serviceName" : "Svc 1",
            "input1" : { "quantity" : 10, "note" : "quantity = 10" }, 
            "input2" : { "quantity" : 20, "note" : "quantity = 20" }
        },
    ]
}

Which you could then update by name, eg input1:

db.services.update(

    // Criteria
    {
        '_id' : ObjectId("5063e80a275c457549de2362"),
        'services.serviceName' : 'Svc 1'
    },

    // Updates
    {
        $set : {
            'services.$.input1.quantity' : 100,
            'services.$.input1.note' : 'Quantity updated to 100'
        }
    }
)
Sign up to request clarification or add additional context in comments.

3 Comments

There is a related request SERVER-267 (partial wildcard support ) in the MongoDB issue tracker.
Thanks for your help, Stennie! I currently group input and output array to another collection as a workaround.
Fyi embedded document can result in data loss if the document is hot.
8

Since you don't know the position of the value wanted to update, first insert a new value with updated information and then remove the value wanted to update.

db.services.update(
   {
    '_id' : ObjectId("505fd43fdbed3dd93f0ae088"),
    'services.serviceName' : 'Svc 1'
   },
   {
    { $addToSet: { 'services.$.input' : "new sub-Doc" }
   }
)

And then remove when insert is success

db.services.update(
   {
    '_id' : ObjectId("505fd43fdbed3dd93f0ae088"),
    'services.serviceName' : 'Svc 1'
   },
   {
    { $pull: { 'services.$.input' : { "quantity" : 10, "note" : "quantity = 10" } }
   }
)

This is useful when index is not known and document should have sub-documents having same key like "input" in post Update an element in sub of sub array in mongodb

1 Comment

This was helpful. Now I'm switching back to MySql. Thanks.

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.