Update multiple array elements (C# driver)?

1,947 views
Skip to first unread message

Brian McGee

unread,
Apr 6, 2016, 6:56:27 PM4/6/16
to mongodb-user
I have a document like this:

{
    "_id" : "DummyId",
    "in_use" : NumberInt(1),
    "host" : "3cec176f-e14e-4fa9-946c-3fc395f8af98",
    "locktime" : ISODate("2016-04-06T14:43:00.842+0000"),
    "owner" : "DummyOwner",
    "readerList" : [
        {
            "_id" : "3cec176f-e14e-4fa9-946c-3fc395f8af98",
            "ts" : ISODate("2016-04-06T14:43:00.843+0000")
        },
        {
                    "_id" : "b959ee51-bfa5-4fe2-a9bf-9070e2c8c742",
                    "ts" : ISODate("2016-04-06T13:36:58.493+0000")
              }
    ],
}


I want to remove from readerList any object which has a specific _id value or which has a ts older than a minute.  I'm trying the following code:

                List<UpdateDefinition<BsonDocument>> updateList = new List<UpdateDefinition<BsonDocument>>();
                updateList.Add(Builders<BsonDocument>.Update.PullFilter("readerList", Builders<BsonDocument>.Filter.Eq("_id", _uniqueId)));
                updateList.Add(Builders<BsonDocument>.Update.PullFilter("readerList", Builders<BsonDocument>.Filter.Lt("ts", DateTime.UtcNow.AddMinutes(-1.0))));

                update = Builders<BsonDocument>.Update.Combine (updateList);

                UpdateResult res = await _syncLockCollection.UpdateOneAsync(filter, update);

What happens is that whichever PullFilter is last gets executed.  Is there a way to accomplish this using the C# driver?  It's crucial to my design that I be able to accomplish this as a single operation, so I can't fall back on reading the document, making my changes, and writing it again.

Any help would be appreciated!

Brian

Craig Wilson

unread,
Apr 6, 2016, 7:18:13 PM4/6/16
to mongodb-user
I think you need to put the OR clause in the filter, not have 2 separate pull filters.

var filter = Builders<BsonDocument>.Filter.Eq("_id", _uniqueId) | Builders<BsonDocument>.Filter.Lt("ts", DateTime.UtcNow.AddMinutes(-1.0));
var update = Builders<BsonDocument>.Update.PullFilter("readerList", filter);

Brian McGee

unread,
Apr 7, 2016, 9:27:53 AM4/7/16
to mongodb-user
Craig,

If I understand Mongo correctly (and I'm not sure I do), by adding that to the filter aren't I saying "Give me any document that has an _id that matches _uniqueID or has a ts more than a minute old?"

I don't want to affect multiple documents -- rather I want to affect multiple rows inside the readerList array in a SINGLE document.

I realize now by having two fields called _id it may not have been clear what I wanted.  In my example code I'm referring to readerList.*._id

Craig Wilson

unread,
Apr 7, 2016, 11:30:14 AM4/7/16
to mongodb-user
I think this is what you want. You are saying, in a given document, Pull an item out of the array IF it's "_id" equals the uniqueId or it's "ts" is less than a minute ago. If you only want to affect a single document, then you need to apply a filter to the update itself. This filter is for the readerList array.

I'd suggest you try it and see what happens.

Brian McGee

unread,
Apr 7, 2016, 1:26:59 PM4/7/16
to mongodb-user
Indeed, I misread what you'd written.

It is exactly what I want.  Thank you very much!

B
Reply all
Reply to author
Forward
0 new messages