skip to Main Content

Firstly, here is my "User" collection simplified down to only the relevant fields:

[
  {
    _id: "10",
    groups: [
      "1"
    ],
    notifications: [
      {
        notificationType: "message-like",
        msgId: "3",
        read: false,
        likers: ["steve", "joe"]
      }
    ]
  },
  {
    _id: "20",
    groups: [
      "1",
      "2"
    ],
    notifications: [
      {
        notificationType: "message-like",
        msgId: "1",
        read: false,
        likers: ["frank", "bob"]
      },
      {
        notificationType: "message-like",
        msgId: "2",
        read: false,
        likers: ["bob"]
      }
    ]
  }
]

I need to:

  1. Find the document with the matching "_id"
  2. Look for the notification with matching "notificationType" and "msgId"
  3. If that object is found, add to the "likers" array, move the notification object to the first position, and set "read" to false if true
  4. If the object is not found, push a new notification object to the array

Basically, I need to do the following in MongoDB:

for (let doc of documents) {
  if (doc._id === "20") {
      let notificationFound = false
      doc.notifications.forEach((notif, i) => {
          if (
              notif.notificationType === "message-like"
              && notif.msgId === "2"
          ) {
              notif.likers.push("joe")
              if (notif.read === true) notif.read = false
              notifications.unshift(notifications.splice(i, 1)[0]) //move notification to 1st position
              notificationFound = true
          }
      })
      if (!notificationFound) doc.notifications.push(newNotificationObject)
  }
} 

What is a clean way to do this in MongoDB/Mongoose?

2

Answers


  1. Chosen as BEST ANSWER

    Figured it out. So far this is working well:

    const newNotificationObject = {
      content: 'New Message Like',
      date: Date.now(),
      notificationType: 'message-like',
      likers: [newLiker],
      msgId: msgId
    }
    
    await User.updateOne(
      { 
          $and: [
              { _id: userId }, 
              { 'notifications.notificationType': 'message-like' },  
              { 'notifications.msgId': msgId }
          ]
      }, 
      {
          $set: {
              'notifications.$[i].read': false,
              'notifications.$[i].date': Date.now()
          },
          $push: { 'notifications.$[i].likers': newLiker }
      },
      {
          arrayFilters: [ { 'i.likers': { $nin: newLiker } } ]
      }
    )
    .then(async (docs1) => {
      if (docs1.matchedCount > 0) res.json(true)
      else {
          await User.updateOne({ _id: userId }, { $push: { notifications: newNotificationObject } })
          .then(docs2 => {
              if (docs2.matchedCount > 0) res.json(true)
              else throw new Error('Could not add notification')
          })
          .catch(err => { throw new Error(err) })
      }
    })
    .catch(err => { throw new Error(err) })


  2. You can use the findOneAndUpdate method with the aggregation pipeline

    const mongoose = require('mongoose');
    const { Schema } = mongoose;
    
    const notificationSchema = new Schema({
      notificationType: String,
      msgId: String,
      read: Boolean,
      likers: [String]
    });
    
    const userSchema = new Schema({
      _id: String,
      groups: [String],
      notifications: [notificationSchema]
    });
    
    const User = mongoose.model('User', userSchema);
    
    const targetId = "20";
    const targetType = "message-like";
    const targetMsgId = "2";
    const newLiker = "joe";
    const newNotificationObject = {
      notificationType: targetType,
      msgId: targetMsgId,
      read: false,
      likers: [newLiker]
    };
    
    User.findOneAndUpdate(
      { _id: targetId },
      [
        {
          $set: {
            notifications: {
              $let: {
                vars: {
                  idx: {
                    $indexOfArray: [
                      "$notifications",
                      { $elemMatch: { notificationType: targetType, msgId: targetMsgId } }
                    ]
                  },
                  notFound: -1
                },
                in: {
                  $cond: [
                    { $eq: ["$$idx", "$$notFound"] },
                    { $concatArrays: ["$notifications", [newNotificationObject]] },
                    {
                      $concatArrays: [
                        [
                          {
                            $mergeObjects: [
                              { $arrayElemAt: ["$notifications", "$$idx"] },
                              {
                                likers: { $concatArrays: [["$notifications.$$idx.likers", newLiker]] },
                                read: false
                              }
                            ]
                          }
                        ],
                        { $slice: ["$notifications", 0, "$$idx"] },
                        { $slice: ["$notifications", { $add: ["$$idx", 1] }, { $size: "$notifications" }] }
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      ],
      { new: true }
    )
      .then(updatedDoc => {
        console.log('Updated document:', updatedDoc);
      })
      .catch(err => {
        console.error('Error updating document:', err);
      });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search