我在这个类的RavenDB中有一个集合......
public class Report
{
public string User { get; set; }
public int Quarter { get; set; }
public int Year { get; set; }
public string ReportData { get; set; }
}
每个用户每年每季度只有一个报告(因此识别密钥为{ User, Quarter, Year }
。我想创建一个函数来保存这些报告列表,覆盖旧报告或插入新报告需要。我想出了这个:
public void Save(IList<Report> reports)
{
session.Query<Report>()
.Join(reports,
x => new { x.User, x.Quarter, x.Year },
y => new { y.User, y.Quarter, y.Year },
(x, y) => new { OldReport = x, NewReport = y })
.ForEach(report =>
{
if (report.OldReport != null)
report.OldReport.InjectFrom(report.NewReport);
else
session.Store(report.NewReport);
});
session.SaveChanges();
}
但是,RavenDB不支持.Join
运算符。 编辑:我刚刚意识到这也需要成为一个正确的外部联接才能实现这一目标,但我认为它传达了我的意图。我知道我需要做一些Map Reduce来制作发生这种情况,但我是RavenDB的新手,我找不到任何与我正在做的相关的好例子。有人试过这样的事吗?
P.S。如果有人在想,.InjectFrom()
操作正在使用Omu.ValueInjecter
。
答案 0 :(得分:3)
有多种方法可以做到这一点,但最简单的方式是提供自己的文档密钥,而不是使用Raven生成的密钥。这通常被称为使用“结构化”或“语义”键。 Here is a good description of the general technique
只需在您的班级中添加字符串Id
属性即可。您希望文档键反映您描述的唯一键,因此它可能应该具有"reports/2013/Q1/bob"
之类的值(但您可能需要user
更独特的值)。
您可以让.Net在属性getter中为您构建密钥,例如:
public class Report
{
public string Id
{
get { return string.Format("reports/{0}/Q{1}/{2}", Year, Quarter, User); }
}
public string User { get; set; }
public int Quarter { get; set; }
public int Year { get; set; }
public string ReportData { get; set; }
}
现在,当您存储这些文档时,只需存储它们:
foreach (var report in reports)
session.Store(report);
如果已存在具有相同密钥的文档,则会使用新数据覆盖该文档。否则,将写入一份新文件。
如果您无法操纵文档密钥,您可以查看的其他技术是:
您可以先运行查询,删除与更改后的数据匹配的所有文档。然后你可以插入所有数据。但是,由于要匹配多个字段,因此难以正确查询。这是有可能的,但这项技术具有挑战性。
您可以使用Patching API来操纵已存储文档的数据。虽然您仍然需要查询以确定哪些是新插入,哪些是更新。此外,必须针对整个数据库测试补丁,因此速度很慢。
我确信还有其他一些想法,但您最安全,最简单的方法是使用语义键来处理报告。