这是一个棘手的问题来了解,但有一个主要问题的“聚合框架”这里主要是你的“活动”根据目前的状态生活在几个不同的时间间隔。
这意味着它始终存在一个“开始”和“完成”间隔以及可能被视为“正在执行”的“几个”间隔。
聚合框架无法真正做到这一点。但是你可以用MapReduce的做到这一点:
db.test.mapReduce(
function() {
// Work out time values
var finished = this.timestamp.valueOf() + this.duration,
finishedInterval = finished -
(finished % (1000 * 60 * 10)),
interval = this.timestamp.valueOf() -
(this.timestamp.valueOf() % (1000 * 60 * 10));
// Emit initialized
emit(
{
"year": new Date(interval).getUTCFullYear(),
"month": new Date(interval).getUTCMonth()+1,
"day": new Date(interval).getUTCDate(),
"hour": new Date(interval).getUTCHours(),
"minute": new Date(interval).getUTCMinutes()
},
{
"Initialized": 1,
"InExecution": 0,
"Finshed": 0
}
);
// Emit finished
emit(
{
"year": new Date(finishedInterval).getUTCFullYear(),
"month": new Date(finishedInterval).getUTCMonth()+1,
"day": new Date(finishedInterval).getUTCDate(),
"hour": new Date(finishedInterval).getUTCHours(),
"minute": new Date(finsihedInterval).getUTCMinutes()
},
{
"Initialized": 0,
"InExecution": 0,
"Finshed": 1
}
);
// Emit In execution for every 10 minute interval until finished
if ((interval + (1000 * 60 * 10)) < finishedInterval) {
for (var x = interval; x<finishedInterval; x+= (1000 * 60 * 10)) {
emit(
{
"year": new Date(x).getUTCFullYear(),
"month": new Date(x).getUTCMonth()+1,
"day": new Date(x).getUTCDate(),
"hour": new Date(x).getUTCHours(),
"minute": new Date(x).getUTCMinutes()
},
{
"Initialized": 0,
"InExecution": 1,
"Finshed": 0
}
);
}
}
},
function(key,values) {
var result = { "Initialized": 0, "InExecution": 0, "Finshed": 0 };
values.forEach(function(value) {
Object.keys(value).forEach(function(key) {
result[key] += value[key];
});
});
return result;
},
{
"out": { "inline": 1 },
"query": { "timestamp": { "$gte": new Date("2015-04-27T12:00:00Z") } }
}
)
正如你所看到的,大部分工作在映射完成。这基本上可以确定任务“开始”和“结束”的时间间隔,并为此发出适当的数据。
然后,当然通过在任务的“开始”间隔工作,每10分钟发出一个“执行中”计数,而该值小于任务的“结束”间隔。
减速器只是将每个间隔的所有发射计数加起来并加起来。所以这是一个非常简单的操作。
的地图和减少的逻辑是合理的,但第一个查询时间之前开始在工作“finshing”或“执行”是有可能会查询选择逻辑的一个问题。
为了做到这一点,您需要修复该查询选择以考虑该问题,并且由于您不存储“完成”时间,因此需要计算该时间,这意味着查询中的JavaScript评估与:
{
"out": { "inline": 1 },
"query": {
"$where": function() {
return (this.timestamp >= new Date("2015-04-27T12:00:00Z") ||
new Date(this.timestamp.valueOf() + this.duration) >=
new Date("2015-04-27T12:00:00Z"))
}
}
}
这个选项在当前查询的开始时间或结束时仍然运行。
这不是伟大的,因为它扫描的集合,这样它会更好,包括“finshed”作为您的数据值,以使查询选择更容易:
{
"out": { "inline": 1 },
"query": {
"$or": [
{ "timestamp": { "$gte": new Date("2015-04-27T12:00:00Z") } },
{ "finished": { "$gte": new Date("2015-04-27T12:00:00Z") } }
]
}
}
,可以使用“指数”和要快得多。
作为最后的事情,仍然会有,因为在这两种形式的“成品”这里“之前”的“时间戳”过滤器值发射值意味着,在该时间之前启动的任务。由于相同的原因,在查询条件和逻辑上放置“结束”时间也是一个好主意。
对于这个再次改变的选项框以包括“范围”瓦尔在执行逻辑中使用,并也增加了“查询”的条件:
{
"out": { "inline": 1 },
"query": {
"$or": [
{
"timestamp": {
"$gte": new Date("2015-04-27T12:00:00Z"),
"$lt": new Date("2015-04-28T12:00:00Z")
}
},
{
"finished": {
"$gte": new Date("2015-04-27T12:00:00Z"),
"$lt": new Date("2015-04-28T12:00:00Z")
}
}
]
},
"scope": {
"start": new Date("2015-04-27T12:00:00Z"),
"finsh": new Date("2015-04-28T12:00:00Z")
}
}
然后围绕每个发射添加的条件下,第一对于开始的地方 “间隔” 大于 “开始”:
// Emit initialized
if (interval >= start.valueOf()) {
emit(
而且finsihed其中 “finishedInterval” 小于 “完成”:
// Emit finished
if (finishedInterval <= finish.valueOf()) {
emit(
然后限制上的“执行力”,以及环路:
// Emit In execution for every 10 minute interval until finished
if ((interval + (1000 * 60 * 10)) < finishedInterval) {
for (var x = interval; ((x<finishedInterval) && (x<finish.valueOf())); x+= (1000 * 60 * 10)) {
if (x > start.valueOf()) {
emit(
这就给了你一个干净的起点和终点,同时保持所有可能的结果中列出的统计信息。
如果你学习我给你应该认识到,这些结果是不一样的答案的内容。 “完成”只是在特定时期开始和完成的工作。对于“执行中”也是如此,因为它只报告该时期的工作正在执行,而后者仅在该时期执行。如果任何一种类型“跨越边界”(并且可能是毫秒级的问题),那么它将不会被拾取。这就是为什么选择mapReduce作为答案的原因,我花时间解释了这些问题。 –