How to find the length of an array by filtering by a parameter value in MongoDb

Viewed 44

I was building a chat server, where I need to get the number of messages that are marked as seen: false.

below is my schema,

const ChatSchema = mongoose.Schema({
    chatId: { type: String, required: true, unique: true },
    messages: [
        {
            message: { type: String, required: true },
            sendBy: { type: String, required: true },
            sendTo: { type: String, required: true },
            seen: { type: Boolean, default: false },
            date: { type: Date, default: Date.now()}
        },
    ],

})

I have tried the following code but it always returns 1

const unSeenCount=await Chat.find({ chatId: chatId }, 
            { messages : { $elemMatch : { seen : false } } })
        
        console.log(`unseen count is ${unSeenCount.length}`);

I think it only gets the documents with the query { chatId: chatId }

3 Answers

You have to calculate from projection,

  • use findOne method because you want one chat's messages count
  • check just chatId condition in the query
  • add projection
    • $filter to iterate loop of seen array from messages and check is any false
    • $size to count to total elements in above-filtered array
  • add lean() at the end, and it will return javascript object instead of mongoose document
const unSeenCount = await Chat.findOne(
  { chatId: chatId },
  {
    count: {
      $size: {
        $filter: {
          input: "$messages.seen",
          cond: { $eq: ["$$this", false] }
        }
      }
    },
    // add more fields if you need them in the result.
  }
).lean();  

console.log(`unseen count is ${unSeenCount.count}`);

Playground

try using filter instead of find:

const unSeenCount=await Chat.filter({ chatId: chatId }, 
        { messages : { $elemMatch : { seen : false } } })
    
    console.log(`unseen count is ${unSeenCount.length}`);

If you use $elemMatch, it will return you all documents that satisfy that criteria. Therefore if document has an array that satisfy that criteria, the whole document will be returned. The MongoDB will not "count" how much appereances are there in an array. Your query will return you either 0 or 1, depends if there is at least one message with seen : false in an array or not.

You can use unwind operation: MongoDB: Count of matching array elements . It will allow you to work with objects inside array in same way as with documents. It is not very efficient though, so if you have a lot of messages and you do this operation often, it can consume a lot of MongoDB resources. So if you need to do this (or similar operations) with messages frequently, it is better to create separate collection for messages.

Related