I'm having the hardest time trying to sort and limit results of a embedded array in mongodb. Here's the scenario:
I have a post-comment structure where post contains an array of comments. I would like to get a list of comments, sorted by createdAt and do a limit/offset... Something like, given a post id paginate the comments and return them to me. =]
... heres a sample of the structure:
{ "_id" : ObjectId("52707a234f2044b7f2d22083"),
"comments" : [{
"_id" : ObjectId("5270986b4f204f5dd51ada8a"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:03.858Z")},
{
"_id" : ObjectId("527098714f204f5dd51ada8b"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:09.425Z")
}
],
"createdAt" : ISODate("2013-10-30T03:16:51.745Z"),
"likes" : [ ],
"status" : "simbora!!",
"userid" : NumberLong(1)
}
so... i could try this query this:
db.post.aggregate([
{$match: {_id: new ObjectId("52707a234f2044b7f2d22083")}},
{$unwind: "$comments"},
{$sort: {"comments.createdAt": -1}},
{$limit: 2}
]);
and it gave me this:
"result" : [
{
"_class" : "models.documents.Post",
"_id" : ObjectId("52707a234f2044b7f2d22083"),
"comments" : {
"_id" : ObjectId("527098714f204f5dd51ada8b"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:09.425Z")
},
"createdAt" : ISODate("2013-10-30T03:16:51.745Z"),
"likes" : [ ],
"status" : "simbora!!",
"userid" : NumberLong(1)
},
{
"_class" : "models.documents.Post",
"_id" : ObjectId("52707a234f2044b7f2d22083"),
"comments" : {
"_id" : ObjectId("5270986b4f204f5dd51ada8a"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:03.858Z")
},
"createdAt" : ISODate("2013-10-30T03:16:51.745Z"),
"likes" : [ ],
"status" : "simbora!!",
"userid" : NumberLong(1)
}
],
"ok" : 1
Note that comments is no longer an array, but an object... and, although it gives me the ordered and limited set, it also gives me the parent object for every comment... thats not so good. So I tried this:
db.post.aggregate([
{$match: {_id: new ObjectId("52707a234f2044b7f2d22083")}},
{$unwind: "$comments"},
{$sort: {"comments.createdAt": -1}},
{"$project": {"_id": 0, "comments": "$comments"}},
{"$group": {"_id": "$_id", "comments": {"$push": "$comments"}}},
]);
and it gave me this:
{
"result" : [
{
"_id" : null,
"comments" : [
{
"_id" : ObjectId("527098714f204f5dd51ada8b"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:09.425Z")
},
{
"_id" : ObjectId("5270986b4f204f5dd51ada8a"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:03.858Z")
},
{
"_id" : ObjectId("527098694f204f5dd51ada89"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:26:01.174Z")
},
{
"_id" : ObjectId("527098674f204f5dd51ada88"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:25:59.795Z")
},
{
"_id" : ObjectId("527098644f204f5dd51ada87"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:25:56.936Z")
},
{
"_id" : ObjectId("527098604f204f5dd51ada86"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:25:52.379Z")
},
{
"_id" : ObjectId("52707a234f2044b7f2d22083"),
"comment" : "asdfasdf asdfasdfa ",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:24:36.539Z")
},
{
"_id" : null,
"comment" : "bla bla",
"userid" : NumberLong(1),
"likes" : [ ],
"createdAt" : ISODate("2013-10-30T05:23:24.037Z")
}
]
}
],
"ok" : 1
}
this is also not so good because, if I apply the limit and skip operators, it will limit by post, not comment...
Can anyone help me out??
If I understand, you want to find a post by id and return it with only the last 2 comments.
You were not so far, the solution is a combination of your 2 trials :
db.posts.aggregate(
{$match: {_id: new ObjectId("52707a234f2044b7f2d22083")}},
{$unwind: "$comments"},
{$sort: {"comments.createdAt": -1}},
{$limit: 2},
{"$group": {"_id": "$_id", "comments": {"$push": "$comments"}}}
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With