Агрегирование mongodb. Получить массив значений приростов значений исходного массива. Это возможно?
К примеру есть коллекция из документов с числовыми полями N:
{ n: 2, name: a },
{ n: 6, name: b },
{ n: 10, name: a },
{ n: 10, name: a },
{ n: 10, name: b },
{ n: 11, name: a },
Я могу сгруппировать документы по name и положить все значения n в массив:
Collection.aggregate([
{ $group: { _id: '$name', values: {$push: '$n'} }} }
])
На выходе будет:
{ _id: a, values: [2, 10, 10, 11] },
{ _id: b, values: [6, 10] }
Вот собственно в чем вопрос! В документации не смог понять что искать и соответственно не нашел.
Мне нужно получить из этих массивов values массивы приростов значений, то есть следующие:
{ _id: a, values: [2, 8, 0, 1] }
// Ну то есть во таким образом из [2, 10, 10, 11] получить [2, 8, 0, 1]
// ( 2 - null == 2; 10 - 2 == 8; 10 - 10 == 0, 11 - 10 == 1 )
Я начал копать в сторону $reduce, но там есть доступ только к текущему элементу '$$this' и к аккумулятору '$$value'. Если бы можно было получить что то типо $$this.index - то норм, но нет( Что делать?
Возможно ли это провернуть средствами mongo db aggregation? И как?))
Ответы (1 шт):
В общем сам нашел решение, напишу тут, для таких же как я, которые не умеют нормально читать доку)
В монге есть такая штука Map-Reduce
С ее помощью можно делать сложные аггрегации данных, с помощью нативного JS. Как я понял, в данном случае мы проигрываем в производительности, ну другого выходу похоже нет. Собственно само решение задачи (mongoose.js):
const o: any = {}
// Данная функция группирует документы по полю name и складывает поля n в массив
1) o.map = 'function() { emit(this.name, this.n); }'
// Тут мы пробегаемся этой функцией по каждому документу: (values - массив и n элементов)
2) o.reduce = `
function(key, values) {
redVal = { count: values.length - 1, qty: 0, arr: [] };
for (var idx = 0; idx < values.length; idx++ ) {
if ( idx > 0) {
var def = values[idx] - values[idx-1];
redVal.qty = redVal.qty + def;
}
}
return redVal;
}
`
// Финальная функция: (находим среднее значение)
3) o.finalize = `
function(key, redVal) {
redVal.avg = redVal.qty/redVal.count;
return redVal;
}
`
4) o.verbose = true // отображение статистики
5) o.query = '{}'
const model = await Product.mapReduce(o)
Как я понял потом к этой модели можно делать обычные лайтовые запросы или записать данные в новую коллекцию.
Решение не самое красивое, тестовый вариант)
Вот примеры из доки.
Примеры из mongoose.js