spondbob
spondbob

Reputation: 1633

mongodb apply sort to lookup results

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)

Upvotes: 26

Views: 32426

Answers (3)

spondbob
spondbob

Reputation: 1633

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

Upvotes: 27

lukasvo
lukasvo

Reputation: 4842

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.

Upvotes: 37

manan5439
manan5439

Reputation: 958

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.

Upvotes: 3

Related Questions