Arhn - архитектура программирования

Mongodb и фреймворк агрегации. Суммирование элементов массивов и суммирование элементов документов

Есть много документов:

{
        "_id"   : ObjectId("506ddd1900a47d802702a904"),
        "subid" : "s1",
        "total" : "300",
        "details" :[{
                      name:"d1", value: "100"
                    },
                    {
                      name:"d2", value: "200"
                    }]
}
{
        "_id"   : ObjectId("306fff1900a47d802702567"),
        "subid" : "s1",
        "total" : "700",
        "details" : [{
                      name:"d1", value: "300"
                    },
                    {
                      name:"d8", value: "400"
                    }]
 }

Элементы в массивах «детали» могут различаться.

Вопрос: как я могу получить такой результат с помощью фреймворка агрегации и java?

{
        "_id"     : "s1",
        "total"   : "1000",
        "details" : [{
                      name:"d1", value: "400"
                    },
                    {
                      name:"d2", value: "200"
                    },
                    {
                      name:"d8", value: "400"
                    }]
 }

Или, может быть, мне следует использовать здесь специальные функции уменьшения карты?


Ответы:


1

Это очень достижимо с помощью агрегата, хотя и немного туповато, но давайте рассмотрим его:

db.collection.aggregate([

    // First Group to get the *master* total for the documents
    {"$group": {
        "_id": "$subid",
         "total": { "$sum": "$total" },
         details: { "$push": "$details" } 
     }},

     // Unwind the details
     {"$unwind": "$details"},

     // Unwind the details "again" since you *pushed* and array onto an array
     {"$unwind":"$details"},

     // Now sum up the values by each name (keeping levels)
     {"$group": {
         "_id:" {
              "_id": "$_id",
              "total": "$total",
              "name":  "$details.name"
          },
          "value": {"$sum": "$details.value"}
      }},

     // Sort the names (because you expect that!)
     {"$sort": { "_id.name": 1}},

     // Do some initial re-shaping for convenience
     {"$project": {
         "_id": "$_id._id",
         "total": "$_id.total",
         "details": { "name": "$_id.name", "value": "$value" }
     }},

     // Now push everything back into an array form
     {"$group": {
         "_id": {
              "_id": "$_id",
              "total": "$total"
         },
         "details": {"$push": "$details"}
     }},

     // And finally project nicely
     {"$project": {
         "_id": "$_id._id",
         "total": "$_id.total",
         "details": 1 
     }}
])

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

По общему признанию, хитрость - это "разобраться" в целом двойной размотке, которая последует дальше. Поскольку в этой первой группе мы поместили массив в другой массив, то теперь мы получаем этот новый вложенный структуру, которую вам нужно развернуть дважды, чтобы перейти к "денормализованной" форме.

Как только вы это сделаете, вы просто $group до поля name:

Equiv (ГРУППА ПО _id, итог, "детали.имя")

Так примерно так, с некоторым разумным изменением формы. Затем я прошу отсортировать по name ключу (потому что вы напечатали его таким образом), и, наконец, мы $project в фактическую форму, которую вы хотели.

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

25.02.2014
  • Спасибо за такой быстрый ответ, Нил! Настоящая проблема заключается в том, что в одном документе есть много общих полей и много различных массивов деталей (на самом деле 7), каждый из которых содержит 5-100 элементов. Сколько размоток нужно использовать в таком случае? :) Может быть, мне изменить схему документа и извлечь эти массивы деталей в отдельные коллекции? 26.02.2014
  • @arctica Это звучит довольно сложно и, вероятно, лучше всего представлено в другом вопросе, где вы можете уточнить детали. Подход примерно такой же, но я или кто-то другой могу лучше объяснить конструкцию, если там есть вопрос с достаточной информацией, чтобы мы могли ответить. 26.02.2014
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..