2012-11-09 102 views
3

我有一個文件是這樣的:RavenDB - MapReduce的複雜聚合

order : 1 
    event : { timestamp: 1/1/2012, employeeName: "mick" }, 
    event : { timestamp: 1/1/2012, employeeName: "mick" }, 
    event : { timestamp: 1/2/2012, employeeName: "rick" }, 
    event : { timestamp: 1/3/2012, employeeName: "mick" } 

    order : 2 
    event : { timestamp: 1/2/2012, employeeName: "mick" }, 
    event : { timestamp: 1/2/2012, employeeName: "rick" } 

我想要運行的map-reduce查詢返回的日期每個訂單的員工事件的計數分組結果的列表。

在這種情況下,Mick在1/1的單一訂單上有2個事件。所有其他日子在11月2日和3日的每個訂單上都有一個員工單獨的活動。所以,我需要結果的地圖功能看起來像:

{ orderId: 1, date: 1/1/2012, employee: "mick", orderEventsCount: 2 }, 
{ orderId: 1, date: 1/2/2012, employee: "rick", orderEventsCount: 1 }, 
{ orderId: 2, date: 1/2/2012, employee: "mick", orderEventsCount: 1 }, 
{ orderId: 2, date: 1/2/2012, employee: "rick", orderEventsCount: 1 }, 
{ orderId: 1, date: 1/3/2012, employee: "mick", orderEventsCount: 1 } 

然後我需要一個Reduce函數,將只需要這些結果,並依日期和一個返回每個具有多個事件的員工一天的計數順序:

{ date: 1/1/2012, multipleEventsPerOrdercount: 1 }, 
{ date: 1/2/2012, multipleEventsPerOrdercount: 0 }, 
{ date: 1/3/2012, multipleEventsPerOrdercount: 0 } 

由於米克是有結果的日期在訂單上只有一個返回員工與多個事件的計數單個訂單上一個日期多個事件的唯一的員工。

什麼是最好的方式來寫這張地圖 - 減少在LINQ中使用.NET的Raven查詢?

感謝

+0

這兩個單獨的訂單文件?或者一個有兩個訂單的文檔?此外,日期將永遠不會按照您展示的方式進入文檔。你在課堂中使用DateTime屬性嗎?請出示您的客戶端代碼,或者表明您是否正在進行直接http呼叫。謝謝。 –

+0

是的。這是兩個單獨的訂單文件。這些日期作爲時間戳字段存儲在RavenDB JSON文檔中,例如, 「2012-11-08T02:32:13.5549981」並反序列化爲實體類對象中的日期字段。我不認爲客戶端代碼在這裏是相關的,這純粹是一個後端計算問題。我只需要一個使用RavenDB索引的純粹的map-reduce解決方案,使用.NET中的LINQ –

回答

1

假設你的類看起來是這樣的:

public class Order 
{ 
    public string Id { get; set; } 
    public List<Event> Events { get; set; } 
} 

public class Event 
{ 
    public DateTime Timestamp { get; set; } 
    public string EmployeeName { get; set; } 
} 

那麼你所要求的指標應該是這樣的:

public class Orders_EventCountsByDate : 
    AbstractIndexCreationTask<Order, Orders_EventCountsByDate.Result> 
{ 
    public class Result 
    { 
    public DateTime Date { get; set; } 
    public double Count { get; set; } 
    } 

    public Orders_EventCountsByDate() 
    { 
    Map = orders => from order in orders 
        from evt in order.Events 
        let subtotal = order.Events.Count(x => x.EmployeeName == evt.EmployeeName && x.Timestamp == evt.Timestamp) 
        select new 
        { 
         evt.Timestamp.Date, 
         Count = subtotal > 1 ? (1.0/subtotal) : 0 
        }; 

    Reduce = results => from result in results 
         group result by result.Date 
         into g 
         select new 
         { 
          Date = g.Key, 
          Count = g.Sum(x => x.Count) 
         }; 
    } 
} 

你就可以使用它是這樣的:

var counts = session.Query<Orders_EventCountsByDate.Result, 
          Orders_EventCountsByDate>(); 

這裏的訣竅是您在地圖上決定您希望每個事件對計數有多大貢獻。如果只有一個事件,則您貢獻零。當有多個事件時,每個事件貢獻一小部分。這些分數稍後總結在減少,讓你接近整數。雙浮點數學應該讓你回到整數,但爲了安全起見,你仍然可能想在你的客戶端代碼中四捨五入到最接近的整數。

這也假設所有事件都在同一時區,並且您不關心夏令時更改,或者時間以UTC。如果兩者都不是,那麼您應該使用DateTimeOffset,並且在決定每個員工的每日概念時,您需要考慮更多事項。

+0

對不起,我只是重新閱讀你的問題,並意識到你並沒有尋找一個簡單的按日期計算,而是隻計算一個以上的員工在同一天訂購。我很快就會有一個更新的答案。 –

+0

好吧,我已經更新了答案以適合您的問題。正如你所看到的,這涉及到一些數學,這就是爲什麼答案難以捉摸的原因。 –