skip to Main Content

Hello guys I’m writing a Message Application with Node.js and Mongoose. I keep datas in mongodb like that:

enter image description here

I want to list users who messaged before so I need to filter my ‘Messages’ collection but I can’t do what exactly I want. If he sent a message to a person I need to take persons name but, if he take a message from a person I need to take persons name however in first situation person name in reciever, in second situation person name in sender. I made a table for explain more easily. I have left table and I need 3 name like second table.(Need to eliminate one John’s name)

enter image description here

Sorry, if this problem asked before but I don’t know how can I search this problem.

I tried this but it take user name who logged in and duplicate some names.

Message.find({$or: [{sender: req.user.username}, {reciever: req.user.username}]})

2

Answers


  1. This is a tricky one, but can be solved with a fairly simple aggregation pipeline.

    Explanation

    On our first stage of the pipeline, we will want to get all the messages sent or received by the user (in our case David), for that we will use a $match stage:

    {
      $match: {
        $or: [
          {sender: 'David'},
          {receiver: 'David'}
        ]
      }
    }
    

    After we found all the messages from or to David, we can start collecting the people he talks to, for that we will use a $group stage and use 2 operations that will help us to achieve this:

    • $addToSet – This will add all the names to a set. Sets only contain one instance of the same value and ignore any other instance trying to be added to the set of the same value.
    • $cond – This will be used to add either the receiver or the sender, depending which one of them is David.

    The stage will look like this:

    {
      $group: {
        _id: null,
        chats: {$addToSet: {$cond: {
          if: {$eq: ['$sender', 'David']},
          then: '$receiver',
          else: '$sender'
        }}}
      }
    }
    

    Combining these 2 stages together will give us the expected result, one document looking like this:

    {
      "_id": null, // We don't care about this
      "chats": [
        "John",
        "James",
        "Daniel"
      ]
    }
    

    Final Solution

    Message.aggregate([{
     $match: {
      $or: [
       {
        sender: req.user.username
       },
       {
        receiver: req.user.username
       }
      ]
     }
    }, {
     $group: {
      _id: null,
      chats: {
       $addToSet: {
        $cond: {
         'if': {
          $eq: [
           '$sender',
           req.user.username
          ]
         },
         then: '$receiver',
         'else': '$sender'
        }
       }
      }
     }
    }])
    
    

    Sources

    Login or Signup to reply.
  2. One option is to use an aggregation pipeline to create two sets and simply union them:

    db.collection.aggregate([
      {$match: {$or: [{sender: req.user.username}, {reciever: req.user.username}]}},
      {$group: {
          _id: 0,
          recievers: {$addToSet: "$reciever"},
          senders: {$addToSet: "$sender"}
      }},
      {$project: {
          _id: req.user.username,
          previousChats: {"$setDifference": 
            [
              {$setUnion: ["$recievers", "$senders"]},
              [req.user.username]
            ]
          }
      }}
    ])
    

    See how it works on the playground example

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search