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.
$set
- SetMembers
field1.1.
$filter
- WithMembers
array asinput
, filter the document(s) with matching the current document'sUserId
andLastMessageDate
is greater than ($gt
) the current document'sLastReadDate
.$match
- Filter the document withMembers
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: []
}
}
}
])
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;