Javascript MongoDB聚合框架-如何执行多个$group查询
我有以下MongoDB聚合查询,它查找指定月份内的所有记录,$按天对记录进行分组,然后返回每天的平均价格。我还想返回整个月的平均价格。我可以通过使用多个$group来实现这一点吗?如果可以,如何实现Javascript MongoDB聚合框架-如何执行多个$group查询,javascript,node.js,mongodb,mongoose,aggregation-framework,Javascript,Node.js,Mongodb,Mongoose,Aggregation Framework,我有以下MongoDB聚合查询,它查找指定月份内的所有记录,$按天对记录进行分组,然后返回每天的平均价格。我还想返回整个月的平均价格。我可以通过使用多个$group来实现这一点吗?如果可以,如何实现 PriceHourly.aggregate([ { $match: { date: { $gt: start, $lt: end } } }, { $group: { _id:
PriceHourly.aggregate([
{ $match: { date: { $gt: start, $lt: end } } },
{ $group: {
_id: "$day",
price: { $avg: '$price' },
system_demand: { $avg: '$system_demand'}
}}
], function(err, results){
results.forEach(function(r) {
r.price = Helpers.round_price(r.price);
r.system_demand = Helpers.round_price(r.system_demand);
});
console.log("Results Length: "+results.length, results);
res.jsonp(results);
}); // PriceHourly();
这是我的模型:
// Model
var PriceHourlySchema = new Schema({
created: {
type: Date,
default: Date.now
},
day: {
type: String,
required: true,
trim: true
},
hour: {
type: String,
required: true,
trim: true
},
price: {
type: Number,
required: true
},
date: {
type: Date,
required: true
}
},
{
autoIndex: true
});
简单的回答是“把你的日期范围扩大到包括一个月内的所有日子有什么不对?”因此,为了得到结果,你只需要改变这些
你能“嵌套”分组阶段吗?是的,您可以向管道添加其他阶段,这就是管道的用途。因此,如果您首先想“平均”每天,然后计算一个月内所有天数的平均值,您可以这样形成:
PriceHourly.aggregate([
{ "$match": {
"date": {
"$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
}
}},
{ "$group": {
"_id": "$day",
"price": { "$avg": "$price" },
"system_demand": { "$avg": "$system_demand" }
}},
{ "$group": {
"_id": null,
"price": { "$avg": "$price" },
"system_demand": { "$avg": "$system_demand" }
}}
])
{
"results" : [
{
"_id" : 1,
"value" : {
"dayAvg" : 105,
"monthAvg" : 105
}
},
{
"_id" : 2,
"value" : {
"dayAvg" : 110,
"monthAvg" : 107.5
}
}
],
}
即使这可能是合理的冗余,因为这可以用一个单独的集团声明来完成
但对这一模式还有更长的评论。实际上,除了获得平均值或模式要包含的内容外,您并没有说明所做工作的大部分目的。所以我想描述一些可能有点不同的东西
假设您有一个集合,其中包括“产品”、“键入”当前价格和“时间戳”,作为“价格”被“更改”的日期。让我们把这个集合称为“价格变化”。因此,每次发生此事件时,都会创建一个新文档
{
"product": "ABC",
"type": 2,
"price": 110,
"timestamp": ISODate("2014-04-01T00:08:38.360Z")
}
这可能在一小时、一天或任何情况下改变很多次
因此,如果您对当月每种产品的“平均”价格感兴趣,您可以这样做:
PriceChange.aggregate([
{ "$match": {
"timestamp": {
"$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
}
}},
{ "$group": {
"_id": "$product",
"price_avg": { "$avg": "$price" }
}}
])
此外,无需任何其他字段,您可以获得每月每天的每种产品的平均价格:
PriceChange.aggregate([
{ "$match": {
"timestamp": {
"$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
}
}},
{ "$group": {
"_id": {
"day": { "$dayOfMonth": "$timestamp" },
"product": "$product"
},
"price_avg": { "$avg": "$price" }
}}
])
或者,您甚至可以获得一整年中每个月的上一次价格:
PriceChange.aggregate([
{ "$match": {
"timestamp": {
"$gte": new Date("2013-01-01"), "$lt": new Date("2014-01-01")
}
}},
{ "$group": {
"_id": {
"date": {
"year": { "$year" : "$timestamp" },
"month": { "$month": "$timestamp" }
},
"product": "$product"
},
"price_last": { "$last": "$price" }
}}
])
因此,您可以使用内置来实现各种结果。这些甚至可以帮助收集这些信息,以便写入新的“预聚合”集合,以用于更快的分析
我想有一种方法可以使用mapReduce将“运行”平均值与所有价格相结合。再次从我的样本来看:
PriceHourly.mapReduce(
function () {
emit( this.timestamp.getDate(), this.price )
},
function (key, values) {
var sum = 0;
values.forEach(function(value) {
sum += value;
});
return ( sum / values.length );
},
{
"query": {
"timestamp": {
"$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
}
},
"out": { "inline": 1 },
"scope": { "running": 0, "counter": 0 },
"finalize": function(key,value) {
running += value;
counter++;
return { "dayAvg": value, "monthAvg": running / counter };
}
}
)
这将返回类似这样的结果:
PriceHourly.aggregate([
{ "$match": {
"date": {
"$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
}
}},
{ "$group": {
"_id": "$day",
"price": { "$avg": "$price" },
"system_demand": { "$avg": "$system_demand" }
}},
{ "$group": {
"_id": null,
"price": { "$avg": "$price" },
"system_demand": { "$avg": "$system_demand" }
}}
])
{
"results" : [
{
"_id" : 1,
"value" : {
"dayAvg" : 105,
"monthAvg" : 105
}
},
{
"_id" : 2,
"value" : {
"dayAvg" : 110,
"monthAvg" : 107.5
}
}
],
}
但是,如果您希望同时看到日和月的离散值,那么如果不运行单独的查询,这是不可能的。Neil,感谢您包含所有这些信息,这非常有帮助。然而,我无法让这两种方法同时返回日平均值和月平均值。第一种方法只返回月平均值,第二个$group似乎覆盖了日平均值。如果我删除第二个$group,每日平均值将按预期返回。第二种方法会导致一个错误声明:{[MongoError:exception:invalid operator'$avg']name:'MongoError',errmsg:'exception:invalid operator\'$avg\'',代码:15999,ok:0}@ac360抱歉,您想要实现什么?您希望在同一结果集中同时显示日平均值和月平均值吗?我甚至不确定那应该是什么样子,从你的问题中当然不清楚。也不确定您所指的是哪个列表上的错误。可能存在键入错误(我发现了一个),因为这些只是作为示例键入的。@ac360确实不确定您想在这里实现什么。我添加了一个具有运行平均值的样本。结果仍然有问题吗?你不能按照你描述的方式来组合结果,将月平均数与日平均数结合起来也没有多大意义,除非你指的是每个累积日的“运行平均数”。否则,两组数据最适合两个查询。在这两种情况下,您的问题都可以从显示您似乎期望的结果数据中获益。