我在 RavenDB 中有客户以及与这些客户关联的订单。目前,当尝试在文档查询中使用 GroupBy 操作时,我在 RavenDB 中遇到错误,指出它无法转换查询。作为解决方法,我一直使用 ToList() 将数据检索到内存中,然后应用 GroupBy、OrderBy 和 Select 来获取每个客户的第一个订单。然而,这种方法效率低下,因为它完全在内存中完成。
现在,我的目标是获取每个客户的最新订单,忽略查找中应用的任何日期过滤器。我尝试通过创建 OrderLookup 索引来实现此目的,但它导致 RavenDB 挂起并启动索引一整天而没有产生任何结果。
public class OrderLookup : AbstractIndexCreationTask<Order, OrderLookup.Results>
{
public class Results
{
public string CustomerId { get; set; }
public DateTimeOffset? MaxCreatedOn { get; set; }
public DateTimeOffset CreatedOn { get; set; }
public bool IsAdvancedAgreement { get; set; }
public bool IsForecastOrder { get; set; }
public short SoftDeleteStatus { get; set; }
public string Id { get; set; }
}
public OrderLookup()
{
Map = orders => from order in orders
select new
{
CustomerId = order.CustomerId.Id,
CreatedOn = order.KeyDates.CreatedOn,
Id = order.Id,
IsForecastOrder = order.IsForecastQuote,
SoftDeleteStatus = order.SoftDeleteStatus,
IsAdvancedAgreement = order.IsAdvancedAgreement
};
Reduce = results => from result in results
orderby result.CreatedOn
let maxCreatedOn = results.OrderByDescending(x => x.CreatedOn)
.GroupBy(x => x.CustomerId)
.Select(g => g.First().CreatedOn)
.First()
select new Results
{
CustomerId = result.CustomerId,
MaxCreatedOn = maxCreatedOn,
CreatedOn = result.CreatedOn,
IsAdvancedAgreement = result.IsAdvancedAgreement,
IsForecastOrder = result.IsForecastOrder,
SoftDeleteStatus = result.SoftDeleteStatus,
Id = result.Id
};
}
}
关于 Map-Reduce 索引,首先要了解的是,它的目的是在添加、修改或删除文档时始终保持聚合值最新。
在您的情况下,您希望索引维护每个客户的最长日期,并参考订单文档。
这是您正在寻找的基本实现:
示例文档类:
public class MyOrder
{
public string Id { get; set; } // document Id
public string OrderId { get; set; }
public string CustomerId { get; set; }
public DateTimeOffset KeyDate { get; set; }
public bool IsAdvancedAgreement { get; set; }
public bool IsForecastQuote { get; set; }
public short SoftDeleteStatus { get; set; }
}
Map-Reduce 索引:
public class MyIndex : AbstractIndexCreationTask<MyOrder, MyIndex.MyIndexEntry>
{
public class MyIndexEntry
{
public string CustomerId { get; set; }
public DateTimeOffset LatestDate { get; set; }
public string LatestOrderId { get; set; }
public string LatestDocumentId { get; set; }
}
public MyIndex()
{
Map = myOrders => from myOrder in myOrders
select new MyIndexEntry
{
CustomerId = myOrder.CustomerId,
LatestDate = myOrder.KeyDate,
LatestOrderId = myOrder.OrderId,
LatestDocumentId = myOrder.Id
};
Reduce = results => from result in results
group result by result.CustomerId into g
let latest = g.OrderByDescending(x => x.LatestDate)
select new MyIndexEntry
{
CustomerId = g.Key,
// The Map-Reduce index will keep the following updated at all times per customer
LatestDate = latest.FirstOrDefault().LatestDate,
LatestOrderId = latest.FirstOrDefault().LatestOrderId,
LatestDocumentId = latest.FirstOrDefault().LatestDocumentId
};
}
}
查询:
using (var session = store.OpenSession())
{
// Query the index for the customer info
MyIndex.MyIndexEntry latestInfoAboutCustomer = session
.Query<MyIndex.MyIndexEntry, MyIndex>()
.Where(x => x.CustomerId == "Customer1")
.FirstOrDefault();
// Load the customer document
// This document will be LATEST order for that customer
MyOrder latestOrderOfCustomer = session.Load<MyOrder>(latestInfoAboutCustomer.LatestDocumentId);
}
注意Map函数的输出结构必须与Reduce函数
相同(两者都有'CustomerId','LatestDate'和'LatestOrderId')