2015-11-30 8 views
8

Ich habe eine Sammlung wieWie erhält man den maximalen Wert eines Feldes für jede Gruppe mit dem Array der entsprechenden Dokumente?

{ 
    "_id" : ObjectId("5738cb363bb56eb8f76c2ba8"), 
    "records" : [ 
     { 
      "Name" : "Joe", 
      "Salary" : 70000, 
      "Department" : "IT" 
     } 
    ] 
}, 
{ 
    "_id" : ObjectId("5738cb363bb56eb8f76c2ba9"), 
    "records" : [ 
     { 
      "Name" : "Henry", 
      "Salary" : 80000, 
      "Department" : "Sales" 
     }, 
     { 
      "Name" : "Jake", 
      "Salary" : 40000, 
      "Department" : "Sales" 
     } 
    ] 
}, 
{ 
    "_id" : ObjectId("5738cb363bb56eb8f76c2baa"), 
    "records" : [ 
     { 
      "Name" : "Sam", 
      "Salary" : 90000, 
      "Department" : "IT" 
     }, 
     { 
      "Name" : "Tom", 
      "Salary" : 50000, 
      "Department" : "Sales" 
     } 
    ] 
} 

Ich möchte mit dem höchsten Gehalt um die Ergebnisse haben von jeder Abteilung

{"Name": "Sam", "Salary": 90000, "Department": "IT"} 
{"Name": "Henry", "Salary": 80000, "Department": "Sales"} 

Ich konnte das höchste Gehalt bekommen. Aber ich konnte die entsprechenden Angestelltennamen nicht bekommen.

db.HR.aggregate([ 

    { "$unwind": "$records" }, 
    { "$group": 
     { 
      "_id": "$records.Department", 
      "max_salary": { "$max": "$records.Salary" } 
     } 
    } 
]) 

Könnte mir jemand helfen?

+0

große Frage, können Sie hinzufügen, was Sie versucht haben, und bekam einen Fehler entdeckt? – inspired

+0

Ich könnte es in SQL mit einem Self-Join lösen. Aber mit Mongo finde ich keinen Weg – dapangmao

Antwort

7

Sie müssen $sort Ihr Dokument nach $unwind und verwenden Sie den $first Operator in der $group Bühne. Sie können auch den $last Operator, in dem Fall, dass Sie Ihre Dokumente in aufsteigender Reihenfolge sortieren müssen

db.HR.aggregate([ 
    { '$unwind': '$records' }, 
    { '$sort': { 'records.Salary': -1 } }, 
    { '$group': { 
     '_id': '$records.Department', 
     'Name': { '$first': '$records.Name' } , 
     'Salary': { '$first': '$records.Salary' } 
    }} 
]) 

, die produziert:

{ "_id" : "Sales", "Name" : "Henry", "Salary" : 80000 } 
{ "_id" : "IT", "Name" : "Sam", "Salary" : 90000 } 

Um wieder das Höchstgehalt und Mitarbeiter Liste Für jede Abteilung müssen Sie $max in Ihrer Gruppenphase verwenden, um das maximale "Gehalt" für jede Gruppe zurückzugeben, und dann $push Akkumulatoroperator verwenden, um eine Liste von "Name "und" Gehalt "für alle Mitarbeiter für jede Gruppe. Von dort müssen Sie den $map Operator in Ihrer $project Bühne verwenden, um eine Liste der Namen neben dem maximalen Gehalt zurückzugeben. Natürlich wird hier der $cond verwendet, um das Gehalt jedes Angestellten mit dem Höchstwert zu vergleichen. Die $setDifference macht seine Arbeit, die alle false herausfiltert und ist in Ordnung, solange die Daten gefiltert werden "einzigartig". In diesem Fall sollte es "in Ordnung" sein, aber wenn zwei Ergebnisse den gleichen "Namen" enthielten, würde es die Ergebnisse verzerren, wenn man die beiden als Eins betrachtet.

db.HR.aggregate([ 
    { '$unwind': '$records' }, 
    { '$group': { 
     '_id': '$records.Department', 
     'maxSalary': { '$max': '$records.Salary' }, 
     'persons': { 
      '$push': { 
       'Name': '$records.Name', 
       'Salary': '$records.Salary' 
      } 
     } 
    }}, 
    { '$project': { 
     'maxSalary': 1, 
     'persons': { 
      '$setDifference': [ 
       { '$map': { 
        'input': '$persons', 
        'as': 'person', 
        'in': { 
         '$cond': [ 
          { '$eq': [ '$$person.Salary', '$maxSalary' ] }, 
          '$$person.Name', 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 

, die ergibt:

{ "_id" : "Sales", "maxSalary" : 80000, "persons" : [ "Henry" ] } 
{ "_id" : "IT", "maxSalary" : 90000, "persons" : [ "Sam" ] } 
3

Es ist nicht die intuitive Sache, aber statt $max sollten Sie $sort und $first verwenden sein:

{ "$unwind": "$records" }, 
{ "$sort": { "$records.Salary": -1}, 
{ "$group" : 
    { 
     "_id": "$records.Department", 
     "max_salary": { "$first": "$records.Salary" }, 
     "name": {$first: "$records.Name"} 
    } 
} 

Alternativ ich denke, das ist machbar mit dem $$ROOT Operator (faire Warnung: Ich habe es nicht wirklich versucht) -

{ "$unwind": "$records" }, 
{ "$group": 
     { 
      "_id": "$records.Department", 
      "max_salary": { "$max": "$records.Salary" } 
      "name" : "$$ROOT.records.Name" 
     } 
    } 
} 
1

Eine andere mögliche Lösung:

db.HR.aggregate([ 
    {"$unwind": "$records"}, 
    {"$group":{ 
     "_id": "$records.Department", 
     "arr": {"$push": {"Name":"$records.Name", "Salary":"$records.Salary"}}, 
     "maxSalary": {"$max":"$records.Salary"} 
    }}, 
    {"$unwind": "$arr"}, 
    {"$project": { 
     "_id":1, 
     "arr":1, 
     "isMax":{"$eq":["$arr.Salary", "$maxSalary"]} 
    }}, 
    {"$match":{ 
     "isMax":true 
    }} 
]) 

Diese Lösung nutzt die $eq Operator zwei Felder in der $project Bühne zu vergleichen.

Testfall:

db.HR.insert({"records": [{"Name": "Joe", "Salary": 70000, "Department": "IT"}]}) 
db.HR.insert({"records": [{"Name": "Henry", "Salary": 80000, "Department": "Sales"}, {"Name": "Jake", "Salary": 40000, "Department": "Sales"}, {"Name": "Santa", "Salary": 90000, "Department": "IT"}]}) 
db.HR.insert({"records": [{"Name": "Sam", "Salary": 90000, "Department": "IT"}, {"Name": "Tom", "Salary": 50000, "Department": "Sales"}]}) 

Ergebnis:

{ "_id" : "Sales", "arr" : { "Name" : "Henry", "Salary" : 80000 }, "isMax" : true } 
{ "_id" : "IT", "arr" : { "Name" : "Santa", "Salary" : 90000 }, "isMax" : true } 
{ "_id" : "IT", "arr" : { "Name" : "Sam", "Salary" : 90000 }, "isMax" : true } 
Verwandte Themen