嵌套数组中带有聚合的多行的总和

时间:2017-05-24 11:12:45

标签: mongodb mongodb-query aggregation-framework

我收到了预订,其中有几张发票,每张发票都有几张发票线。

我希望我的汇总选择加入发票的预订,然后只给我所有invoiceLines.amountTotal的总额。

示例预订表:

{ 
    "_id" : "0PDLR", 
    "checkin" : 1488326400, 
    "checkout" : 1498780800, 
}

示例发票表:

{ 
    "_id" : 1, 
    "bookingId" : "0PDLR", 
    "invoiceLines" : [
        {
            "lineText" : "Rent Price", 
            "amountTotal" : 3000
        }, 
        {
            "lineText" : "Discount", 
            "amountTotal" : -500
        }, 
    ] 
}   
{ 
    "_id" : 2, 
    "bookingId" : "0PDLR", 
    "invoiceLines" : [
        {
            "lineText" : "Final cleaning", 
            "amountTotal" : 200
        }, 
    ] 
}   
{ 
    "_id" : 3, 
    "bookingId" : "0PDLR", 
    "invoiceLines" : [
        {
            "lineText" : "Taxi to Airport", 
            "amountTotal" : 300
        }, 
        {
            "lineText" : "Reservation fee paid already", 
            "amountTotal" : -500
        }
    ] 
}   

这是我想得到的结果:

booking Result: [
    {
        "_id": "0PDLR",
        "checkin": 1488326400,
        "checkout": 1498780800,
        "sum": 2500
    }
]

这是我目前的汇总管道:

    bookingTable.aggregate([
        { "$match": myMatch },
        {                                                                 
            $lookup: {                                                    
                from: "invoice",                                          
                localField: "_id",                                        
                foreignField: "bookingId",                                
                as: "invoice"                                             
            }                                                             
        },                                                                
        {                                                                  
            $project:{                                                     
                "_id" : 1,                                                
                "checkin" : 1,                                            
                "checkout" : 1,                                           
                "invoiceLines" : "$invoice.invoiceLines"                  
            }                                                              
        }                                                                  
    ])

其中一个原因只是一个接一个地返回所有发票行的预订,
所以我将不得不遵循惯例:

    var invoiceTotal = 0;
    for (var i=0; i<selectBooking[b].invoiceLines.length; i++) {
        for (var x=0; x<selectBooking[b].invoiceLines.length; x++) {    
            for (var y=0; y<selectBooking[b].invoiceLines[x].length; y++) { 
                invoiceTotal += selectBooking[b].invoiceLines[x][y].amountTotal
            }
        }
    }

所以我的问题是,我可以在MongoDB中这样做吗?

有没有办法让它计算所有invoiceLines.amountTotal然后只返回$ project中的总和?

2 个答案:

答案 0 :(得分:1)

这至少需要MongoDB 3.2,但您可以使用$map$sum

执行此操作
 bookingTable.aggregate([
   { "$match": myMatch },
   { "$lookup": {
     "from": "invoice",
     "localField": "_id",
     "foreignField": "bookingId",
     "as": "invoice"
   }},
   { "$project": {
     "checkin" : 1,
     "checkout" : 1,
     "bookingTotal": {
       "$sum": {
         "$map": {
           "input": "$invoice",
           "as": "iv",
           "in": {
             "$sum": {
               "$map": {
                 "input": "$$iv.invoiceLines",
                 "as": "il",
                 "in": "$$il.amountTotal"
               }
             }
           }
         }
       }
     }
   }}
])

这实际上只是迭代每个数组,并将结果从"amountTotal"减少到每一行的单个值。

使用3.4,您可以使用$reduce

来减少这一点
 bookingTable.aggregate([
   { "$match": myMatch },
   { "$lookup": {
     "from": "invoice",
     "localField": "_id",
     "foreignField": "bookingId",
     "as": "invoice"
   }},
   { "$project": {
     "checkin" : 1,
     "checkout" : 1,
     "bookingTotal": {
       "$reduce": {
         "input": "$invoice",
         "initialValue": 0,
         "in": {
           "$sum": [
             { "$reduce": {
               "input": "$$this.invoiceLines",
               "initialValue": 0,
               "in": { "$sum": [ "$$this.amountTotal", "$$value" ] }
             }},
             "$$value"
           ]
         }
       }
     }
   }}
])

答案 1 :(得分:1)

您可以在3.4版本中使用$reduce尝试$concatArrays

amountTotal视为[[3000, -500], [200], [300, -500]]。内部$reduceamountTotal转换为[3000, -500, 200, 300, -500],后跟外部$reduce以对金额求和。

 { "$addFields": {
     "invoiceTotal": 
             {
           $reduce: {
              input: {
               $reduce: {
                  input: "$invoice.invoiceLines",
                  initialValue: [],
                  in: { $concatArrays : ["$$value", "$$this.amountTotal"] }
               }
             },
              initialValue: 0,
              in: { $add : ["$$value", "$$this"] } 
           }
        }
   }}