Есть ли способ использовать Aggregation или $ sum для динамического встроенного объекта? - PullRequest
0 голосов
/ 26 июня 2018

У меня есть коллекция с такой структурой:

{
  field1:'Foo',
  field2:11,
  stats:{
   A:10,
   B:15,
   C:10
 }
}

Теперь я хочу обобщить все свойства поля статистики. Но поле статистики является объектом и может отличаться от документа к документу (всегда только числовые поля, но имя полей может меняться), так это может выглядеть примерно так:

{
  field1:'Foo2',
  field2:12,
  stats:{
   A:10,
   B:10,
   D:5
 }
}

есть ли способ использовать агрегацию или $ сумму, чтобы получить такой результат:

{
  sumStats:{
   A:20,
   B:25,
   C:10,
   D:5
 }
}

Ответы [ 2 ]

0 голосов
/ 26 июня 2018

Вы также можете попробовать агрегат mapReduce. Документы говорят, что он может быть менее эффективным, но его очень легко читать (это использует синтаксис Mongoose, но это просто тонкая оболочка над функцией с тем же именем в драйвере Mongo):

const mrd = {
  //creates an output bucket for each property in stats object
  map: function () { for (const prop in this.stats) emit(prop, this.stats[prop])},
  //sums all the values in the output bucket for each property
  reduce: function (k, vals) { return vals.reduce((accumulator, currentValue) => accumulator + currentValue, 0)}
};

YourModel.mapReduce(mrd, function (err, results) {
  console.log(results)
  //something like this (ran at Mongo console, assuming Mongoose output is similar): 
  //[{ "_id" : "a", "value" : 20 },
  //{ "_id" : "b", "value" : 25 },
  //{ "_id" : "c", "value" : 10 }
  //{ "_id" : "d", "value" : 5 }
})

Вывод не в одном объекте, но это достаточно просто сделать в прямом коде (например, Array.mapReduce ());

Refs: http://mongoosejs.com/docs/api.html#mapreduce_mapReduce https://docs.mongodb.com/manual/tutorial/map-reduce-examples/

0 голосов
/ 26 июня 2018

Вы можете попробовать ниже агрегации:

db.col.aggregate([
    {
        $project: {
            stats: { $objectToArray: "$stats" }
        }
    },
    {
        $unwind: "$stats"
    },
    {
        $group: {
            _id: "$stats.k",
            value: { $sum: "$stats.v" }
        }
    },
    {
        $group: {
            _id: null,
            values: { $push: { k: "$_id", v: "$value" } }
        }
    },
    {
        $replaceRoot: {
            newRoot: { $arrayToObject: "$values" }
        }
    }
])

Поскольку ваши ключи неизвестны, вам нужно $ objectToArray , чтобы преобразовать stats в список пар ключ-значение. Затем вы можете использовать $ unwind , чтобы получить отдельные документы для каждой пары ключ-значение. На следующем шаге вы можете использовать $ group для агрегирования значений из всех документов. В результате вам нужен один объект, поэтому вам нужно сгруппировать по null, чтобы собрать все значения в одном объекте в виде списка пар ключ-значение, а затем вы можете использовать $ arrayToObject с $ replaceRoot для получения окончательной структуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...