1

我在此类的 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 的新手,我找不到任何与我正在做的事情相关的好例子。有没有人尝试过这样的事情?

PS 该.InjectFrom()操作正在使用Omu.ValueInjecter,如果有人想知道的话。

4

1 回答 1

3

有多种方法可以做到这一点,但最简单的方法是提供您自己的文档密钥,而不是使用 Raven 生成的密钥。这通常被称为使用“结构化”或“语义”键。 这是对一般技术的一个很好的描述

只需将字符串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来操作已存储文档的数据。尽管您仍然需要查询以确定哪些是新插入,哪些是更新。此外,补丁必须针对您的整个数据库进行测试,因此速度会很慢。

我敢肯定还有其他一些想法,但您最安全和最简单的选择是使用报告的语义键。

于 2013-07-01T22:09:35.073 回答