Reputation: 505
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
property (list) that contains the members in the group chat. Each member has the UserId
and LastReadDate
properties. The LastReadDate
is updated when the user writes 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 unread messages (The messages are 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 receive the following 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?
Upvotes: 1
Views: 256
Reputation: 51160
Based on the provided data in the comment, I think the aggregation query is required to achieve the outcome.
$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
.
$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: []
}
}
}
])
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;
Upvotes: 4
Reputation: 689
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);
Upvotes: 0