Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a complete object via $max in mongodb aggregation

I have a collection of objects that need to be grouped by masterID or id if masterID is absent. Of a group of objects with the same masterID I need to find the one with the highest pos attribute ($max: "$pos"). However, I struggle to get the complete object where pos is maximized, it seems via aggregation I can only get the pos attribute, but not the whole object. Here is the sample aggregation I use, lacking the $ifNull for masterID:

> db.tst.aggregate([{ "$group": { "_id": "$masterID", "pos": { "$max": "$pos" } } }])

Sample objects would be:

> db.tst.find()
{ "_id" : ObjectId("547d6bd28e47d05a9a492e2e"), "masterID" : "master", "pos" : "453", "id" : "hallo" }
{ "_id" : ObjectId("547d6bda8e47d05a9a492e2f"), "masterID" : "master", "pos" : "147", "id" : "welt" }
{ "_id" : ObjectId("547d6be68e47d05a9a492e30"), "masterID" : "master2", "pos" : "1", "id" : "welt" }

The wanted aggregation result is:

{ "_id" : ObjectId("547d6bd28e47d05a9a492e2e"), "masterID" : "master", "pos" : "453", "id" : "hallo" }
{ "_id" : ObjectId("547d6be68e47d05a9a492e30"), "masterID" : "master2", "pos" : "1", "id" : "welt" }

Is there a way to achieve this result via aggregation, or do I need to use $push to obtain all grouped objects and implement the maximisation on pos in the Java code around the aggregation?

This question mongodb aggregation: How to return a the object with min/max instead of the value indicates that $first or $last should be used on the objects sorted by pos, but I fail to see how that returns the whole object.

like image 259
TeTeT Avatar asked Nov 23 '25 01:11

TeTeT


2 Answers

Start by sorting by pos descending. That way, the first document encountered for each masterID is the document with the highest pos. You can use $$ROOT to refer to the entire document currently being processed in the pipeline.

db.tst.aggregate([
    { "$sort" : { "pos" : -1 } },
    { "$group": { 
        "_id": { $ifNull: [ "$masterID", "$_id" ] },
        "max_doc" : { "$first" : "$$ROOT" }
    } }
])
like image 181
wdberkeley Avatar answered Nov 26 '25 14:11

wdberkeley


If I have understood right, you could try this:

db.tst.aggregate([
    { "$sort" : { "pos" : -1 } },
    { 
    "$group": { 
        "_id": { $ifNull: [ "$masterID", "$id" ] },    
           "_Tempid":{$first:"$_id"},
           "masterID":{$first:{ $ifNull: [ "$masterID", "$id" ] }},
           "pos": { "$max": "$pos" },
           "id" : {$first:"$id"}

      }  
  },{
      $project:{
          _id:"$_Tempid",
          "masterID":1,
          "pos":1,
          id:1
      }
  }

])

result:

{
    "result" : [ 
        {
            "_id" : ObjectId("547d6be68e47d05a9a492e30"),
            "masterID" : "master2",
            "pos" : "1",
            "id" : "welt"
        }, 
        {
            "_id" : ObjectId("547d6bd28e47d05a9a492e2e"),
            "masterID" : "master",
            "pos" : "453",
            "id" : "hallo"
        }
    ],
    "ok" : 1
}
like image 44
Barno Avatar answered Nov 26 '25 12:11

Barno