2013-03-18 52 views
1

我們使用RavenDB(2261)作爲基於隊列的視頻上傳系統的後端,並且我們被要求提供關於各種指標的「實時」SLA報告,以處理上傳系統。你將如何處理基於RavenDB文檔數據的聚合/報告方案?

該文件的格式如下:

{ 
    "ClipGuid": "-1234-abcd-efef-123412341234", 
    "CustomerId": "ABC123", 
    "Title": "Shakespeare in Love", 
    "DurationInSeconds": 82, 
    "StateChanges": [ 
    { 
     "OldState": "DoesNotExist", 
     "NewState": "ReceivedFromUpload", 
     "ChangedAt": "2013-03-15T15:38:38.7050002Z" 
    }, 
    { 
     "OldState": "ReceivedFromUpload", 
     "NewState": "Validating", 
     "ChangedAt": "2013-03-15T15:38:38.8453975Z" 
    }, 
    { 
     "OldState": "Validating", 
     "NewState": "AwaitingSubmission", 
     "ChangedAt": "2013-03-15T15:38:39.9529762Z" 
    }, 
    { 
     "OldState": "AwaitingSubmission", 
     "NewState": "Submitted", 
     "ChangedAt": "2013-03-15T15:38:43.4785084Z" 
    }, 
    { 
     "OldState": "Submitted", 
     "NewState": "Playable", 
     "ChangedAt": "2013-03-15T15:41:39.5523223Z" 
    } 
    ], 
} 

在每個ClipInfo中的記錄,有夾子從加工環節到另一個的一部分通過每次加StateChanges的集合。我們需要將這些StateChanges減少到兩個特定時間段 - 我們需要知道剪輯從DoesNotExist更改爲AwaitingSubmission需要多長時間,以及從DoesNotExist到Playable需要多長時間。然後,我們需要一羣這些持續時間的日期/時間,因此,我們可以得出一個簡單的SLA報告,看起來像這樣:

Mockup video upload SLA

必要的謂詞可以表示爲LINQ語句,但是當我試圖確定這種Raven查詢中的複雜邏輯我似乎只能得到空的結果(或許多DateTime.MinValue結果)

我意識到像Raven這樣的文檔數據庫不適合報告 - 我們很樂意探索複製到SQL或其他類型的緩存機制 - 但目前我只是看不到任何提取數據的方式,除了進行多個查詢來檢索整個小組然後在.NET中執行計算。

有什麼建議嗎?

感謝,

迪倫

+0

這可以通過map/reduce索引完成。我正在爲您解決問題。當你說「今天」 - 你的意思是「從午夜到UTC午夜」還是別的什麼?當你說「本週」時,你的意思是「週日到週六UTC」或其他什麼? – 2013-03-18 21:37:27

+0

另外 - 我應該將哪個狀態作爲分組的目的?第一個與'OldState ==「DoesNotExist」' - 或其他? – 2013-03-18 21:40:20

回答

0

我已經這對於您可能需要調整一些假設:

  • 您在UTC時區嚴格操作 - 你的「天」是午夜UTC的午夜。
  • 你本週星期天到星期六
  • 您希望將日期爲第一狀態日期報道(一標有「DoesNotExist」作爲自己的老態。)

您將需要一個單獨的每日,每週,每月的每個日期範圍的地圖/縮小索引。

除了定義起始日期的方式外,它們幾乎完全相同。如果你想變得富有創造力,你可能會想出一種方法將它們變成一個通用的索引定義 - 但它們總是會在RavenDB中成爲三個單獨的索引。

// This is the resulting class that all of these indexes will return 
public class ClipStats 
{ 
    public int CountClips { get; set; } 
    public int NumPassedWithinTwentyPct { get; set; } 
    public int NumPlayableWithinOneHour { get; set; } 
    public DateTime Starting { get; set; } 
} 

