Update mongo document as a whole or specific fields

Viewed 1293

Scenario

I have a conversation document in mongodb , I have to add messages to a messages array and update the last sent date , see schema below.

{ 
"_id" : NumberInt(5), 
"CurrentOwnerId" : NumberInt(9), 
"LastSent" : ISODate("2019-06-21T11:57:32.861+0000"), 
"Messages" : [
    {
        "_id" : BinData(3, "nuC1iYTKtkGzSuv7pVHsKg=="), 
        "MessageContent" : "Hi There", 
        "Status" : "Pending", 
        "DateSent" : ISODate("2019-06-21T11:57:32.861+0000"), 
        "Method" : "Slack"
    }
 ]
}

My question

Would it be more efficient to simply read out the whole document (using the BsonId) and update the document via c# as a whole ie pushing my message to the array and setting the last sent day and then updating the document as a whole using the driver OR do two calls to the database using $set and $push operators to achieve what I want to do.

3 Answers

As Eduardo Hitek said, you can set multiple properties in a single query. So you can update the entity without actually having to retrieve it from the database first like this:

    //Build an Id Filter
    var idFilter = Builders<Chat>.Filter.Eq(x => x.Id, "1");


    var message = new Message() { MessageContent = "Hey!", Method = "Slack" };

    //Build an update definition to add the message
    var addMessage = Builders<Chat>.Update.AddToSet(x => x.Messages, message);
    //Build an update definition to set the LastSent property
    var setLastSent = Builders<Chat>.Update.Set(x => x.LastSent, DateTime.Now);
    //Combine both update definitions
    var combinedUpdate = Builders<Chat>.Update.Combine(addMessage, setLastSent);

    //Execute the query.
    db.GetCollection<Chat>("ChatCollection").UpdateOne(idFilter, combinedUpdate);

The added benefit from updating an entity like this is that it's done atomically.

i think you don't need to make 2 calls, because MongoDB update command can accept more than one update instruction. Example:

db.collection.update({_id: docId}, {$push: {values: dboVital}, $set: {endTime: time}});

So, you could add the message to your array, and update the lastSentDay with just one call.

like the other answers say, you can do it in a single mongodb command with the help of either $push or $addToSet. here's how to do it in a convenient and strongly typed manner using my library MongoDB.Entities

using MongoDB.Entities;
using System;

namespace StackOverflow
{
    public class Program
    {
        public class Conversation : Entity
        {
            public DateTime LastSent { get; set; }
            public Message[] Messages { get; set; }
        }

        public class Message
        {
            public string Content { get; set; }
        }

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

            var convo = new Conversation
            {
                LastSent = DateTime.Now.AddMinutes(-10),
                Messages = new[] { new Message { Content = "This is the first message..." } }
            };
            convo.Save();

            var msg = new Message { Content = "This is a new message..." };

            DB.Update<Conversation>()
              .Match(c => c.ID == convo.ID)
              .Modify(c => c.LastSent, DateTime.Now)
              .Modify(b => b.Push(c => c.Messages, msg))
              .Execute();
        }
    }
}

the following update command is sent to the database:

db.Conversation.update(
    {
        "_id": ObjectId("5d0ce23647e2d210903b3930")
    },
    {
        "$set": {
            "LastSent": ISODate("2019-06-21T13:57:10.998Z")
        },
        "$push": {
            "Messages": {
                "Content": "This is a new message..."
            }
        }
    },
    {
        "multi": true,
        "upsert": false
    }
)
Related