2015-01-17 6 views
4

Я разрабатываю простое финансовое приложение для отслеживания доходов и результатов.Как рассчитать общую сумму с использованием совокупности

Для простоты, давайте предположим, что это лишь некоторые из моих документов:

{ "_id" : ObjectId("54adc0659413535e02fba115"), "description" : "test1", "amount" : 100, "dateEntry" : ISODate("2015-01-07T23:00:00Z") } 
{ "_id" : ObjectId("54adc21a0d150c760270f99c"), "description" : "test2", "amount" : 50, "dateEntry" : ISODate("2015-01-06T23:00:00Z") } 
{ "_id" : ObjectId("54b05da766341e4802b785c0"), "description" : "test3", "amount" : 11, "dateEntry" : ISODate("2015-01-09T23:00:00Z") } 
{ "_id" : ObjectId("54b05db066341e4802b785c1"), "description" : "test4", "amount" : 2, "dateEntry" : ISODate("2015-01-09T23:00:00Z") } 
{ "_id" : ObjectId("54b05dbb66341e4802b785c2"), "description" : "test5", "amount" : 12, "dateEntry" : ISODate("2015-01-09T23:00:00Z") } 
{ "_id" : ObjectId("54b05f4ee0933a5c02398d55"), "description" : "test6", "amount" : 4, "dateEntry" : ISODate("2015-01-09T23:00:00Z") } 

То, что я хотел бы теперь нарисовать «баланс» график, на основе таких данных:

[ 
    { day:'2015-01-06', amount:50}, 
    { day:'2015-01-07', amount:150}, 
    { day:'2015-01-09', amount:179}, 
... 
] 

Другими словами, мне нужно сгруппировать все мои транзакции днем, и для каждого дня мне нужно суммировать все мои предыдущие транзакции (с начала мира).

Я уже знаю, как группа в день:

$group: { 
    _id: { 
     y: {$year:"$dateEntry"}, 
     m: {$month:"$dateEntry"}, 
     d: {$dayOfMonth:"$dateEntry"} 
    }, 
    sum: ??? 
} 

Но я не знаю, как вернуться назад и подвести все суммы. Представьте себе, что мне нужно показать ежемесячный отчет о балансе: должен ли я запускать 31 запрос, по одному на каждый день, суммируя всю сумму транзакции, за исключением следующих дней? Конечно, я могу, но не думаю, что это лучшее решение.

Заранее благодарен!

+0

Я не знаю, как именно написать запрос, Нил. Если бы я просто сделал {$ sum: "$ amount"}, то только мои текущие групповые транзакции были бы включены в мой результат, и я не хочу этого делать. Пожалуйста, внимательно прочитайте вопрос;) –

+0

Извините. Может быть, я не могу объяснить, что я точно хочу, серьезно. Мне не нужна простая сумма всех сумм за каждый день. Я хочу суммировать все суммы ВСЕХ дней до каждого дня. В моем примере 2015-01-07 будет представлять собой сумму 6 января и 7 января, 2015-01-09 будет равна сумме 6,7,9 янв. И т. Д. –

+0

Почему мой вопрос вниз проголосовали? Я думал, что это будет интересно ... –

ответ

3

На самом деле больше подходит для mapReduce, чем структура агрегации, по крайней мере, при решении начальных задач. В структуре агрегации нет понятия ценности предыдущего документа или предыдущего «сгруппированного» значения документа, поэтому он не может этого сделать.

С другой стороны, mapReduce имеет «глобальную область видимости», которую можно разделить между этапами и документами по мере их обработки. Это даст вам «текущую сумму» для текущего баланса в конце дня, который вам нужен.

db.collection.mapReduce(
    function() { 
    var date = new Date(this.dateEntry.valueOf() - 
     (this.dateEntry.valueOf() % (1000 * 60 * 60 * 24)) 
    ); 

    emit(date, this.amount); 
    }, 
    function(key,values) { 
     return Array.sum(values); 
    }, 
    { 
     "scope": { "total": 0 }, 
     "finalize": function(key,value) { 
      total += value; 
      return total; 
     }, 
     "out": { "inline": 1 } 
    } 
)  

Это будет суммировать по дате группировки, а затем в разделе «финализировать» оно суммирует сумму с каждого дня.

"results" : [ 
      { 
        "_id" : ISODate("2015-01-06T00:00:00Z"), 
        "value" : 50 
      }, 
      { 
        "_id" : ISODate("2015-01-07T00:00:00Z"), 
        "value" : 150 
      }, 
      { 
        "_id" : ISODate("2015-01-09T00:00:00Z"), 
        "value" : 179 
      } 
    ], 

В долгосрочной перспективе вы бы лучше иметь отдельную коллекцию с записью для каждого дня изменить баланс с помощью $inc в обновлении. Просто также сделать $incupsert в начале каждого дня, чтобы создать новый документ, несущий вперед баланс за предыдущий день:

// increase balance 
db.daily(
    { "dateEntry": currentDate }, 
    { "$inc": { "balance": amount } }, 
    { "upsert": true } 
); 

// decrease balance 
db.daily(
    { "dateEntry": currentDate }, 
    { "$inc": { "balance": -amount } }, 
    { "upsert": true } 
); 

// Each day 
var lastDay = db.daily.findOne({ "dateEntry": lastDate }); 
db.daily(
    { "dateEntry": currentDate }, 
    { "$inc": { "balance": lastDay.balance } }, 
    { "upsert": true } 
); 
+0

Большое спасибо Нейлу! :) –

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