Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return whole document from aggregation

I'm using the following query to fetch one most recent comment for every post in database:

db.comments.aggregate([
    {
        "$match": {
            "post_id": {
                "$in": [ObjectId("52c5ce24dca32d32740c1435"), ObjectId("52c5ce24dca32d32740c15ad")]
            }
        }
     },
     {
         "$sort": {"_id": -1}
     },
     {
        "$group": {
            "_id": "$post_id",
            "lastComment": {
                "$first": "$_id"
            }
        }
     }
])

I expect it to return the whole comment's document but it only returns the _id field of each document. So what would be the proper way to get all most recent comments as a whole document (or at least include some other fields)?

like image 913
King Julien Avatar asked Jan 10 '14 19:01

King Julien


People also ask

What does aggregate return in MongoDB?

aggregate() method returns a cursor to the documents produced by the final stage of the aggregation pipeline operation, or if you include the explain option, the document that provides details on the processing of the aggregation operation.

Does aggregate return a cursor?

If the pipeline includes the $out operator, aggregate() returns an empty cursor.

Does aggregate return an array?

aggregate() method always returns Objects no matter what you do and that cannot change. However, that does not mean you cannot put them in an array and return the array in an object.

What is unwind in aggregation?

$unwind treats the sizes field as a single element array if: the field is present, the value is not null, and. the value is not an empty array.


2 Answers

Currently you cannot get the whole comment document via single $first operator. But you can include other necessary fields (similar to _id field) during $group step:

{
    "$group": {
        _id: "$post_id",
        lastComment: { "$first": "$_id" },
        field_1: { "$first": "$field_1" },
        field_2: { "$first": "$field_2" },
        // ...
        field_N: { "$first": "$field_N" }
    }
}

According to this JIRA ticket: https://jira.mongodb.org/browse/SERVER-5916, the whole document will be available to return from aggregation operations from 2.5.3 version. It will be possible using new variables: $$ROOT or $$CURRENT:

{
    "$group": {
        _id: "$post_id",
        lastComment: { "$first": "$$CURRENT" }
    }
}
like image 93
Shad Avatar answered Sep 18 '22 14:09

Shad


As suggested, we can do :

 {
    "$group": {
        _id: "$post_id",
        lastComment: { "$first": "$$CURRENT" }
    }
}

and then do use { '$replaceRoot': { 'newRoot': '$lastComment' } } on any mongodb server 3.4 or above to unwrap object from {lastComment:{actualEntireObj}},{lastComment:{actualEntireObj}} to {},{} this way it will get embedded $$ROOT document to the top level and replaces all other fields like _id returning from $group stage of aggregation.

    db.collection.aggregate([
    {
        "$match": {
            "post_id": {
                "$in": [ObjectId("52c5ce24dca32d32740c1435"), ObjectId("52c5ce24dca32d32740c15ad")]
            }
        }
    },
    {
        "$sort": { "_id": -1 }
    },
    {
        "$group": {
            _id: "$post_id",
            lastComment: { "$first": "$$CURRENT" }
        }
    },
    { '$replaceRoot': { 'newRoot': '$lastComment' } }
])
like image 33
whoami - fakeFaceTrueSoul Avatar answered Sep 20 '22 14:09

whoami - fakeFaceTrueSoul