mongodbmeteormongodb-queryminimongo

How to update property in multiple objects in an array for one document in Meteor collection/minimongo?


My question is almost a duplicate of this question. The difference is that I am using minimongo within the Meteor framework/platform. Given this document:

{
"_id" : ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id" : "714638ba-2e08-2168-2b99-00002f3d43c0",
"events" : [
{
    "handled" : {
        "name": "Mike",
        "visible": false
    },
    "profile" : 10,
    "data" : "....."
}
{
    "handled" : {
        "name": "Shaun",
        "visible": false
    },
    "profile" : 10,
    "data" : "....."
}
{
    "handled" : {
        "name": "Glen",
        "visible": true
    },
    "profile" : 20,
    "data" : "....."
}
]}

How do I query for a particular user and update all the objects in the events array ONLY where 'handled.visible':false to 'handled.visible':true? As much as possible, I would like to have it in a single query. My objective is really to improve the performance of my app. Instead of having to fetch the entire array of objects, process them on the client side (change the object properties) then re-update on the server, it would be great to directly update via Mongo. Changing the data directly on the server is also natively reactive and is advantageous, though is not really necessary for my app.

I am not exactly sure as how to formulate the query in minimongo.

I have tried:

Meteor.users.update({_id: 's8Ppj4ZFSeq9X6xC4', 'events.handled.visible': false }, { $set:{'events.$.handled.visible':true} });

This worked for only the first object found in the array. However I would want to update all objects in the array where the handled.visible is false.


Solution

  • You could create a server method that uses the aggregation framework to return a count that you can use in your client as a loop to update each matching array element. The following untested example may have some pointers on how you can go about it (I haven't tested it so feedback on your implementation is much welcome to make this a better solution):

    Server:

    Add the meteorhacks:aggregate package to your app with

    meteor add meteorhacks:aggregate
    

    and then use as

    Meteor.methods({
        getInvisibleEventsCount: function(userId){
            var pipeline = [
                { "$match": { "_id": userId, "events.handled.visible": false } },
                { "$unwind": "$events.handled" },
                { "$match": { "events.handled.visible": false } },
                { "$group": {
                    "_id": null,
                    "count": { "$sum": 1 }
                }}
            ];
            var result = Meteor.users.aggregate(pipeline);
            return result;
        }
    });
    

    Client:

    Meteor.call("getInvisibleEventsCount", userId, function(err, response) {
        var max = response[0].count;
        while(max--) {
            Meteor.users.update(
                { "_id": userId, "events.handled.visible": false }, 
                { "$set": { "events.$.handled.visible": true} }
            );      
        }
    });