2017-01-12 4 views
2

Итак, я хочу использовать агрегацию конвейера в MongoDB для запроса определенных значений из документов, а затем добавить их вместе.Объединение агрегации агрегатов MongoDB вместе

Мой альбом "Альбомы".

{ 
"_id" : ObjectId("5875ed1dc939408da0601f31"), 
"AlbumName" : "Blurryface", 
"Artist" : "21 Pilots", 
"Date" : "20151110", 
"Label" : "Fueled By Ramen", 
"Writers" : "Tyler Joseph", 
"Producer" : "Mike Elizondo", 
"Songlist" : [ 
    { 
     "_id" : ObjectId("5875e5e8c939408da0601d73"), 
     "SongID" : "1", 
     "SongName" : "Stressed Out", 
     "Artist" : "21 Pilots", 
     "Album" : "Blurryface", 
     "Duration:" : "200", 
     "nPlays" : 800000000, 
     "SongDataFile" : "data" 
    }, 
    { 
     "_id" : ObjectId("5875e855c939408da0601dcc"), 
     "SongID" : "4", 
     "SongName" : "Heathens", 
     "Artist" : "21 Pilots", 
     "Album" : "Blurryface", 
     "Colaborator" : "NA", 
     "Duration:" : "320", 
     "nPlays" : 5000000, 
     "SongDataFile" : "data" 
    } 
] 
} 

Как я могу сделать трубопровод агрегации, который извлекает «nPlays» из песен в массиве, а затем добавить их вместе?

Я прошу здесь, поскольку документация по MongoDB является подпарами, и у них нет примеров того, как использовать эти операторы вместе. Добавьте к этому, что все примеры только для google-запроса для $ gt $ lt или используют тот же пример, который использует только $ match и $ group, что не помогает мне с моей проблемой вообще.

Короче:

Как извлечь «nPlays» и добавить их в агрегации трубопроводной?

+1

Какая у вас версия MongoDB? – chridam

ответ

4

У вас должно быть unind внутренние документы. Эта операция создает документ для каждого вложенного документа в поле Songlist.

В результате агрегации трубопроводов заключается в следующем:

db.Albums.aggregate([ 
    {$unwind: {path: "$Songlist"}}, 
    {$project : { "_id" : 0, "AlbumName" : 1, "Songlist.nPlays" : 1} }, 
    {$group : {"_id" : "$AlbumName", "sum" : {"$sum" : "$Songlist.nPlays"}}} 
]) 

В результате документ заключается в следующем:

{ 
    "_id" : "Blurryface", 
    "sum" : 805000000 
} 

В целом, с $unwind операции вы сплющить внутренние поддокументы. Затем, с простым $project, вы можете сохранить только нужные вам поля (этот этап необязателен). Наконец, используя $group, вы можете суммировать информацию, которая вам нужна.

Надеюсь, это поможет.

+0

А, ладно. Я знал о $ unwind, но я не знал, что вы можете использовать $ group, а затем $ sum для указанного атрибута группы. Большое спасибо! – DannyBoy

+0

Последующий вопрос, если у вас есть время. Скажем, у меня много альбомов в моей коллекции «Альбомы». Как я тогда группирую общее количество nPlays для каждого альбома и сравниваю это, а затем $ limit к самому воспроизведенному альбому? (Какой альбом имеет наибольшие nTotalPlays? – DannyBoy

+0

Конвейер я дал вам уже агрегаты для каждого альбома. –

1

Вы можете использовать агрегат с $ group. Это даст общее количество для всех записей.

db.collectionName.aggregate([ 
    {$unwind: '$Songlist'}, 
    {$group: {_id: null, sum: {$sum: '$Songlist.nPlays'}}} 
]) 
2

Для наиболее эффективного решения, которое не нуждается в нескольких трубопроводов, я хотел бы предложить натыкаясь сервер MongoDB 3.4 (при использовании более ранних версий), а также использовать новый оператор $reduce массива для добавления значения поля в массиве Songlist бесшовно.

Он вычисляет сумму полей "Songlist.nPlays" в массиве путем применения выражения к каждому элементу массива и объединения их в одно значение.

Вы можете использовать это как выражение с $addFields трубопровода, чтобы получить нужное поле вместе с другими полями:

db.collection.aggregate([ 
    { 
     "$addFields": { 
      "totalPlayDuration": { 
       "$reduce": { 
        "input": "$Songlist", 
        "initialValue": 0, 
        "in": { "$add": ["$$value", "$$this.nPlays"] } 
       } 
      } 
     } 
    }   
]) 

Пример вывода

/* 1 */ 
{ 
    "_id" : ObjectId("5875ed1dc939408da0601f31"), 
    "AlbumName" : "Blurryface", 
    "Artist" : "21 Pilots", 
    "Date" : "20151110", 
    "Label" : "Fueled By Ramen", 
    "Writers" : "Tyler Joseph", 
    "Producer" : "Mike Elizondo", 
    "Songlist" : [ 
     { 
      "_id" : ObjectId("5875e5e8c939408da0601d73"), 
      "SongID" : "1", 
      "SongName" : "Stressed Out", 
      "Artist" : "21 Pilots", 
      "Album" : "Blurryface", 
      "Duration:" : "200", 
      "nPlays" : 800000000, 
      "SongDataFile" : "data" 
     }, 
     { 
      "_id" : ObjectId("5875e855c939408da0601dcc"), 
      "SongID" : "4", 
      "SongName" : "Heathens", 
      "Artist" : "21 Pilots", 
      "Album" : "Blurryface", 
      "Colaborator" : "NA", 
      "Duration:" : "320", 
      "nPlays" : 5000000, 
      "SongDataFile" : "data" 
     } 
    ], 
    "totalPlayDuration": 805000000 
} 

NB: Решение, которое использует $unwind, может быть не столь эффективным по шкале и ожидать снижения производительности при работе с большими массивами, поскольку оно производит декартовое произведение документов, то есть копию каждого документа на запись в массив, которая использует больше памяти (возможно колпачок памяти на агрегатных трубопроводах с общей памятью 10%), и поэтому требуется время для обработки, а также обработки документов во время процесса сглаживания.

Кроме того, многократное решение трубопровода требует знаний полого документа, так как это необходимо в трубопроводе $group где сохранить поля в процессе группировки, используя аккумуляторы, как $first или $last. Это может быть огромным ограничением, если ваш запрос должен быть динамическим. Поэтому по существу было бы выгоднее использовать новые операторы, найденные в версиях MongoDB версии 3.4 и выше, которые предлагают улучшенную производительность агрегационного конвейера.

+0

imho вы не даете решения для исходного варианта использования. информация, которую вы дали, чрезвычайно полезна и правильна, но они не являются ответом на исходный вопрос. –

+1

Благодарим за отзыв. Вопрос OP: «Как я могу создать конвейер агрегации, который извлекает« nPlays »из песен в массиве а затем добавить их вместе? _ и приведенные выше адреса ответов, которые с помощью '$ reduce', который извлекает поле' nPlays' без необходимости '$ unwind', которое может быть дорогостоящим вычислительно заданным lar ge. Теперь я не уверен, как этот ответ не подходит/не разрешает вопрос OP, как вы подразумеваете, не хотите показать мне, где я ошибся? – chridam

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