I have chat, admin,user and message entities. chat has reference of user and admin, message has reference on chat. Message has boolean property called viewed. So I am trying to get chats for admin with user data populated and the count of unviewed messages.
Chat.aggregate([
{
$match: {
admin: new Types.ObjectId(admin.id),
},
},
{
$lookup: {
from: "admins",
localField: "admin",
foreignField: "_id",
as: "admin",
},
},
{ $unwind: "$admin" },
{
$lookup: {
from: "users",
localField: "user",
foreignField: "_id",
as: "user",
},
},
{ $unwind: "$user" },
{
$lookup: {
from: "messages",
localField: "_id",
foreignField: "chat",
as: "messages",
},
},
//following match statement doesn't work
// {
// $match: {
// "messages.viewed": false,
// //"messages.reciever":new Types.ObjectId(admin.id),
// },
// },
{
$project: {
id: "$_id",
"admin.id": "$admin._id",
"admin.email": "$admin.email",
"admin.vendorId": "$admin.vendorId",
"user.fullName": 1,
"user.id": "$user._id",
"user.phoneNumber": 1,
"unreadMsgs": {
$size: {
$filter: {
input: "$messages",
as: "message",
cond: { $eq: ["$$message.viewed", false] },
},
},
},
},
},
]).exec();
the commented part of the code (2nd match section seems not working). while there are unwieved messages code was working correctly, when there is no unviewed messages it returns empty array so then commented second mathc statement then everything started working as expected. The question is how can i get count of unread msgs within match not performing additional filtering operations?
Here are Chat Schema
const chatSchema = new Schema(
{
user: { type: Schema.Types.ObjectId, ref: User },
admin: { type: Schema.Types.ObjectId, ref: Admin },
product: { type: Schema.Types.ObjectId, ref: Product },
},
{
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
}
);
Here is Message Schema
const messageSchema = new Schema(
{
sender: { type: Schema.Types.ObjectId, required: true, ref: User },
reciever: { type: Schema.Types.ObjectId, required: true, ref: User },
chat: { type: Schema.Types.ObjectId, required: true, ref: Chat },
message: String,
file: String,
viewed:{type:Boolean, default:false}
},
{
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
timestamps: true,
}
);
User and Admin Schemas
const adminSchema = new Schema(
{
email: String,
password: String,
vendorId: { type: Schema.Types.ObjectId, ref: "Vendor" },
online:{type:Boolean, default:false},
super: { type: Boolean, default: false },
},
{
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
}
);
const userSchema = new Schema(
{
fullName: String,
password: String,
phoneNumber: Number,
avatar: String,
gender: String,
birthdate: Date,
online:{type:Boolean, default:false},
basket:[{type:Schema.Types.ObjectId, ref:Product}]
},
{
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
}
);
2
Answers
The $match stage matches documents, it does not filter the contents of an array.
In the preceding $lookup all of the messages are fetched from the messages collection and place in an array in the ‘messages’ field.
The $match will then select each document that contains at least one unviewed message. If there are no unviewed messages, the entire document is eliminated from the pipeline.
Since you already filter the array in the $project stage, that $match stage is completely unnecessary.
If you want to get the count of unread msgs you have to add following stage in your pipeline.
The above modified pipeline, following the "$project" stage, an "$addFields" stage has been introduced. This new stage assesses whether the messages array is devoid of content (indicating no messages). If so, it assigns a value of 0 to unreadMsgs. However, if there are messages present, it retains the existing count of unreadMsgs.
This refinement ensures that even in scenarios where there are no unviewed messages, the pipeline will furnish the intended outcome without yielding an empty array.