【问题标题】:How to properly create a Map/Reduce Index for RavenDB in C#如何在 C# 中为 RavenDB 正确创建 Map/Reduce 索引
【发布时间】:2013-02-25 02:17:18
【问题描述】:

我正在开发一个在后端使用 RavenDB 的应用程序。这是我第一次使用 Raven,我在 Map/Reduce 方面遇到了困难。

我一直是reading the doc's,但不幸的是我在这个过程中没有取得任何进展。

基本上我有成千上万个这样的文档。

{
  .....
  "Severity": {
    "Code": 6,
    "Data": "Info"
  },
  "Facility": {
    "Code": 16,
    "Data": "Local Use 0 (local0)"
  },
  .....
}

除此之外,我需要使用如下所示的输出进行单个查询。

{"Severity": [
    {"Emergency":0},
    {"Alert":0},
    {"Critical":0},
    {"Error":0},
    {"Warning":0},
    {"Notice":0},
    {"Info":2711},
    {"Debug":410}
],
"Facility": [
    {"Kernel Messages":0},
    {"User-Level Messages":0},
    {"Mail System":0},
    {"System Daemons":0},
    {"Security/Authorization Messages":0},
    {"Internal Syslogd Messages":0},
    {"Line Printer Subsystem":2711},
    {"Network News Subsystem":410},
    ....
    {"Local Use 0 (local0)": 2574},
    ...
]}

其中 Severity/Facility Array 中的“Key”是上述 json 数据的 Data 部分,Severity/Facility Array 中的“value”是每个 Code 类型的文档 Count

示例:
以上述数据为指导,

我的数据库中有 2711 个文档,其严重性为 Info
我的数据库中有 410 个文档,其严重性为 Debug
我的数据库中有 2574 个文档,带有 local0 设施。
等等……


我想做的是在应用启动时生成适当的索引(或检查它们是否已经存在),但我什至不知道从哪里开始。

注意:应用程序需要生成索引,仅手动将其写入 RavenDB Web UI 是不够的。

【问题讨论】:

  • Code 属性是否需要对索引产生任何影响?
  • CodeData 始终匹配。即:Code:6 = Data:Info 每次。
  • 好的,但是您没有将它们包含在输出中,因此它们与此任务基本上无关,对吧?或者两个不同的代码具有相同的数据字符串是否存在冲突的风险?
  • 另外,您的样本结果中显示为零。除非您有一些列出所有不同代码的第三个文档或一组文档,否则这是不可能的。换句话说,如果它不在任何文档中,您将如何将其放入结果中?
  • 我正在制定解决方案。如果您确实有一个或多个包含所有代码/数据对的文档 - 请告诉我。我可以对此进行优化。

标签: c# mapreduce ravendb


【解决方案1】:

您需要结合多种技术来实现这一点,但这是完全可行的。

这是一个适合您的索引。

public class MyIndex : AbstractMultiMapIndexCreationTask<MyIndex.ReduceResult>
{
    public class ReduceResult
    {
        public string Source { get; set; }
        public string Code { get; set; }
        public string Data { get; set; }
        public int Count { get; set; }
    }

    public MyIndex()
    {
        AddMap<MyDoc>(docs => from doc in docs
                              select new
                                     {
                                         Source = "Severity",
                                         doc.Severity.Code,
                                         doc.Severity.Data,
                                         Count = 1
                                     });

        AddMap<MyDoc>(docs => from doc in docs
                              select new
                                     {
                                         Source = "Facility",
                                         doc.Facility.Code,
                                         doc.Facility.Data,
                                         Count = 1
                                     });

        Reduce = results => from result in results
                            group result by new { result.Source, result.Code }
                            into g
                            select new
                            {
                                g.Key.Source,
                                g.Key.Code,
                                g.First().Data,
                                Count = g.Sum(x => x.Count)
                            };

        TransformResults = (database, results) =>
                           from result in results
                           group result by 0
                           into g
                           select new
                           {
                               Severity = g.Where(x => x.Source == "Severity")
                                           .ToDictionary(x => x.Data, x => x.Count),
                               Facility = g.Where(x => x.Source == "Facility")
                                           .ToDictionary(x => x.Data, x => x.Count)
                           };
    }
}

您还需要一个用于转换结果的容器类:

public class MyDocCounts
{
    public IDictionary<string, int> Severity { get; set; }
    public IDictionary<string, int> Facility { get; set; }
}

你会这样查询它:

var result = session.Query<MyIndex.ReduceResult, MyIndex>()
                    .As<MyDocCounts>()
                    .ToList().First();

.ToList() 可能看起来多余,但它是必要的,因为我们在转换中进行分组。

完整的单元测试是here。其输出如下所示:

{
  "Severity": {
    "AAA": 20,
    "BBB": 20,
    "CCC": 20,
    "DDD": 20,
    "EEE": 20
  },
  "Facility": {
    "FFF": 20,
    "GGG": 20,
    "HHH": 20,
    "III": 20,
    "JJJ": 20
  }
}

【讨论】:

  • 好的,所以我有机会尝试一下,但是,我想知道如何调用它,以便索引建立在应用程序启动时。当前运行查询会抛出以下异常There is no index named: MyIndex
  • 纳米,我明白了...IndexCreation.CreateIndexes(typeof(SyslogDocumentCountIndex).Assembly, DataDocumentStore.DocumentStore);
  • 这很好,但它会创建您在程序集中任何位置定义的所有索引。如果您只想创建单个索引,请参阅我引用的单元测试。
  • 那么我上面贴的和documentStore.ExecuteIndex(new SyslogDocumentCountIndex());这个有什么区别?
  • 进行单元测试时,通常只添加一个索引。您可能在程序集中定义了几个与您正在执行的测试无关的内容。在生产中,您通常按照自己的方式进行操作。它对 raven 服务器没有影响。
猜你喜欢
  • 2017-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多