public class ClipStats_ByDay : AbstractIndexCreationTask<ClipInfo, ClipStats> 
{ 
    public ClipStats_ByDay() 
    { 
     Map = clips => from clip in clips 
         let state1 = clip.StateChanges.FirstOrDefault(x => x.OldState == "DoesNotExist") 
         let state2 = clip.StateChanges.FirstOrDefault(x => x.NewState == "AwaitingSubmission") 
         let state3 = clip.StateChanges.FirstOrDefault(x => x.NewState == "Playable") 
         let time1 = state2.ChangedAt - state1.ChangedAt 
         let time2 = state3.ChangedAt - state1.ChangedAt 
         select new 
         { 
          CountClips = 1, 
          NumPassedWithinTwentyPct = time1.TotalSeconds < clip.DurationInSeconds * 0.2 ? 1 : 0, 
          NumPlayableWithinOneHour = time2.TotalHours < 1 ? 1 : 0, 
          Starting = state1.ChangedAt.Date 
         }; 

     Reduce = results => from result in results 
          group result by result.Starting 
          into g 
          select new 
          { 
           CountClips = g.Sum(x => x.CountClips), 
           NumPassedWithinTwentyPct = g.Sum(x => x.NumPassedWithinTwentyPct), 
           NumPlayableWithinOneHour = g.Sum(x => x.NumPlayableWithinOneHour), 
           Starting = g.Key 
          }; 
    } 
} 

public class ClipStats_ByWeek : AbstractIndexCreationTask<ClipInfo, ClipStats> 
{ 
    public ClipStats_ByWeek() 
    { 
     Map = clips => from clip in clips 
         let state1 = clip.StateChanges.FirstOrDefault(x => x.OldState == "DoesNotExist") 
         let state2 = clip.StateChanges.FirstOrDefault(x => x.NewState == "AwaitingSubmission") 
         let state3 = clip.StateChanges.FirstOrDefault(x => x.NewState == "Playable") 
         let time1 = state2.ChangedAt - state1.ChangedAt 
         let time2 = state3.ChangedAt - state1.ChangedAt 
         select new 
         { 
          CountClips = 1, 
          NumPassedWithinTwentyPct = time1.TotalSeconds < clip.DurationInSeconds * 0.2 ? 1 : 0, 
          NumPlayableWithinOneHour = time2.TotalHours < 1 ? 1 : 0, 
          Starting = state1.ChangedAt.Date.AddDays(0 - (int) state1.ChangedAt.Date.DayOfWeek) 
         }; 

     Reduce = results => from result in results 
          group result by result.Starting 
          into g 
          select new 
          { 
           CountClips = g.Sum(x => x.CountClips), 
           NumPassedWithinTwentyPct = g.Sum(x => x.NumPassedWithinTwentyPct), 
           NumPlayableWithinOneHour = g.Sum(x => x.NumPlayableWithinOneHour), 
           Starting = g.Key 
          }; 
    } 
} 

public class ClipStats_ByMonth : AbstractIndexCreationTask<ClipInfo, ClipStats> 
{ 
    public ClipStats_ByMonth() 
    { 
     Map = clips => from clip in clips 
         let state1 = clip.StateChanges.FirstOrDefault(x => x.OldState == "DoesNotExist") 
         let state2 = clip.StateChanges.FirstOrDefault(x => x.NewState == "AwaitingSubmission") 
         let state3 = clip.StateChanges.FirstOrDefault(x => x.NewState == "Playable") 
         let time1 = state2.ChangedAt - state1.ChangedAt 
         let time2 = state3.ChangedAt - state1.ChangedAt 
         select new 
         { 
          CountClips = 1, 
          NumPassedWithinTwentyPct = time1.TotalSeconds < clip.DurationInSeconds * 0.2 ? 1 : 0, 
          NumPlayableWithinOneHour = time2.TotalHours < 1 ? 1 : 0, 
          Starting = state1.ChangedAt.Date.AddDays(1 - state1.ChangedAt.Date.Day) 
         }; 

     Reduce = results => from result in results 
          group result by result.Starting 
          into g 
          select new 
          { 
           CountClips = g.Sum(x => x.CountClips), 
           NumPassedWithinTwentyPct = g.Sum(x => x.NumPassedWithinTwentyPct), 
           NumPlayableWithinOneHour = g.Sum(x => x.NumPlayableWithinOneHour), 
           Starting = g.Key 
          }; 
    } 
} 

然後,當你想查詢...

var now = DateTime.UtcNow; 

var today = now.Date; 
var dailyStats = session.Query<ClipStats, ClipStats_ByDay>() 
         .FirstOrDefault(x => x.Starting == today); 

var startOfWeek = today.AddDays(0 - (int) today.DayOfWeek); 
var weeklyStats = session.Query<ClipStats, ClipStats_ByWeek>() 
         .FirstOrDefault(x => x.Starting == startOfWeek); 

var startOfMonth = today.AddDays(1 - today.Day); 
var monthlyStats = session.Query<ClipStats, ClipStats_ByMonth>() 
          .FirstOrDefault(x => x.Starting == startOfMonth); 

在結果中,你將有總數。因此,如果您想要SLA的百分比平均值,只需將統計量除以計數即可。

相關問題