2015-10-27 4 views
4

Я хочу получить «все документы», имеющие наибольшее значение для определенного поля, а не группу по другому полю.получить все документы, имеющие максимальное значение, используя агрегацию в mongodb

Рассмотрим ниже данных:

_id:1, country:india, quantity:12, name:xyz 
_id:2, country:USA, quantity:5, name:abc 
_id:3, country:USA, quantity:6, name:xyz 
_id:4, country:india, quantity:8, name:def 
_id:5, country:USA, quantity:10, name:jkl 
_id:6, country:india, quantity:12, name:jkl 

Ответ должен быть

country:india max-quantity:12 
name xyz 
name jkl 

country:USA max-quantity:10 
name jkl 

Я попробовал несколько запросов, но я могу получить только максимальное значение без имени или я могу пойти группу, но это показывает все значения.

db.coll.aggregate({$group:{_id:"$country","maxQuantity":{$max:"$quantity"}}}) 

, например, выше, будет давать максимальное количество на каждой стране, но как совместить с другой областью, например, что он показывает все документы максимального количества.

+0

добавить комментарий добавочное поле в $ group сразу после поля _id –

+0

@AlokDeshwal вы можете дать мне точный запрос –

+0

@AlokDeshwal Это не сработает, так как цель состоит в том, чтобы найти «максимальное» значение для каждой страны, а затем вернуть только другие значения из документов «соответствие», которые значение «max». –

ответ

8

Если вы хотите сохранить информацию о документе, тогда вам в основном нужно указать $push в массив. Но, конечно же, то имея свои $max значения, вам нужно фильтровать содержимое массива для всего элементов, которые соответствуют:

db.coll.aggregate([ 
    { "$group":{ 
     "_id": "$country", 
     "maxQuantity": { "$max": "$quantity" }, 
     "docs": { "$push": { 
      "_id": "$_id", 
      "name": "$name", 
      "quantity": "$quantity" 
     }} 
    }}, 
    { "$project": { 
     "maxQuantity": 1, 
     "docs": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$docs", 
        "as": "doc", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$maxQuantity", "$$doc.quantity" ] }, 
          "$$doc", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 

Так вы храните все в массиве, а затем проверить каждый элемент массива, чтобы увидеть, если это значение соответствует та, которая была записана как максимальная, отбрасывая любые, которые этого не делают.

Я бы сохранил значения _id в документах массива, так как именно это делает их «уникальными» и не будет отрицательно влиять на $setDifference при фильтрации значений. Но, конечно, если «имя» всегда уникально, оно не потребуется.

Вы также можете просто вернуть все поля, которые хотите получить от $map, но я просто возвращаю весь документ, например.

Имейте в виду, что это ограничение не превышает ограничение размера BSON 16 МБ, так что это нормально для небольших выборок данных, но все, что создает потенциально большой список (поскольку вы не можете предварительно фильтровать содержимое массива), было бы лучше обрабатывается отдельным запросом, чтобы найти значения «max», а другой - для получения соответствующих документов.

+0

Ваше решение работает отлично, но его немного сложное для новичка, подобного мне, поскольку я не знаю о $ map, $ setDifference ... все же я постараюсь понять и реализовать его. Но есть ли более простой способ достичь этого? –

+0

@ViyatGandhi Простой взгляд Да, используйте '$ unwind' и отфильтровывайте массив с' $ match' перед группировкой снова или просто условно '$ sum'. Но это не «эффективный» способ, поэтому вы должны придерживаться этого. Будущие выпуски MongoDB будут иметь оператор '$ filter', который немного упростит это. Ключ понимает, что делает '$ map', поэтому посмотрите на связанную документацию и поработайте с некоторыми образцами.Как было сказано, более крупные сгруппированные результаты в любом случае должны быть отдельными запросами. –

+0

Это экстремально сложный. Есть ли альтернативный синтаксис? возможно, менее подробный? – Danielo515

1

Я знаю, как сделать подобную задачу проще, только если изменить определенный диапазон стран:

[ 
{"$match":{"name":{"$in":["USA","india"]}}}, // stage one 
{ "$sort": { "quanity": -1 }}, // stage three 
{"$limit":2 } // stage four - count equal ["USA","india"] length 
] 

Если вам нужны все страны пытаются следовать, но без гарантий от меня:

[ 
{"$project": { 
    "country": "$country", 
    "quantity": "$quantity", 
    "document": "$$ROOT" // save all fields for future usage 

}}, 
{ "$sort": { "quantity": -1 }}, 
{"$group":{"_id":{"country":"$country"},"original_doc":{"$first":"$document"} }} 
] 
+0

Если вам нужны все страны, попробуйте $ sort descend, а затем $ group sampling first match каждой группы. – basil

+0

Василий, не могли бы вы уточнить? Это стратегия, которую я имею в виду, но я не могу выплюнуть ее. –

+0

Второй - блестящий. Спасибо. –

Смежные вопросы