MongoDB Complex聚合查询,用于限制嵌入数组中的元素

时间:2017-04-22 00:06:02

标签: mongodb-query

我对Mongo相对较新,并试图弄清楚如何完成此查询。我在node.js中使用mongoose。我想从嵌入的对象数组中获取sell.amount和buy.amount的总和。交易数组很大,我想将它限制为第一个(从0)n个对象。我希望能够得到像这样的东西:

{buy.totalAmount, buy.tradesCount},{sell.totalAmount, sell.tradesCount}, 

作为$ limit

选择的元素数量的总和

我认为我必须接近,但到目前为止,我无法弄清楚如何使这项工作。

我的查询:

tradePair.aggregate(
        {$match: {pair:"STOCK"}},
        {$unwind:"$trades"},
        {$limit: 20},
        {$group : { _id : '$trades.buy', 
            count : {$sum : 1},
            totalAmount: { $sum: '$trades.buy.amount' }
        }},
        function(err,result){
          return result
        }
)

我的数据库文档,只显示了2个交易数组元素......很多......

{
    "_id" : ObjectId("58fa86c81cdd7b2375cdd4cc"),
    "pair" : "STOCK",
    "trades" : [ 
        {
            "sell" : {
                "trades" : 1,
                "total" : 0.13309789,
                "amount" : 24.80139,
                "rate" : 0.00536655
            },
            "buy" : {
                "trades" : 0,
                "total" : 0,
                "amount" : 0,
                "rate" : 0
            },
            "_id" : ObjectId("58fa87290567372b4035d16f"),
            "endTradeId" : 41306,
            "startTradeId" : 41306,
            "dateEnd" : ISODate("2017-04-21T21:37:39.000Z"),
            "dateStart" : ISODate("2017-04-21T21:37:39.000Z")
        }, 
        {
            "sell" : {
                "trades" : 2,
                "total" : 1.23879614,
                "amount" : 230.83659924,
                "rate" : 0.00536655
            },
            "buy" : {
                "trades" : 0,
                "total" : 0,
                "amount" : 0,
                "rate" : 0
            },
            "_id" : ObjectId("58fa87290567372b4035d16e"),
            "endTradeId" : 41305,
            "startTradeId" : 41304,
            "dateEnd" : ISODate("2017-04-21T21:35:28.000Z"),
            "dateStart" : ISODate("2017-04-21T21:35:27.000Z")
        }, 
        ..., 
        ..., 

    ],
    "lastTradeId" : 41306,
    "dateStart" : ISODate("2017-04-21T21:37:39.000Z"),
    "dateEnd" : ISODate("2017-04-21T21:37:39.000Z"),
    "__v" : 0
}

1 个答案:

答案 0 :(得分:1)

您可以在Mongo 3.4中使用以下聚合管道。

以下代码将使用$match阶段来保存匹配的文档。

$project使用$slice数组上的trades返回20个元素,后跟$reduce,后者获取数组值并将每个文档的值相加。

db.tradePair.aggregate([{
        $match: {
            pair: "STOCK"
        }
    },
    {
        $project: {
            trades: {
                $reduce: {
                    "input": {
                        $slice: ["$trades", 20]
                    },
                    initialValue: {
                        buyTotalAmount: 0,
                        buyTradesCount: 0,
                        sellTotalAmount: 0,
                        sellTradesCount: 0
                    },
                    in: {
                        buyTotalAmount: {
                            $add: ["$$value.buyTotalAmount", "$$this.buy.amount"]
                        },
                        buyTradesCount: {
                            $add: ["$$value.buyTradesCount", "$$this.buy.trades"]
                        },
                        sellTotalAmount: {
                            $add: ["$$value.sellTotalAmount", "$$this.sell.amount"]
                        },
                        sellTradesCount: {
                            $add: ["$$value.sellTradesCount", "$$this.sell.trades"]
                        }
                    }
                }
            },
            _id: 0
        }
    }
])

更新

sell.trades字段的相同管道中添加avg,min和max。

以下查询会将initialValue设置为$trades.sell.trades的第一个元素,然后在$lt上进行sell.trades比较,如果为true,则设置为$$this $$value,如果为false,则保留前一个值,$reduce所有值都找到最小元素,$gt找到具有相似逻辑的max元素。此外,添加了count字段以跟踪交易条目的数量。最外面的$let读取内部$reduce的结果,同时将sumsellTradesCount除以count来计算平均值。

