2014-08-31 6 views
0

Использование конвейера агрегации MongoDB, есть ли способ суммировать значение в необязательном поле массива?Агрегирование данных в дополнительных массивах в MongoDB

Пусть эта коллекция:

db.myCollection.insert([ 
    {_id: 1}, 
    { _id: 2, 
    events: [{_id: 201, value: 10}, {_id:202, value:20}] 
    }]) 

Я хотел бы суммировать events.value поле с помощью трубопровода агрегации, чтобы произвести это:

{_id: 1, totalValue: 0}, 
{_id: 2, totalValue: 30} 

Я не могу использовать {$unwind: "$events"} потому, что бы устраните {_id: 1} с выхода, поэтому я попытался нажать значения на массив и использовать $cond для создания единственного [0], если элемент отсутствует:

db.myCollection.aggregate([ 
    {$group: {_id:"$_id", values: {$push: "$events.value"}}}, 
    {$project: {_id:1, values: {$cond: { 
    if: {$gt: [{$size: "$values"}, 0]}, 
    then: "$values", 
    else: [[0]] 
    }}}} ]) 

Это создает следующий вывод:

{ "_id" : 2, "values" : [ [ 10, 20 ] ] } 
{ "_id" : 1, "values" : [ [ 0 ] ] } 

Теперь я могу использовать $unwind на значения, но я не могу суммировать значения.

Использование $group с $sum

db.myCollection.aggregate([ 
    {$group: {_id:"$_id", values: {$push: "$events.value"}}}, 
    {$project: {_id:1, values: {$cond: { 
    if: {$gt: [{$size: "$values"}, 0]}, 
    then: "$values", 
    else: [[0]] 
    }}}}, 
    {$unwind: "$values"}, 
    {$group: {_id:"$_id", totalValue: {$sum: "$values"}}} ]) 

производит это:

{ "_id" : 1, "totalValue" : 0 } 
{ "_id" : 2, "totalValue" : 0 } 

и используя $project с $add выдает ошибку:

db.myCollection.aggregate([ 
    {$group: {_id:"$_id", values: {$push: "$events.value"}}}, 
    {$project: {_id:1, values: {$cond: { 
    if: {$gt: [{$size: "$values"}, 0]}, 
    then: "$values", 
    else: [[0]] 
    }}}}, 
    {$project: {_id:1, totalValue: {$add: "$values"}}} ]) 

результаты в виде исключения:

exception: $add only supports numeric or date types, not Array 

ответ

2

Так что ваши документы может или не содержать элемент массива для того, что вы хотите суммировать. Поэтому перед выполнением любой операции $unwind вы применяете оператор $ifNull с номером $project.Это занимает поле для тестирования в качестве аргумента, и другое значение, которое возвращается, если поле отсутствует или вычисляется в null, в противном случае значение поля, которое существует, возвращается:

db.myCollection.aggregate([ 
    { "$project": { 
     "events": { "$ifNull": [ "$events", [{ "value": 0 }] ] } 
    }}, 
    { "$unwind": "$events" }, 
    { "$group": { 
     "_id": "$_id", 
     "totalValue": { "$sum": "$events.value" } 
    }} 
]) 

Гораздо упрощенной форме поскольку содержимое не удаляется из-за отсутствия массива и заменяется значением по умолчанию, которое работает на вас.

0

мне не хватает $unwind заявление:

После один $unwind:

db.myCollection.aggregate([ 
{$group: {_id:"$_id", values: {$push: "$events.value"}}}, 
{$project: {_id:1, values: {$cond: { 
    if: {$gt: [{$size: "$values"}, 0]}, 
    then: "$values", 
    else: [[0]] 
    }}}}, 
{$unwind: "$values"} ]) 

я получаю это:

{ "_id" : 2, "values" : [ 10, 20 ] } 
{ "_id" : 1, "values" : [ 0 ] } 

Но если добавить второй $unwind:

db.myCollection.aggregate([ 
{$group: {_id:"$_id", values: {$push: "$events.value"}}}, 
{$project: {_id:1, values: {$cond: { 
    if: {$gt: [{$size: "$values"}, 0]}, 
    then: "$values", 
    else: [[0]] 
    }}}}, 
{$unwind: "$values"}, 
{$unwind: "$values"}]) 

я получаю это:

{ "_id" : 2, "values" : 10 } 
{ "_id" : 2, "values" : 20 } 
{ "_id" : 1, "values" : 0 } 

Теперь я могу добавить $group пункт:

db.myCollection.aggregate([ 
{$group: {_id:"$_id", values: {$push: "$events.value"}}}, 
{$project: {_id:1, values: {$cond: { 
    if: {$gt: [{$size: "$values"}, 0]}, 
    then: "$values", 
    else: [[0]] 
    }}}}, 
{$unwind: "$values"}, 
{$unwind: "$values"}, 
{$group: {_id:"$_id", totalValue: {$sum: "$values"}}}]) 

получения желаемого результата:

{ "_id" : 1, "totalValue" : 0 } 
{ "_id" : 2, "totalValue" : 30 } 
+0

Получил эту работу, но ответ кажется слишком сложным. Какие-нибудь более простые решения? –

+0

Сохраните сумму в документе и обновите ее при обновлении массива. – wdberkeley

+0

Спасибо, к сожалению, не вариант здесь, поскольку я запрашиваю данные, созданные сторонним приложением. –

Смежные вопросы