代码之家  ›  专栏  ›  技术社区  ›  Alex Klaus

多对多关系ravendb:文档结构和索引

  •  1
  • Alex Klaus  · 技术社区  · 6 年前

    如何为以下关系模式构建nosql模型和索引(最好是ravendb v4)?

    文件类型 Contact ,其中每个记录可以具有多个附加属性(属性的类型被定义为 CustomField 以及 ContactCustomField ) enter image description here

    考虑到需要在一个查询中筛选/排序突出显示的字段(联系人和自定义字段中的所有字段)。


    我认为可能的选择:

    选项1

    当然,我可以想象以下持久模型:

    public class Contact
    {
        public string Id      { get; set; }
        public string Name    { get; set; }
        public string Address { get; set; }
        public string Phone   { get; set; }
        // Where the key is CustomField.Id and the value is ContactCustomField.Value
        public Dictionary<string, string> CustomValues { get; set; }
    }
    
    public class CustomField
    {
        public string Id          { get; set; }
        public string Code        { get; set; }
        public string DataType    { get; set; }
        public string Description { get; set; }
    }
    

    但是,为下面这样的查询构建索引(很抱歉,语法有误)让我困惑:

    SELECT Name, Address, Phone, CustomValues
    FROM Contact
    WHERE Name LIKE '*John*' AND CustomValues.Any(v => v.Key == "11" && v.Value == "student")
    

    选项2

    另一种方法是保持结构正常化(如上图所示)。那就行了-我只需要包括 联系人自定义域 在查询中 接触

    缺点是不能利用nosql的好处。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Alex Klaus    6 年前

    更新的答案 (2018年6月29日)

    成功的关键在于一个被低估的乌鸦的特征- Indexes with Dynamic Fields . 它允许保持逻辑数据结构,并避免创建 fanout index

    使用方法是构建选项1中如上所述的集合:

    public class Contact
    {
        public string Id      { get; set; }
        public string Name    { get; set; }
        public string Address { get; set; }
        public string Phone   { get; set; }
        public Dictionary<string, object> CustomFields { get; set; }
    }
    
    public class CustomField
    {
        public string Id          { get; set; }
        public string Code        { get; set; }
        public string DataType    { get; set; }
        public string Description { get; set; }
    }
    

    哪里 Contact.CustomFields.Key 是指 CustonField.Id Contact.CustomFields.Value 存储该自定义字段的值。

    为了筛选/搜索自定义字段,我们需要以下索引:

    public class MyIndex : AbstractIndexCreationTask<Contact>
    {
        public MyIndex()
        {
            Map = contacts =>
                from e in contacts
                select new
                {
                    _ = e.CustomFields.Select( x => CreateField ($"{nameof(Contact.CustomFields)}_{x.Key}", x.Value))
                };
        }
    } 
    

    该索引将覆盖字典的所有键值对,因为它们是 Contact

    果茶

    如果使用常用的查询对象在c中编写查询,会遇到一个很大的问题( IRavenQueryable 类型),而不是 RQL DocumentQuery . 它以我们命名动态字段的方式-它是一个特定格式的复合名称: dictionary_name + underscore + key_name . 它允许我们构建如下查询

    var q = s.Query<Person, MyIndex>()
                    .Where(p => p.CustomFields["Age"].Equals(4));
    

    在引擎盖下被转换成RQL:

    from index 'MyIndex' where CustomFields_Age = $p1
    

    这是无证的而且 here 是我与Oren Eini(aka Ayende Rahien)的讨论,你可以在这方面学到更多的东西。

    P.S.我的一般建议是通过 文档查询 而不是平常 Query ( link ),因为linq集成仍然很弱,开发人员可能会在这里和那里不断遇到bug。


    初步答复 (2018年6月9日)

    因为它是 suggested 由Oren Eini(又名Ayende Rahien)提供,方法是选择2-包括一个单独的 ContactCustomField 查询中的集合。

    因此,尽管使用了nosql数据库,但关系方法是唯一的方法。

        2
  •  0
  •   user2292759    6 年前

    为此,您可能需要使用map-reduced索引。

    地图:

    docs.Contacts.SelectMany(doc => (doc, next) => new{
    // Contact Fields
    doc.Id,
    doc.Name,
    doc.Address,
    doc.Phone,
    doc.CustomFieldLoaded = LoadDocument<string>(doc.CustomValueField, "CustomFieldLoaded"),
    doc.CustomValues
    });
    

    减少:

    from result in results
    group result by {result.Id, result.Name, result.Address, result.Phone, result.CustomValues, result.CustomFieldLoaded} into g
    select new{
    g.Key.Id,
    g.Key.Name,
    g.Key.Address,
    g.Key.Phone,
    g.Key.CustomFieldLoaded = new {},
    g.Key.CustomValues = g.CustomValues.Select(c=> g.Key.CustomFieldLoaded[g.Key.CustomValues.IndexOf(c)])
    }
    

    您的文档将如下所示:

    {
    "Name": "John Doe",
    "Address": "1234 Elm St",
    "Phone": "000-000-0000",
    CustomValues: "{COLLECTION}/{DOCUMENTID}"
    }
    

    这将加载联系人,然后加载关系文档的数据。

    我没有测试这个确切的例子,但它是基于一个工作的例子,我已经在我自己的项目中实现。你可能需要做些调整。

    当然,您需要调整它以包含许多文档,但它应该为您提供如何使用关系的基本概念。

    您还应该签出的文档 document relationships

    我希望这能有帮助。