0

I have a doc with an array of embedded docs ("comments"), and an example that looks like this:

{
    "_id" : ObjectId("539e9213209e743d107e7202"),
    "article" : "article1",
    "comments" : [
        {
            "comment" : "comment1",
            "created" : ISODate("2014-06-16T06:43:38Z"),
            "_id" : ObjectId("539e921a209e743d107e7203"),
            "read" : {
                "marked" : false
            },
            "timesent" : {
                "datetime" : "Mon Jun 16 2014 02:43:38 GMT-0400 (EDT)",
                "hour" : 2,
                "minute" : "43",
                "second" : 38,
                "am" : true,
                "month" : 5,
                "day" : 16,
                "year" : 2014
            }
        }
    ]
}

For each comment in the comments array, is there a way to batch update the field "read" : {"marked" : true}?

Am working with node.js, and have something like this in mind (the questionable portion begins with

if (req.body.readComment) {..

// update the article with this id (accessed by PUT at
// http://localhost:4200/api/v1/articles/:article_id)
.put(function(req, res) {

    Article.findById(req.params.article_id, function(err, article) {

        if (err)
            res.send(err);

        if (req.body.comment) {

            article.comments.push({

                comment : req.body.comment,
                timesent :
                {
                    datetime : req.body.datetimeNow,
                    hour : req.body.hourNow,
                    minute : req.body.minuteNow,
                    second : req.body.secondNow,
                    am : req.body.amNow,
                    month : req.body.monthNow,
                    day : req.body.dayNow,
                    year : req.body.yearNow 
                },
                read :
                {
                    marked : req.body.readComment,
                    datetime : req.body.readCommentDatetime
                },
                created : req.body.datetimeNow

            });

        } // if newComment


    if (req.body.readComment) {

      var comments = // some sort of .find ?
      var embeddedDoc;
      for (var i=0, length=comments.length; i < length; i++){
        embeddedDoc = comments[i];
        embeddedDoc_id = // something to find the embedded doc_id ?
        console.log(i);

                article.comments.push({ // maybe push to the embedded doc_id

                    read :
                    {
                        marked : req.body.readComment,
                        datetime : req.body.readCommentDatetime
                    }

                });

      };

    } // if readComment == true (from ajax .put)


        // save the article, and check for errors
        article.save(function(err) {

            if (err)
                res.send(err);

        res.json({ message: 'Update "' + req.params.article_id });

        });

    });

})
2
  • Was there something in answer that did not meet the needs of your question? Commented Jun 30, 2014 at 7:42
  • @NeilLunn not necessarily.. the database has changed a bit (thanks to your awesome answers), and have yet get around to grabbing that comments._id, and therefore have yet to test this.. The answer seems ideal, as usual, so is it proper to accept, or should I test it first? Just upvoted! Commented Jun 30, 2014 at 17:32

1 Answer 1

2

Well as each comment would need to be identified within the array, the term "Bulk" doesn't really apply as they would be inherently separate. As for being able to just say "Update all of these 'comments' and mark them as true", that is not directly supported.

But on the other hand you can streamline this with Bulk update operations. So for a "list" of "comment" _id values you could do this:

var bulk = collection.initializeOrderedBulkOp();

comments.forEach(function(commentId) {
    bulk.find({ "comments._id": commentId }).updateOne({ 
        "$set": { "comments.$.read.marked": false }
    });

    counter++;
    if ( counter % 500 == 0 ) {
        bulk.execute(function(err,result) {
           // do something with the result
           bulk = collection.initializeOrderedBulkOp();
           counter = 0;
        });
    }
});

// Catch any under or over the 500's
if ( counter > 0 ) 
    bulk.execute(function(err,result) {
       // do something with the result here
    });

That at least avoids you sending an update "over the wire" to the server instance for every single "comment" _id you send into the API. By batching the results this results in less traffic and less time waiting for the response in the callback.

You can likely better this example with "async" so even looping the input list is a non-blocking operation. The batch sizes can vary, but this is just a safe example to stay under the 16MB BSON hard limit, as the whole "request" is equal to one BSON document.

Sign up to request clarification or add additional context in comments.

2 Comments

Very cool. Thanks for this. Any recommendations for getting the commentId of each comment in the article doc? Or should I just grab the commentId of each comment from a hidden field in the frontend, to be sent over ajax?
@StackThis Depends on how you are populating the data in your client really. If you are just rendering HTML then a hidden field seems reasonable. If you render via data sourced from a JSON response then there should be a way to get the associated value. Really is another question actually that depends on your implementation.

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.