Home > database >  Count unread messages in MongoDb
Count unread messages in MongoDb

Time:07-14

I try to count unread messages for a user. On my model i have a property, LastMessageDate that contains the date on the last created message in the group chat. I have also a Members propeerty (list) that contains the members in the group chat. Each member has a UserId and LastReadDate property. The LastReadDate is updated when the user write a new message in the group chat or when the user loads messages from the group chat.

Now i want to count the number of chats where a specific user has unreaded messages (The messages is stored in another collection). I try this:

var db = GetGroupCollection();

var filter = Builders<ChatGroup>.Filter.Where(p => p.Members.Any(m => m.UserId == userId && m.LastReadDate < p.LastMessageDate));
return await db.CountDocumentsAsync(filter);

But i recive error: The LINQ expression: {document}{Members}.Where((({document}{UserId} == 730ddbc7-5d03-4060-b9ef-2913d0b1d7db) AndAlso ({document}{LastReadDate} < {document}{LastMessageDate}))) has the member "p" which can not be used to build a correct MongoDB query.

What should i do? Is there a better solution?

CodePudding user response:

When you want to query a nested list of a document, ElemMatch is your solution, Try

var filter = builder.ElemMatch(o => o.Members,m => m.UserId == userId && m.LastReadDate < p.LastMessageDate);

CodePudding user response:

Based on the provided data in the comment, I think the aggregation query is required to achieve the outcome.

  1. $set - Set Members field

    1.1. $filter - With Members array as input, filter the document(s) with matching the current document's UserId and LastMessageDate is greater than ($gt) the current document's LastReadDate.

  2. $match - Filter the document with Members is not an empty array.

db.groups.aggregate([
  {
    "$set": {
      Members: {
        $filter: {
          input: "$Members",
          cond: {
            $and: [
              {
                $eq: [
                  "$$this.UserId",
                  1
                ]
              },
              {
                $gt: [
                  "$LastMessageDate",
                  "$$this.LastReadDate"
                ]
              }
            ]
          }
        }
      }
    }
  },
  {
    $match: {
      Members: {
        $ne: []
      }
    }
  }
])

Sample Mongo Playground


For C# syntax, either you can directly provide the query as a string or convert the query to BsonDocument syntax.

Note that the query above will return the array of documents, hence you will need to use System.Linq to count the returned document(s).

using System.Linq;

var pipeline = new BsonDocument[]
{
    new BsonDocument("$set", 
        new BsonDocument("Members", 
            new BsonDocument("$filter", 
                new BsonDocument
                { 
                    { "input", "$Members" },
                    { "cond", new BsonDocument
                        (
                            "$and", new BsonArray
                            {
                                new BsonDocument("$eq", 
                                    new BsonArray { "$$this.UserId", userId }),
                                new BsonDocument("$gt",
                                    new BsonArray { "$LastMessageDate", "$$this.LastReadDate" })
                            }
                        )
                    }
                }
            )
        )
    ),
    new BsonDocument("$match",
        new BsonDocument("Members",
            new BsonDocument("$ne", new BsonArray())))

};

var db = GetGroupCollection();

return (await db.AggregateAsync<BsonDocument>(pipeline))
    .ToList()
    .Count;
  • Related