I have notifications schema like this
const mongoose = require("mongoose");
var NotificationsSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User", //this may be null if user is not in system
},
userEmail: {
type: String,
//required: [true, "User Email cannot be empty"],
},
notifications: [
{
_id: mongoose.Schema.Types.ObjectId,
notificationTemplate: {
type: mongoose.Schema.Types.ObjectId,
ref: "NotificationTemplate",
required: [true, "Notification Template cannot be empty"],
},
url: {
type: String,
//required: [true, "Message cannot be empty"],
},
attachment: {
type: [String],
},
by: {
type: mongoose.Schema.Types.ObjectId,
ref: "User", //if some other user send me notification
},
protocols: {
type: [
{
type: String,
enum: ["email", "webapp", "smstext"],
},
],
validate: {
validator: function (protocols) {
return protocols.length > 0;
},
message: "At least one protocol is required",
},
required: [true, "Protocols for Notification cannot be empty"],
},
isRead: {
type: Boolean,
default: false,
},
readOn: {
type: Date,
},
sentWeb: {
type: Date,
},
sentEmail: {
type: Date,
},
sentSmsText: {
type: Date,
},
isDeleted: {
type: Boolean,
default: false,
},
//Make sure to save all subject and message variable in notificationParameters
notificationParameters: Object, //here saved any other info
createdOn: {
type: Date,
default: Date.now(),
},
},
],
createdDate: {
type: Date,
default: Date.now(),
},
});
let Notifications;
if (!mongoose.models["Notifications"]) {
Notifications = mongoose.model("Notifications", NotificationsSchema);
} else {
Notifications = mongoose.models["Notifications"];
}
module.exports = Notifications;
I am trying to find user notifications with pagination and populate notificationTemplate but somehow it never populate notificationTemplate field. Each user can have multiple documents in Notifications.
here is my try
const doc = await Notifications.aggregate([
{ $match: { user: mongoose.Types.ObjectId(req.params.userId) } },
//{ $unwind: "$notifications" },
{ $match: { "notifications.protocols": { $in: ["webapp"] } } },
{
$lookup: {
localField: "notificationTemplate",
//localField: "notifications.notificationTemplate", //not working
foreignField: "_id",
from: "NotificationTemplate", // Replace with the actual collection name
as: "notificationTemplate",
// as: "notificationTemplateInfo",//not working
},
},
{ $sort: { "notifications.createdOn": -1 } }, // Sort by createdOn field in descending order
{ $skip: (page - 1) * parseInt(itemsPerPage) },
{ $limit: parseInt(itemsPerPage) },
{ $project: { notifications: 1 } },
//{ $unwind: "$notifications" },
]);
Here is how i recieve data, note that notificationTemplate is always id
[
{
attachment: [],
protocols: [ 'email', 'webapp' ],
isRead: false,
isDeleted: false,
createdOn: 2023-11-03T16:46:59.372Z,
notificationTemplate: 65451f5277306d1d2c418cd5,
url: '/session/654524e33e894e35ac4c00b9',
by: 63a66a814e32a254204776e8,
sentWeb: 2023-11-03T16:50:43.769Z,
notificationParameters: { subject: 'Networking iDea' },
sentEmail: 2023-11-03T16:50:45.766Z,
_id: 654524e59c36fe07f08542c5
}
]
Here is sample data in mongodb. Note, Each user will have multiple documents
{
"_id":{"$oid":"65461f21db7f106a02931cc2"},
"createdDate":{"$date":{"$numberLong":"1699030019372"}},
"user":{"$oid":"63a24f8a9d7babc0c0e51743"},
"userEmail":"[email protected]",
"notifications":[
{
"attachment":[],
"protocols":["email","webapp"],
"isRead":false,
"isDeleted":false,
"createdOn":{"$date":{"$numberLong":"1699030019372"}},
"notificationTemplate":{"$oid":"65451f5277306d1d2c418cd5"},
"url":"/session/654524e33e894e35ac4c00b9",
"by":{"$oid":"63a66a814e32a254204776e8"},
"sentWeb":{"$date":{"$numberLong":"1699030243769"}},
"notificationParameters":{"subject":"Networking iDea"},
"sentEmail":{"$date":{"$numberLong":"1699030245766"}},
"_id":{"$oid":"654524e59c36fe07f08542c5"}
}
]
}
when i try this it is populating document fine
const c = await Notifications.find({ user: req.params.userId }).populate(
"notifications.notificationTemplate"
);
CodePudding user response:
Your notificationTemplate
is a property of the objects stored in the notifications
array so you need access them in that way. Without seeing your schema this is a little like shooting in the dark but based on what you've shared your lookup should look like this:
{
$lookup: {
localField: "notifications.notificationTemplate",
foreignField: "_id",
from: "NotificationTemplate", // Replace with the actual collection name
as: "notificationTemplate",
},
},
However, $lookup
returns an array so that changes the shape of your documents. You will now have an array named notificationTemplate
on the same level as the notifications
array, not nested inside each notifications
object.
CodePudding user response:
In $lookup, localField must be different from as field.
Otherwise, it won't work correctly.
For example, notificationTemplete field is 65451f5277306d1d2c418cd5 but there is no data with _id like that in NotificationTemplete collection.
result's notificationTemplete field won't be changed to Object and remaining in 65451f5277306d1d2c418cd5.
In your query, 'localField' is 'notificationTemplate' and 'as' is also 'notificationTemplate'.
Change one and it'll works correctly.
For example
$lookup: {
localField: "notificationTemplate",
foreignField: "_id",
from: "NotificationTemplate",
as: "notificationTemplateInfo",
},
Then result will be
[
{
attachment: [],
protocols: [ 'email', 'webapp' ],
isRead: false,
isDeleted: false,
createdOn: 2023-11-03T16:46:59.372Z,
notificationTemplateInfo: 65451f5277306d1d2c418cd5,
url: '/session/654524e33e894e35ac4c00b9',
by: 63a66a814e32a254204776e8,
sentWeb: 2023-11-03T16:50:43.769Z,
notificationParameters: { subject: 'Networking iDea' },
sentEmail: 2023-11-03T16:50:45.766Z,
_id: 654524e59c36fe07f08542c5
}
]
I hope this answer can help you.
Good luck.