Lion789
Lion789

Reputation: 4482

Mongoose query to keep only 50 documents

How do I make a query in mongoose to find if a user has 50 documents then remove the oldest one and add in the new one, if not just add in the new one?

This is my attempt:

    Notifications.findOne({_id: userId}, function(err, results) {
       if(err) throw err;
       if(results.length < 50) {
         saveNotification();
       } else {
          Notifications.findByIdAndUpdate(userId, {pull: //WHAT GOES HERE), 
            function(err, newNotify) {
              if(error) throw error;
               saveNotification();
            });

       }
    });

function saveNotification() { 

    var new_notification = new Notification ({
        notifyToUserId: creatorId,
        notifyFromUserId: null,
        notifyMsg: newmsg,
        dateNotified: dateLastPosted                
    });

    new_notification.save(function(err, results){
        if(!err) {
           console.log('notification has been added to the db');
           cb(null, resultObject);
        } else {
           console.log("Error creating notification " + err);
        }
    });

}

Upvotes: 2

Views: 1096

Answers (4)

Lion789
Lion789

Reputation: 4482

I ended up using cubbuk's answer and expanding it to add a notification if there is no array to start with along with upsert... 

Notification.findOneAndUpdate({notifyToUserId: to}, {
            $push: { 
                notifyArray: {$each: [newNotificationObject], // insert your new notification
                   $sort: { dateNotified: 1 }, // sort by insertion date
                   $slice: -50 // retrieve the last 50 notifications.
                }
            }
        }, {upsert: true}, function(err, resultOfFound) {
            if(err) throw err;
            if(resultOfFound == null) {
                var new_notification = new Notification ({
                    notifyToUserId: to,
                    notifyArray: [newNotificationObject]
                });
                new_notification.save(function(err, results){
                    if(!err) {

                        console.log('notification has been added to the db');
                        cb(null, resultObject);
                    } else {
                        console.log("Error creating notification " + err);


                    }
                });

            } else {
                cb(null, resultObject);
            }
        });

Upvotes: 0

maniacneron
maniacneron

Reputation: 424

Capped collections do what you want by nature. If you define a capped collection with size 50 it will only keep 50 documents and will overwrite old data when you insert more. check

new Schema({..}, { capped: { size: 50, max: 50, autoIndexId: true } });

Remember that when working with capped collection you can only make inplace updates. Updating whole document may change the size of collection that will remove other documents.

Upvotes: 0

cubbuk
cubbuk

Reputation: 7920

As @Pio mentioned I don't think you can do it in one query with your current schema. But if you have chance to change the schema, you can use fixed size array pattern that is described in the following article Limit Number of Elements in an Array after an Update

Basically you can keep the notifications of users in one document. Key of the document will be userId, and notifications will be stored in an array. Then the following query would achieve your goal.

Notifications.update(
   { _id: userId },
   {
     $push: {
        notifications: {
           $each: [ notificationObject ], // insert your new notification
           $sort: { dateNotified: 1 }, // sort by insertion date
           $slice: -50 // retrieve the last 50 notifications.
        }
     }
   }
)

Upvotes: 2

Pio
Pio

Reputation: 4062

I am not sure you can do it in one query, but you can .count({user: yourUser'}) then depending on the count .insert(newDocument) or update the oldest one so you won't remove + insert.

Upvotes: 0

Related Questions