db.tradePair.aggregate([{
        $match: {
            pair: "STOCK"
        }
    },
    {
        $project: {
            trades: {
                $let: {
                    vars: {
                        obj: {
                            $reduce: {
                                "input": {
                                    $slice: ["$trades", 20]
                                },
                                initialValue: {
                                    minsellTradesCount: {
                                        $let: {
                                            vars: {
                                                obj: {
                                                    $arrayElemAt: ["$trades", 0]
                                                }
                                            },
                                            in: "$$obj.sell.trades"
                                        }
                                    },
                                    maxsellTradesCount: {
                                        $let: {
                                            vars: {
                                                obj: {
                                                    $arrayElemAt: ["$trades", 0]
                                                }
                                            },
                                            in: "$$obj.sell.trades"
                                        }
                                    },
                                    sumsellTradesCount: 0,
                                    count: 0
                                },
                                in: {
                                    sumsellTradesCount: {
                                        $add: ["$$value.sumsellTradesCount", "$$this.sell.trades"]
                                    },
                                    minsellTradesCount: {
                                        $cond: [{
                                            $lt: ["$$this.sell.trades", "$$value.minsellTradesCount"]
                                        }, "$$this.sell.trades", "$$value.minsellTradesCount"]
                                    },
                                    maxsellTradesCount: {
                                        $cond: [{
                                            $gt: ["$$this.sell.trades", "$$value.minsellTradesCount"]
                                        }, "$$this.sell.trades", "$$value.minsellTradesCount"]
                                    },
                                    count: {
                                        $add: ["$$value.count", 1]
                                    }
                                }
                            }
                        }
                    },
                    in: {
                        minsellTradesCount: "$$obj.minsellTradesCount",
                        maxsellTradesCount: "$$obj.maxsellTradesCount",
                        sumsellTradesCount: "$$obj.sumsellTradesCount",
                        avgsellTradesCount: {
                            $divide: ["$$obj.sumsellTradesCount", "$$obj.count"]
                        }
                    }
                }
            },
            _id: 0
        }
    }
])

更新2:

sell.amount字段的相同管道中添加avg,min和max。

db.tradePair.aggregate([{
        $match: {
            pair: "STOCK"
        }
    },
    {
        $project: {
            trades: {
                $let: {
                    vars: {
                        obj: {
                            $reduce: {
                                "input": {
                                    $slice: ["$trades", 20]
                                },
                                initialValue: {
                                    minsellTotalAmount: {
                                        $let: {
                                            vars: {
                                                obj: {
                                                    $arrayElemAt: ["$trades", 0]
                                                }
                                            },
                                            in: "$$obj.sell.amount"
                                        }
                                    },
                                    maxsellTotalAmount: {
                                        $let: {
                                            vars: {
                                                obj: {
                                                    $arrayElemAt: ["$trades", 0]
                                                }
                                            },
                                            in: "$$obj.sell.amount"
                                        }
                                    },
                                    sumsellTotalAmount: 0,
                                    count: 0
                                },
                                in: {
                                    minsellTotalAmount: {
                                        $cond: [{
                                            $lt: ["$$this.sell.amount", "$$value.minsellTotalAmount"]
                                        }, "$$this.sell.amount", "$$value.minsellTotalAmount"]
                                    },
                                    maxsellTotalAmount: {
                                        $cond: [{
                                            $gt: ["$$this.sell.amount", "$$value.maxsellTotalAmount"]
                                        }, "$$this.sell.amount", "$$value.maxsellTotalAmount"]
                                    },
                                    sumsellTotalAmount: {
                                        $add: ["$$value.sumsellTotalAmount", "$$this.sell.amount"]
                                    },
                                    count: {
                                        $add: ["$$value.count", 1]
                                    }
                                }
                            }
                        }
                    },
                    in: {
                        minsellTotalAmount: "$$obj.minsellTotalAmount",
                        maxsellTotalAmount: "$$obj.maxsellTotalAmount",
                        sumsellTotalAmount: "$$obj.sumsellTotalAmount",
                        avgsellTotalAmount: {
                            $divide: ["$$obj.sumsellTotalAmount", "$$obj.count"]
                        }
                    }
                }
            },
            _id: 0
        }
    }
])