我有一个包含以下文档的集合:
{startDate: ISODate("2016-01-02T00:00:00Z"), endDate: ISODate("2016-01-05T00:00:00Z")},
{startDate: ISODate("2016-01-02T00:00:00Z"), endDate: ISODate("2016-01-08T00:00:00Z")},
{startDate: ISODate("2016-01-05T00:00:00Z"), endDate: ISODate("2016-01-08T00:00:00Z")},
{startDate: ISODate("2016-01-05T00:00:00Z"), endDate: ISODate("2016-01-10T00:00:00Z")},
{startDate: ISODate("2016-01-07T00:00:00Z"), endDate: ISODate("2016-01-10T00:00:00Z")}
我想为最低startDate
和最高endDate
之间的每个日期返回一条记录。与每条记录一起,我想返回startDate
和endDate
包含此日期的记录数的计数。
因此,对于我的上述示例,分钟startDate
为1/2/2016
,最大endDate
为1/10/2016
,因此我希望返回这两者之间的所有日期以及计数。请参阅以下所需的输出:
{date: ISODate("2016-01-02T00:00:00Z"), count: 2}
{date: ISODate("2016-01-03T00:00:00Z"), count: 2}
{date: ISODate("2016-01-04T00:00:00Z"), count: 2}
{date: ISODate("2016-01-05T00:00:00Z"), count: 4}
{date: ISODate("2016-01-06T00:00:00Z"), count: 3}
{date: ISODate("2016-01-07T00:00:00Z"), count: 4}
{date: ISODate("2016-01-08T00:00:00Z"), count: 4}
{date: ISODate("2016-01-09T00:00:00Z"), count: 2}
{date: ISODate("2016-01-010T00:00:00Z"), count: 2}
如果这没有意义,请告诉我,我可以尝试更详细地解释。
我可以使用如下循环来完成此操作:
var startDate = ISODate("2016-01-02T00:00:00Z")
var endDate = ISODate("2016-02-10T00:00:00Z")
while(startDate < endDate){
var counts = db.data.find(
{
startDate: {$lte: startDate},
endDate: {$gte: startDate}
}
).count()
print(startDate, counts)
startDate.setDate(startDate.getDate() + 1)
}
但我想知道是否有办法使用聚合框架来做到这一点?我来自一个主要是SQL的背景,循环获取数据通常是一个坏主意。这个规则适用于MongoDB吗?我是否应该关注在这里使用循环并尝试使用聚合框架,或者这是一个有效的解决方案吗?
答案 0 :(得分:1)
这里最好的选择是mapReduce。这是因为您可以在每个文档中的“startDate”和“endDate”之间循环值,并在这些值之间每天(或其他所需的间隔)发出。然后,只需从所有数据中累积每个发出的日期键:
db.collection.mapReduce(
function() {
for( var d = this.startDate.valueOf(); d <= this.endDate.valueOf(); d += 1000 * 60 * 60 * 24 ) {
emit(new Date(d), 1)
}
},
function(key,values) {
return Array.sum(values);
},
{ "out": { "inline": 1 } }
)
这会产生如下结果:
{
"results" : [
{
"_id" : ISODate("2016-01-02T00:00:00Z"),
"value" : 2
},
{
"_id" : ISODate("2016-01-03T00:00:00Z"),
"value" : 2
},
{
"_id" : ISODate("2016-01-04T00:00:00Z"),
"value" : 2
},
{
"_id" : ISODate("2016-01-05T00:00:00Z"),
"value" : 4
},
{
"_id" : ISODate("2016-01-06T00:00:00Z"),
"value" : 3
},
{
"_id" : ISODate("2016-01-07T00:00:00Z"),
"value" : 4
},
{
"_id" : ISODate("2016-01-08T00:00:00Z"),
"value" : 4
},
{
"_id" : ISODate("2016-01-09T00:00:00Z"),
"value" : 2
},
{
"_id" : ISODate("2016-01-10T00:00:00Z"),
"value" : 2
}
],
"timeMillis" : 35,
"counts" : {
"input" : 5,
"emit" : 25,
"reduce" : 9,
"output" : 9
},
"ok" : 1
}
您的日期四舍五入到样本中的某一天,但如果它们不在实际数据中,则只需要应用日期数学就可以实现每个时间间隔的舍入。
答案 1 :(得分:0)
在mongodb聚合框架中,有阶段而不是循环。它是一个管道,它经过每个阶段,直到达到指定的最后阶段。这就是为什么你在使用聚合框架时看到[]的原因。有几个阶段,仅举几例(匹配,组和项目)。看看他们的文档很简单。无论如何,这非常简短。至于你的问题,这是我的主张:
我没试过这个。如果您可以尝试这个并让我知道它是否有效:
首先,您只使用$ match保留日期在您想要的范围内。然后使用$ group阶段。 例如:
db.collection.aggregate{
[
{$match: {
$and : [
{startDate: {$gte:ISODate("2016-01-02T00:00:00Z")},
{endDate: {$lte:ISODate("2016-02-10T00:00:00Z")}
]
},
{$group:
{_id: {startDate:"$startDate",endDate:"$endDate"},
count:{$sum:1}
}
}
]
}
如果您只想使用startDate进行分组,请按照示例替换
_id: {startDate:"$startDate",endDate:"$endDate"
用这个:
_id: "$startDate"
我希望有帮助