2013-03-18 67 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的百分比平均值,只需将统计量除以计数即可。