Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongodb apply sort to lookup results

Tags:

mongodb

If I have a user and post collection

{"_id": 1, "name": "User 1"}
{"_id": 2, "name": "User 2"}

{"_id": 1, "title": "Post 1", "userId": 1, "createdAt": ISODate("2017-07-24T04:12:54.255Z")}
{"_id": 2, "title": "Post 2", "userId": 1, "createdAt": ISODate("2017-07-25T04:12:54.255Z")}
{"_id": 3, "title": "Post 1", "userId": 2, "createdAt": ISODate("2017-07-24T04:12:54.255Z")}

How can I list all users with their latest post? Would be something like

{
  "_id": 1,
  "name": "User 1",
  "post": {
    "_id": 2,
    "title": "Post 2",
    "userId": 1,
    "createdAt": ISODate("2017-07-25T04:12:54.255Z")
  }
}

I know I can easily use $lookup, $unwind post, then $sort by post.createdAt, but that leave me with redundant user (User 1 will be listed twice for Post 1 and Post 2).

I don't know how can I use $group to remove duplicates while keep maintaining other fields (name, post.title, etc)

like image 828
spondbob Avatar asked Dec 01 '17 00:12

spondbob


People also ask

How do I sort values in MongoDB?

To sort documents in MongoDB, you need to use sort() method. The method accepts a document containing a list of fields along with their sorting order. To specify sorting order 1 and -1 are used. 1 is used for ascending order while -1 is used for descending order.

Does lookup use index MongoDB?

Starting in MongoDB 5.0, the $eq , $lt , $lte , $gt , and $gte comparison operators placed in an $expr operator can use an index on the from collection referenced in a $lookup stage. Limitations: Multikey indexes are not used.

How do I sort Two fields in MongoDB?

We can use the following code to sort the documents in the teams collection first by “points” ascending and then by “rebounds” ascending: What is this? Notice that the documents are sorted by the “points” field ascending (smallest to largest) and then by the “rebounds” field ascending (smallest to largest).


3 Answers

I solved the duplicates using $group and $first

db.getCollection('user').aggregate([
    {$lookup: {from: "post", localField: "_id", foreignField: "userId", as: "post"}},
    {$unwind: { path: "$post", preserveNullAndEmptyArrays: true }},
    {$sort: {"post.createdAt": -1}},
    {$group: {"_id": "$_id", "name": {$first: "$name"}, "post": {$first: "$post"}},
    {$project: {"_id": 1, "name": 1, "post": 1}}
])

Feel free to post your answer

like image 152
spondbob Avatar answered Oct 21 '22 02:10

spondbob


You can solve this in a single aggregation step with the new $lookup syntax

db.getCollection('users').aggregate([{
    '$lookup': {
      'from': 'posts',
      'let': {
        'userId': '$_id'
      },
      'pipeline': [{
          '$match': { '$expr': { '$eq': ['$userId', '$$userId'] } }
        }, {
          '$sort': {  'createdAt': -1 }
        }, {
          '$limit': 10
        },
      ],
      'as': 'posts'
    }
  }
])

Note: untested code, but the principle should be clear.

like image 45
lukasvo Avatar answered Oct 21 '22 02:10

lukasvo


here is a way you can use

db.getCollection('post').aggregate([
{ $limit: 10 },

{$sort: {"createdAt": -1}},

{$lookup: {from: "user", localField: "userId", foreignField: "_id", as: "user"}},
])

you should query post which sorted by date and join with user. so if you want to provide a limit for posts then you can.

like image 2
manan5439 Avatar answered Oct 21 '22 03:10

manan5439