4

I have a document in MongoDB that looks like that:

{
    "Id":"123",
    "Product": "test",
    "Tags":[
        {
            "Name": "name",
            "Categories": [
                {
                    //item
                },
                {
                    //item
                }
            ]
        },
        {
            "Name": "name",
            "Categories": [
                {
                    //item
                },
                {
                    //item
                }
            ]
        }
    ]
}

Now, I need to add a new Item and it needs to be added to all of the categories of the Tags element of that Product by its Id. For example, when I'll insert Item 3 the document should look like this:

{
    "Id":"123",
    "Product": "test",
    "Tags":[
        {
            "Name": "name",
            "Categories": [
                {
                    //item 1
                },
                {
                    //item 2
                },
                {
                    //item 3
                }
            ]
        },
        {
            "Name": "name",
            "Categories": [
                {
                    //item 1
                },
                {
                    //item 2
                },
                {
                    //item 3
                }
            ]
        }
    ]
}

and same goes for removing an item, it needs to be removed from all of the categories as well. Is there a way to do that with the C# MongoDB Driver without pulling the object and "manually" updating them?

Liran Friedman
  • 4,027
  • 13
  • 53
  • 96

1 Answers1

11

You can try something like below in 2.5 driver with 3.6 version.

Finds the document with filter criteria and update which includes new positional identifier to update multiple elements in array inside UpdateOne method.

$[] updates all the Tags arrays to include new item in all Categories array. It acts as a placeholder for updating all elements in array.

Push

var filter = Builders<Product>.Filter.Eq("Id", "123");
var update = Builders<Product>.Update.Push("Tags.$[].Categories", "Item 3");
var result = collection.UpdateOne(filter, update);

Pull

var filter = Builders<Product>.Filter.Eq("Id", "123");
var update = Builders<Product>.Update.Pull("Tags.$[].Categories", "Item 3");
var result = collection.UpdateOne(filter, update);

Additional Information:

You can set the ArrayFilters options in UpdateOptions for applying query criteria on nested array to control what elements to update.

For example to update all the Categories in Tags array where each tag has Name name.

var filter = Builders<Product>.Filter.Eq("Id", "123");
var update = Builders<Product>.Update.Push("Tags.$[t].Categories", "Item 3");
var arrayFilters = new List<ArrayFilterDefinition>{ new ArrayFilterDefinition(new BsonDocument("t.Name", "name")) };
var updateOptions = new UpdateOptions({ArrayFilters = arrayFilters});
var result = collection.UpdateOne(filter, update, updateOptions);
s7vr
  • 73,656
  • 11
  • 106
  • 127
  • I'm getting this error on the `Push` solution: `"A write operation resulted in an error.\r\n cannot use the part (Tags of Tags.$[].Categories) to traverse the element` – Liran Friedman Dec 31 '17 at 08:44
  • It looks like you are not on the latest 3.6 version. Execute `db.version()` in shell to check the version. – s7vr Dec 31 '17 at 16:21
  • I've already checked it, version 3.6.1... I run it in Robo mongo. – Liran Friedman Dec 31 '17 at 16:22
  • Didn't get a chance to test c# code but I was able to run queries in shell for the sample data provided. Not sure where the error is. Try looking at the data you are trying to update and run queries in shell. May be some of your documents are missing Categories or Tags and both are type of arrays. Try `db.collection_name.find({"Id":"123"}, {"$push":{"Tags.$[].Categories":"Item 3"}});` in shell. – s7vr Dec 31 '17 at 16:55
  • I copy pasted your query into my Robo Mongo query editor and it gives this error: `Error: error: { "ok" : 0, "errmsg" : "Unsupported projection option: $push: { Tags.$[].Categories: \"Item 3\" }", "code" : 2, "codeName" : "BadValue" }` – Liran Friedman Jan 01 '18 at 06:46
  • Something is very strange... I even copied the sample query from MongoDB's documentation with their sample collection as well from [Update Nested Arrays](https://docs.mongodb.com/manual/reference/operator/update/positional-all/#position-nested-arrays) and it gives an error `cannot use the part (grades of grades.$[].questions.$[score]) to traverse the element`. But when I run `db.version() ` in Robo Mongo it returns 3.6.1. I must be missing something here... – Liran Friedman Jan 01 '18 at 06:57
  • Did you ever figure out what was the issue here ? – s7vr Feb 20 '18 at 14:51
  • Yes. Turn out it was a mongo db settings issue, you need to run this command `db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )` in order to be able to use hthe new array filters... – Liran Friedman Feb 20 '18 at 14:53
  • I wanted to add that this [answer ](https://stackoverflow.com/a/51622030/1057052) ended up giving me the correct result! – Jose A Jan 25 '19 at 02:37