【问题标题】:MongoDB Complex aggregate query to sum elements in embedded arrays, with limitMongoDB 复杂聚合查询对嵌入式数组中的元素求和,有限制
【发布时间】:2017-04-22 00:06:02
【问题描述】:

我对 Mongo 比较陌生,并试图弄清楚如何完成此查询。我在 node.js 中使用猫鼬。我想用嵌套的对象数组从这个文档中获取 sell.amount 和 buy.amount 的总和。 trades 数组很大,我想将其限制为第一个(从 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 个交易数组元素..of 很多...

{
    "_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
}

【问题讨论】:

    标签: mongodb-query


    【解决方案1】:

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

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

    $projecttrades 数组上使用 $slice 返回 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 字段添加平均值、最小值和最大值。

    下面的查询会将initialValue 设置为$trades.sell.trades 的第一个元素,然后将$ltsell.trades 进行比较,如果为真,则将$$this 设置为$$value,如果为假,则保留之前的值和$reduce 找到最小元素的所有值和$gt 找到具有相似逻辑的最大元素。此外,添加了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 字段添加平均值、最小值和最大值。

    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
            }
        }
    ])
    

    【讨论】:

    • 谢谢 Veeram,明天我会试一试,看看结果如何。 $project 对我来说有点神秘。几个问题.. $slice 会抢到前 20 名(0 - 19)还是最后 20 名?此外,是否有可能例如从索引(5 - 25)中抓取一个切片..跳过前几个文档..
    • 不客气。 $project 保留/删除管道中的字段。第一个问题的答案:前 20 名。最后 20 名使用 $slice:[array, -20]。第二个问题的答案:是的,$slice:[array, 5, 25]。更多示例和信息docs.mongodb.com/manual/reference/operator/aggregation/slice & docs.mongodb.com/manual/reference/operator/aggregation/proje‌​ct
    • Great Veeram...一切都按预期工作,这也是我开始对数据进行更多查询的一个很好的模板。非常感谢!
    • 嗨,Veeram,您似乎知识渊博。我想知道您是否知道我还可以做一些事情,例如返回平均、最小和最大金额,我在其中求和金额。我注意到 mongo 算术运算符似乎不允许直接这样做。
    • 是的,这是可能的。我已将其添加为更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 2021-08-13
    • 2020-03-02
    • 1970-01-01
    • 2021-03-09
    • 2018-09-10
    • 1970-01-01
    相关资源
    最近更新 更多