使用C#查询RavenDB中的复合键集合

时间:2013-07-01 20:06:11

标签: c# ravendb

我在这个类的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

1 个答案:

答案 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来操纵已存储文档的数据。虽然您仍然需要查询以确定哪些是新插入,哪些是更新。此外,必须针对整个数据库测试补丁,因此速度很慢。

我确信还有其他一些想法,但您最安全,最简单的方法是使用语义键来处理报告。