2015-07-02 36 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作爲答案的原因,我花時間解釋了這些問題。 –