Home > Enterprise >  $lookup not working in nested field array
$lookup not working in nested field array

Time:11-05

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.

  • Related