我是RavenDB的新手,我正在努力寻找以下解决方案:
我有一个名为ServiceCalls的集合,如下所示:
public class ServiceCall
{
public int ID { get; set; }
public string IncidentNumber { get; set; }
public string Category { get; set; }
public string SubCategory { get; set; }
public DateTime ReportedDateTime { get; set; }
public string Block { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
}
我有一个名为ServiceCalls / CallsByCategory的索引,如下所示:
Map = docs => from doc in docs
select new
{
Category = doc.Category,
CategoryCount = 1,
ServiceCalls = doc,
};
Reduce = results => from result in results
group result by result.Category into g
select new
{
Category = g.Key,
CategoryCount = g.Count(),
ServiceCalls = g.Select(i => i.ServiceCalls)
};
所以输出是:
public class ServiceCallsByCategory
{
public string Category { get; set; }
public int CategoryCount { get; set; }
public IEnumerable<ServiceCall> ServiceCalls { get; set; }
}
使用此查询一切正常
var q = from i in session.Query<ServiceCallsByCategory>("ServiceCalls/CallsByCategory") select i
我绝对丢失的地方是编写一个允许我通过ReportedDateTime查询的索引。可以让我这样做的东西:
var q = from i in session.Query<ServiceCallsByCategory>("ServiceCalls/CallsByCategory")
where i.ServiceCalls.Any(x=>x.ReportedDateTime >= new DateTime(2012,10,1))
select i
任何指导都会受到青睐。
答案 0 :(得分:4)
一些事情,
您的reduce子句中不能有.Count()
方法。如果你仔细观察,你会发现你的计数错了。从构建2151开始,这实际上会引发异常。相反,您需要CategoryCount = g.Sum(x => x.CategoryCount)
您始终希望地图的结构与reduce的结构相匹配。如果您要构建一个事物列表,那么您应该映射每个事物的单个元素数组,并在reduce步骤中使用.SelectMany()
。你现在拥有它的方式只能起作用,因为它可能会在某个时刻得到修复。
通过将结果构建为ServiceCalls列表,您将整个文档复制到索引存储中。这不仅效率低下,而且没有必要。你最好保留一个id列表。 Raven有一个.Include()
方法,如果需要检索完整文档,可以使用它。这里的主要优点是,即使您的索引结果仍然陈旧,您也可以保证获得每个项目的最新数据。
将所有三个放在一起,正确的索引是:
public class ServiceCallsByCategory
{
public string Category { get; set; }
public int CategoryCount { get; set; }
public int[] ServiceCallIds { get; set; }
}
public class ServiceCalls_CallsByCategory : AbstractIndexCreationTask<ServiceCall, ServiceCallsByCategory>
{
public ServiceCalls_CallsByCategory()
{
Map = docs => from doc in docs
select new {
Category = doc.Category,
CategoryCount = 1,
ServiceCallIds = new[] { doc.ID },
};
Reduce = results => from result in results
group result by result.Category
into g
select new {
Category = g.Key,
CategoryCount = g.Sum(x => x.CategoryCount),
ServiceCallIds = g.SelectMany(i => i.ServiceCallIds)
};
}
}
使用includes查询它看起来像这样:
var q = session.Query<ServiceCallsByCategory, ServiceCalls_CallsByCategory>()
.Include<ServiceCallsByCategory, ServiceCall>(x => x.ServiceCallIds);
当您需要文档时,仍然使用session.Load<ServiceCall>(id)
加载它,但Raven不必回程到服务器就可以获得它。
现在 - 这并未解决有关如何按日期过滤结果的问题。为此,你真的需要考虑你想要完成的事情。以上所有内容都假设您确实希望一次为每个类别显示每个服务调用。大多数情况下,这并不实用,因为您想要对结果进行分页。你可能不想甚至使用我上面描述的内容。我在这里做了一些大的假设,但大部分时间都会按类别过滤,而不是按类别过滤。
假设你有一个只计算类别的索引(上面没有服务调用列表的索引)。您可以使用它来显示概览屏幕。但是,在您单击一个并钻入详细信息屏幕之前,您不会对每个类别中的文档感兴趣。此时,您知道您所在的类别,并且您可以按此过滤并缩小到没有静态索引的日期范围:
var q = session.Query<ServiceCall>().Where(x=> x.Category == category && x.ReportedDateTime >= datetime)
如果我错了,您确实需要显示所有类别的所有文档,按类别分组,并按日期过滤,那么您将不得不采用类似我所描述的高级技术in this other StackOverflow answer 。如果这是真的你需要什么,请在评论中告诉我,我会看看我是否可以为你写。您将需要Raven 2.0才能使其正常工作。
另外 - 要非常小心为ReportedDateTime存储的内容。如果您要进行任何比较,您需要了解日历时间和瞬时时间之间的区别。日历时间有一些怪癖,如夏令时转换,时区差异等。瞬间记录发生事件的时刻,无论是谁在询问。您可能希望使用瞬时时间,这意味着使用UTC DateTime
或切换到DateTimeOffset
,这样您就可以在不丢失本地上下文值的情况下表示即时时间。
<强>更新强>
我尝试构建一个索引,该索引将使用我所描述的技术,让您在类别组中获得所有结果,但仍按日期过滤。不幸的是,这是不可能的。您必须将所有ServiceCall组合在原始文档中并在Map中表达。如果你必须首先减少它,它根本不会以相同的方式工作。因此,一旦您处于特定类别,您应该考虑对ServiceCalls进行简单查询。
答案 1 :(得分:0)
您可以将ReportedDateTime添加到地图并在Reduce中聚合吗?如果您只关心每个类别的最大值,那么这样就足够了。
Map = docs => from doc in docs
select new
{
Category = doc.Category,
CategoryCount = 1,
ServiceCalls = doc,
ReportedDateTime
};
Reduce = results => from result in results
group result by result.Category into g
select new
{
Category = g.Key,
CategoryCount = g.Sum(x => x.CategoryCount),
ServiceCalls = g.Select(i => i.ServiceCalls)
ReportedDateTime = g.Max(rdt => rdt.ReportedDateTime)
};
然后,您可以根据聚合的ReportedDateTime:
进行查询var q = from i in session.Query<ServiceCallsByCategory>("ServiceCalls/CallsByCategory")
where i.ReportedDateTime >= new DateTime(2012,10,1)
select i