2015-12-06 3 views
1

У меня есть следующие коллекции с c массива внутри каждого документаСумма элементов массива в агрегации MongoDB

{ 
    { 
    id: 1, 
    k: 2.2, 
    type: "dog", 
    c: [ {parentId:1, p:2.2}, {parentId:1, p:1.4} ] 
    }, 

    { 
    id: 2, 
    k: 4.3, 
    type:"cat", 
    c: [ {parentId:2, p:5.2}, {parentId:2, p:4.5} ] 
    } 
} 

parentId внутри каждого поддокументе в c это идентификатор, содержащий документ.

Я хочу сгруппировать все документы по type и в каждой группе узнать сумму k и сумму всех p во всех массивах группы.

В настоящее время я делаю суммирование k на групповом этапе, но суммирование p в массиве результатов в приложении. Я хочу сделать суммирование p в БД!

Это то, что я в настоящее время:

db.myCol.aggregate([ 

{ 
    $group: { 
    _id: { type: '$type'}, 
    k: {$sum: '$k'}, // sum k values, very easy! 
    // p: {$sum: '$c.0.p'} <==== Does not work, too 
    c: {$addToSet: '$c'} // add to each group all c arrays of group's members 
    } 
} 
], function(err, res) { 
    // go over c-arrays and sum p values 
    var accP = 0; // accumulator for p values 
    for (var i=0; i<res.length; i++) { 
    var c = res[i].c; 
    for (var j=0;j<c.length; j++) { 
     var c2 = c[j]; 
     for (var k=0; k<c2.length; k++) { // finally got to objects c array 
      accP += c2[k].p; 
     } 
    } 
    res[i].c = accP; // replace array with accumulated p value 
    } 
}); 

ответ

3

Вы должны сначала $group ваши документы по «типу», используйте оператор $sum аккумулятор вернуть сумму «к» и использовать $push, которая возвращает 2D-массив «c». Теперь вам нужно два этапа "$unwind", где вы денормируете 2D-массив «c». Ваш последний этап в трубопроводе другой $group этап, на котором вы вычислить сумму "р" с использованием "точечной нотации"

db.collection.aggregate([ 
    { '$group': { 
     '_id': '$type', 
     'k': { '$sum': '$k' }, 'c': { '$push': '$c' } 
    } }, 
    { '$unwind': '$c' }, 
    { '$unwind': '$c' }, 
    { '$group': { 
     '_id': '$_id', 
     'k': { '$first': '$k' }, 
     'c': { '$sum': '$c.p' } 
    }} 
]) 

что дает:

{ "_id" : "dog", "k" : 2.2, "c" : 3.6 } 
{ "_id" : "cat", "k" : 4.3, "c" : 9.7 } 

Starting in version 3.2, следующие выражения аккумуляторов, ранее доступные только на этапе $group, теперь также доступны на этапе $project.

Это означает, что мы можем воспользоваться этим и использовать оператор $sum аккумулятора в $project. Конечно, оператор $map возвращает массив «p» для каждого документа.

db.collection.aggregate([ 
    { '$project': { 
     'type': 1, 
     'k': 1, 
     'c': { 
      '$sum': { 
       '$map': { 
        'input': '$c', 
        'as': 'subc', 
        'in': '$$subc.p' 
       } 
      } 
     } 
    }}, 
    { '$group': { 
     '_id': '$type', 
     'k': { '$sum': '$k' }, 
     'c': { '$sum': '$c' } 
    }} 
]) 

Который возвращает:

{ "_id" : "cat", "k" : 4.3, "c" : 9.7 } 
{ "_id" : "dog", "k" : 2.2, "c" : 3.6 } 
Смежные вопросы