DoneDeal0
DoneDeal0

Reputation: 6257

Mongoose: how to automatically remove old entries form an array?

I have a messaging app, that can display messages threads. In order not to bother with pagination, endless api calls and performance issues, I would like to automatically delete messages older than x days in an array in order to keep a small a amount of data.

How to do it? If it is not possible, how to automatically remove the oldest element from an array if it already has 50 elements while trying to push a new message? I could do it with native javascript, but maybe there is a cleaner way?

const mongoose = require("mongoose");

const conversationSchema = new mongoose.Schema(
  {
    name: { type: String, required: true, unique: true },
    messages: [{ message: { type: String }, authorId: { type: String } }],
    lastMessage: {
      authorId: { type: String },
      snippet: { type: String },
      read: { type: Boolean },
    },
  },
  { timestamps: true }
);

conversationSchema.index({ name: 1 });

module.exports = mongoose.model("Conversation", conversationSchema);

Upvotes: 1

Views: 704

Answers (1)

klhr
klhr

Reputation: 3390

The $slice operator allows $pushing to an array and then limiting that array's length:

db.messageChannels.update(
   { _id: myMessageChannelId },
   {
     $push: {
       messages: {
         $each: [ "Hello world!" ],
         $slice: -50
       }
     }
   }
)


Similarly, on an update, you can use $pull to remove any messages that match a criteria (in this case, the criteria would be being older than X days). This won't automatically expire these messages if nobody is using the channel (which you could automatically handle by deleting the whole channel if you track a lastMessageDate on your whole channel using a TTL index).

db.messageChannels.updateOne(
    { _id: myMessageChannelId },
    { $pull: { messages: { sentAt: {$lte: expirationDate} }, $push: {messages: "Hello world!"} },
)

To expire all, you can run a query like the below on a cron:

db.messageChannels.update(
    { },
    { $pull: { messages: { sentAt: {$lte: expirationDate} } },
    { multi: true }
)

TTL indexes apply to the whole document so aren't workable, unless you group your messages by day or something similar.

Upvotes: 1

Related Questions