MongoDB - групповые документы затем суммируют вложенные вложенные документы - PullRequest
0 голосов
/ 31 января 2019

У меня есть коллекция монго, содержащая документы, в которых есть вложенные документы, для которых я хочу суммировать поля - пример того, чего я надеюсь достичь, ниже

Общая структура каждого документа

{
    "pool" : "Foo",
    "monthly-figures" : {
        "1": {
            "a" : 311,
            "b" : 1481,
            ...
            "x" : {"a" : 311, "b" : 19.965999999999998},
            "y" : {"a" : 200, "b" : 14.174000000000003
            }
        },
        "2": {
            "a" : 500,
            "b" : 100,
            ...
            "x" : {"a" : 123, "b" : 198},
            "y" : {"a" : 200, "b" : 13.7}
        },
        ... // May not all be present
        "12": {...}
        }
    }
}

Причина, по которой ежемесячно хранится как объект, а не как массив, состоит в том, что некоторые месяцы могут отсутствовать.

Возьмем, к примеру, три документа

{
    "pool" : "Foo",
    "monthly-figures" : {
        "1": {
            "a" : 10,
            "b" : 20,
            ...
            "x" : {"a" : 15, "b" :30}
            }
        },
        "2": {
            "a" : 500,
            "b" : 100,
            ...
            "x" : {"a" : 40, "b" : 50},
        },
        "7": {
            "a": 300,
            "b": 90,
            ...
            "x": {"a": 4, "b": 5}
        }
    }
}

{
    "pool" : "Foo",
    "monthly-figures" : {
        "1": {
            "a" : 15,
            "b" : 25,
            ...
            "x" : {"a" : 20, "b" : 35},
        },
        "2": {
            "a" : 250,
            "b" : 200,
            ...
            "x" : {"a" : 60, "b" : 80},
        }
    }
}


{
    "pool" : "Bar",
    "monthly-figures" : {
        "1": {
            "a" : 300,
            "b" : 400,
            ...
            "x" : {"a" : 51, "b" : 3},
            }
        },
        "6": {
            "a" : 75,
            "b" : 135,
            ...
            "x" : {"a" : 12.5, "b" : 16},
        }
    }
}

ЧтоЯ хочу добиться с помощью агрегации, чтобы сгруппировать на основе поля pool, а затем суммировать значения, содержащиеся в monthly-figures - так, чтобы получающиеся два документа выглядели как

{
    "pool" : "Foo",
    "monthly-figures" : {
        "1": {
            "a" : 25,
            "b" : 45,
            ...
            "x" : {"a" : 35, "b" : 65},
        },
        "2": {
            "a" : 750,
            "b" : 300,
            ...
            "x" : {"a" : 100, "b" : 130},
        },
        "7": {
            "a": 300,
            "b": 90,
            ...
            "x": {"a": 4, "b": 5}
        }
    }
}

(Документ с pool Бар будет идентичным, так как есть только 1)

Неважно, если месяц имеет все 0 значений после агрегирования (если, скажем, месяц не существует ни в одном документе группировки), нов идеале это не так?

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

{$group: {
        // Group to pool
        _id: "$pool",

        // Sum grouped documents
        "1a": {$sum: "$monthly-figures.1.a"},
        "1b": {$sum: "$monthly-figures.1.b"},
        ...
        "1xa": {$sum: "$monthly-figures.1.x.a"},
        "1xb": {$sum: "$monthly-figures.1.x.b"},


        "2a": {$sum: "$monthly-figures.2.a"},


        ... Continue all the way down to 12
    }
},
{$project: {
        "_id": 0,
        "pool": "$_id",

        "monthly-figures": {

            "1": {
                "a": "$1a",
                "b": "$1b",
                ...
                "x": {
                    "a": "$1xa",
                    "b": "$1xb"
                }
            },
            "2": {
                "a": "$2a",
                ...
            }

            ... Continue all the way down to 12
        }
    }
}

Есть идеи по чистоте трубопровода?Ура!

1 Ответ

0 голосов
/ 31 января 2019

Один из способов исключить необходимость составления списка каждый месяц - это превратить объект месячных цифр в массив.Вы сказали:

Причина, по которой ежемесячно хранится как объект, а не как массив, состоит в том, что некоторые месяцы могут отсутствовать.

Но массив все равно будет работать, потому чтоу вас может быть:

[{month: 1, figures: {...}}, {month: 6, figures: {...}}]

С массивом помесячных чисел мы можем $unwind каждый элемент массива в своем собственном документе.Теперь можно сделать $group как для пула, так и для месяца, чтобы получить суммы.Чтобы собрать месяцы для каждого пула, мы можем сделать еще один $group только для пула и $push специально сформированные объекты, содержащие месяц и цифры, для массива, называемого month-figure.Эти объекты являются особыми, потому что клавиши k и v распознаются оператором $arrayToObject, который использовался на следующем этапе, чтобы вернуть исходную форму.Вот запрос:

db.colx.aggregate([{
    "$project": {
        "pool": 1,
        "monthly-figures": {"$objectToArray": "$monthly-figures"}
    }
}, {
    "$unwind": "$monthly-figures"
}, {
    "$group": {
        "_id": {
            "pool": "$pool",
            "month": "$monthly-figures.k",      
        },
        "a": {"$sum": "$monthly-figures.v.a"},
        "b": {"$sum": "$monthly-figures.v.b"},
        "x_a": {"$sum": "$monthly-figures.v.x.a"},
        "x_b": {"$sum": "$monthly-figures.v.x.b"}
    }
}, {
    "$group": {
        "_id": "$_id.pool",
        "monthly-figures": {
            "$push": {
                "k": "$_id.month",
                "v": {
                    "a": "$a",
                    "b": "$b",
                    "x": {
                        "a": "$x_a",
                        "b": "$x_b"
                    }
                }
            }
        }
    }
}, {
    "$project": {
        "_id": 0,
        "pool": "$_id",
        "monthly-figures": {"$arrayToObject": "$monthly-figures"}
    }
}])

Вот результат запроса:

{
    "pool" : "Bar",
    "monthly-figures" : {
        "6" : {
            "a" : 75,
            "b" : 135,
            "x" : {
                "a" : 12.5,
                "b" : 16
            }
        },
        "1" : {
            "a" : 300,
            "b" : 400,
            "x" : {
                "a" : 51,
                "b" : 3
            }
        }
    }
}
{
    "pool" : "Foo",
    "monthly-figures" : {
        "1" : {
            "a" : 25,
            "b" : 45,
            "x" : {
                "a" : 35,
                "b" : 65
            }
        },
        "2" : {
            "a" : 750,
            "b" : 300,
            "x" : {
                "a" : 100,
                "b" : 130
            }
        },
        "7" : {
            "a" : 300,
            "b" : 90,
            "x" : {
                "a" : 4,
                "b" : 5
            }
        }
    }
}

Ссылки на документацию MongoDB:

...