Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

|MongoDB| How to sort and limit embedded arrays? embedded array pagination

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??

like image 365
Misael Neto Avatar asked Feb 15 '23 20:02

Misael Neto


1 Answers

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"}}}
)
like image 182
Yann Moisan Avatar answered Feb 17 '23 15:02

Yann Moisan