4

In my Azure CosmosDb MongoApi I have JSON with an embed array of documents.

{
    "_id": ObjectId("5a95745df886842904b82f71"),
    "token": "value1",
    "channel": "value2",
    "urls":
    [
        {
            "url": "<url1>", 
            "interval": "<int>"
        },
        {
            "url": "<url2>"
            "interval": "<int>"
        }
    ]
}

I want to update "interval" field in a specific array element. Problem is when I use solutions like this or this, I end up with an exception:

MongoDB.Driver.MongoCommandException: Command findAndModify failed: Invalid BSON field name 'urls.$.interval'.

So, I decided to try running queries in Mongo Shell and got the same error:

{
    "nMatched": 0,
    "nUpserted": 0,
    "nModified": 0,
    "writeError": 
    {
        "code": 2,
        "errmsg": "Invalid BSON field name 'urls.$.interval'"
    }
}

Here is my C# code:

    var filterBuilder = Builders<User>.Filter;
    var filter = filterBuilder.Where(p => p.Token == model.Token && p.Channel == model.Channel && p.Urls.Any( u => u.Url == model.Url));
    var update = Builders<User>.Update.Set(p => p.Urls.ElementAt(-1).interval, 5);
    await _context.Users.FindOneAndUpdateAsync<User>(filter, update);

Here is my MongoDb shell request:

db.Users.update( {"urls.interval": 60}, {$set: {"urls.$.interval": 30}} ); 

My question is what is causing this exception and how can I avoid it?

1 Answers1

1

Positional operator is not currently supported by Cosmos DB. Please use the following workaround: iterate over documents and array elements on the client side, change the required element and issue an update on the document with new array:   For example, assuming you have a collection users of following elements:

{ 
    "_id" : ObjectId("59d6b719708b990d6c9b3aca"), 
    "tags" : [
        {
            "id" : 1, 
            "value" : "11"
        }, 
        {
            "id" : 2, 
            "value" : "22"
        }, 
        {
            "id" : 3, 
            "value" : "32"
        }
    ]
}
 

…you can issue the following command to get one of the elements (with id=1 in this case) updated:  

db.users.find().forEach( 
        function(user) 
        { 
         user.tags.forEach( 
         function(tag) 
         {
               if (tag.id=="1")
               {
                       tag.value = "Updated!";                          
                       db.users.updateOne({_id : user._id}, {$set: {"tags": user.tags}});
               }
        }
        );
       }  
);

You can adjust the condition in if() with even finer granularity than positional operator allows.

alekseys
  • 321
  • 1
  • 6