2015-11-30 10 views
8

Mam kolekcję jakJak uzyskać maksymalną wartość pola dla każdej grupy z tablicą odpowiednich dokumentów?

{ 
    "_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" 
     } 
    ] 
} 

Chcę mieć wyniki z najwyższego wynagrodzenia przez każdego działu

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

mogę dostać najwyższe wynagrodzenie. Ale nie mogłem uzyskać odpowiednich nazwisk pracowników.

db.HR.aggregate([ 

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

Czy ktoś może mi pomóc?

+0

wielkie pytanie, można dodać co próbowali i masz błąd? – inspired

+0

Mogłem rozwiązać to w SQL z samołączeniem. Ale nie mogę znaleźć sposobu na Mongo – dapangmao

Odpowiedz

7

Musisz mieć dokument o numerze: $sort po $unwind i użyć operatora $first w etapie $group. Można również użyć operatora $last w takim przypadku trzeba będzie uporządkować dokumenty w kolejności rosnącej

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

która produkuje:

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

Aby przywrócić maksymalną listę płac i pracowników dla każdego działu musisz użyć numeru $max na swoim etapie grupowym, aby zwrócić maksymalną "Wynagrodzenie" dla każdej grupy, a następnie użyj operatora akumulatorów $push, aby zwrócić listę "Nazwa "i" Wynagrodzenie "dla wszystkich pracowników dla każdej grupy. Stamtąd musisz użyć operatora $map na swoim etapie $project, aby zwrócić listę nazw wraz z maksymalną pensją. Oczywiście tutaj $cond służy do porównywania każdej pensji pracownika do maksymalnej wartości. $setDifference wykonuje swoją pracę, odfiltrowując wszystkie false i jest w porządku, dopóki filtrowane dane są "unikalne". W tym przypadku "powinno" być w porządku, ale jeśli dowolne dwa wyniki zawierały tę samą "nazwę", wówczas wynik byłby przekrzywiony, biorąc pod uwagę, że oba są jednością.

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] 
      ] 
     } 
    }} 
]) 

co daje:

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

Jego nie najbardziej intuicyjny rzeczą, ale zamiast $max należy używać $sort i $first:

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

Alternatywnie, myślę, że jest to wykonalne za pomocą operatora $$ROOT (uczciwe ostrzeżenie: tak naprawdę tego nie próbowałem) -

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

Innym możliwym rozwiązaniem:

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 
    }} 
]) 

Rozwiązanie to wykorzystuje operatora $eq porównać dwa pola w etapie $project.

przypadek testowy:

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"}]}) 

Wynik:

{ "_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 } 
Powiązane problemy