2015-07-02 76 views
1

我与MongoDB的工作,需要做搜索Nº操作的查询初始化,在执行和finishd一段时间(每隔一小时或每月...)总结不同状态

我的JSON文件具有以下结构:

{ 
    "_id" : ObjectId("55263d62c63265b9bb138551"), 
    "timestamp" : ISODate("2015-02-12T15:27:48.546Z"),  
    "duration" : 199821 
} 

时间戳场是procces INIT开始和持续时间以毫秒为单位的执行时间。如果我添加时间戳+持续时间=光洁度时间戳

我能团是在一段时间内与该查询操作(10分钟)的数目:

db.test.aggregate([ 
    { "$match" :{ 
    "timestamp":{ "$gte": ISODate("2015-0427T12:00:00.0Z") } 
    }}, 
    { "$group" :{ 
    "_id": { 
     "dayOfMonth":{ "$dayOfMonth": "$timestamp" }, 
     "month":{ "$month": "$timestamp" }, 
     "hour": { "$hour":"$timestamp" }, 
     "time": { 
     "$subtract": [ 
      { "$minute":"$timestamp" }, 
      { "$mod": [{ "$minute": "$timestamp" }, 10] } 
     ] 
     } 
    }, 
    "count":{ "$sum":1 } 
    }}, 
    { "$sort": { "_id.time": 1 } } 
]) 

但我也需要数“在执行“和”完成“。

我试着用MapReduce的,和其他聚集查询,但我不能让一个simmilar结果:

{ 
_id: { 
    "month" : 03, 
    "minute" : 00, 
    "Initialized" : 6, 
    "InExecution" : 10, 
    "Finished": 5 
    } 
_id: { 
    "month" : 03, 
    "minute" : 10, 
    "Initialized" : 4, 
    "InExecution" : 12, 
    "Finished": 4 
    } 
_id: { 
    "month" : 03, 
    "minute" : 20, 
    "Initialized" : 3, 
    "InExecution" : 8, 
    "Finished": 5 
    } 
} 

回答

1

这是一个棘手的问题来了解,但有一个主要问题的“聚合框架”这里主要是你的“活动”根据目前的状态生活在几个不同的时间间隔。

这意味着它始终存在一个“开始”和“完成”间隔以及可能被视为“正在执行”的“几个”间隔。

聚合框架无法真正做到这一点。但是你可以用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(

这就给了你一个干净的起点和终点,同时保持所有可能的结果中列出的统计信息。

0

非常感谢布雷克斯,

为了您的大利益。我一直在研究你的解决方案,并认为好主意。

我发现了一个可能的聚合框架解决方案。

db.getCollection('test').aggregate(
{$match:{ 
      "timestamp":{$exists: true, "$gte": ISODate("2015-03-27T12:00:00.0Z") },    
     } }, 
{ $project: { 
    _id: 1, 
    timestamp : 1, 
    error: {$cond: [{$eq: ["$severidad", "ERROR"]}, 1, 0]}, 

    init: {$cond: [ {$and : [{$eq: [{"$subtract": [ 
            {"$minute":"$timestamp"}, 
            {"$mod": [{"$minute":"$timestamp"}, 10]} 
           ]}, {"$subtract": [ 
            {"$minute":"$timestamp"}, 
            {"$mod": [{"$minute":"$timestamp"}, 10]} 
           ]}]}, {$ne: ["$severidad", "ERROR"]}]}, 1, 0]}, 

    executing: {$cond: [ {$and : [{$gt: [{"$subtract": [ 
            {"$minute":{ $add: [ "$timestamp", "$datos_aplicacion.duracion"]}}, 
            {"$mod": [{"$minute":{ $add: [ "$timestamp", "$datos_aplicacion.duracion"]}}, 10]} 
           ]}, {"$subtract": [ 
            {"$minute":"$timestamp"}, 
            {"$mod": [{"$minute":"$timestamp"}, 10]} 
           ]}]}, {$ne: ["$severidad", "ERROR"]}]}, 1, 0]}, 

    finished: {$cond: [ {$and : [{$eq: [{"$subtract": [ 
            {"$minute":{ $add: [ "$timestamp", "$datos_aplicacion.duracion"]}}, 
            {"$mod": [{"$minute":{ $add: [ "$timestamp", "$datos_aplicacion.duracion"]}}, 10]} 
           ]}, {"$subtract": [ 
            {"$minute":"$timestamp"}, 
            {"$mod": [{"$minute":"$timestamp"}, 10]} 
          ]}]}, {$ne: ["$severidad", "ERROR"]}]}, 1, 0]},          
    }}, 
{$group :{_id: { 

     dayOfMonth:{"$dayOfMonth":"$timestamp"}, month:{"$month":"$timestamp"}, hour:{"$hour":"$timestamp"} , 
     time: { 
       "$subtract": [ 
        {"$minute":"$timestamp"}, 
        {"$mod": [{"$minute":"$timestamp"}, 10]} 
       ] 
      },    
    }, 
    NumError: {$sum:"$error"}, 
    NumInit:{$sum:"$init"}, 
    NumExecuting:{$sum:"$executing"}, 
    NumFinished:{$sum:"$finished"}   

    }}, 
{ $sort : { "_id": 1} }); 

它需要1,2秒,1M记录

问候,

+1

如果你学习我给你应该认识到,这些结果是不一样的答案的内容。 “完成”只是在特定时期开始和完成的工作。对于“执行中”也是如此,因为它只报告该时期的工作正在执行,而后者仅在该时期执行。如果任何一种类型“跨越边界”(并且可能是毫秒级的问题),那么它将不会被拾取。这就是为什么选择mapReduce作为答案的原因,我花时间解释了这些问题。 –