简体   繁体   中英

Query from two collections in MongoDB

I'm trying to find my way around MongoDB. It's the first time I'm using this database, coming from MySQL. But for a chat application I'm making, MongoDB was recommended as a better fit.

I have two collections:

conversations in which I store the members userID (which is stored in a MySQL database) and the join date.

{
    "_id" : ObjectId("5e35f2c840713a43aeeeb3d9"),
    "members" : [ 
        {
            "uID" : "1",
            "j" : 1580580922
        }, 
        {
            "uID" : "4",
            "j" : 1580580922
        }, 
        {
            "uID" : "5",
            "j" : 1580580922
        }
    ]
}

messages in which I store the sender (userID), message, timestamp, conversationID (from the collection above), read and delivered status

{
    "_id" : ObjectId("5e35ee5f40713a43aeeeb1c5"),
    "c_ID" : ObjectId("5e35f2c840713a43aeeeb3d9"),
    "fromID" : "1",
    "msg" : "What's up?",
    "t" : 1580591922,
    "d" : {
        "4" : 1580592039
    },
    "r" : {
        "4" : 1580592339
    }
}

What I want to do now is query the conversations for a specific user, let's say userID 1, together with the last message sent in that conversation.

I came up with the following:

db.getCollection('conversations').aggregate(
[{
    $match: {
        "members.uID": "1"
    }
},
{
    $lookup: {
        as: 'lastMessage',
        foreignField: 'c_ID',
        from: 'messages',
        localField: '_id',
    }
},
])

But the problem here is that it lists all the messages, not only the last one. So I would like to limit this to 1, or if there is an alternative approach.. please let me know.

Any help is appreciated!

I guess we can understand the last message from timestamp field.

After $match , and $lookup stages, we need to $unwind messages, and then $sort by timestamp.

Now the first message in the messages array is the lastMessage, so when we group , we push the first message as lastMessage, and finally $replaceRoot to shape our result.

If so you can use the following aggregation:

db.conversations.aggregate([
  {
    $match: {
      "members.uID": "1"
    }
  },
  {
    $lookup: {
      foreignField: "c_ID",
      from: "messages",
      localField: "_id",
      as: "messages"
    }
  },
  {
    "$unwind": "$messages"
  },
  {
    "$sort": {
      "messages.t": -1
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "lastMessage": {
        "$first": "$messages"
      },
      "allFields": {
        "$first": "$$ROOT"
      }
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {
        "$mergeObjects": [
          "$allFields",
          {
            "lastMessage": "$lastMessage"
          }
        ]
      }
    }
  },
  {
    $project: {
      messages: 0
    }
  }
])

If the messages array is already sorted, the solution can be simplified, but this is a general solution.

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM