【问题标题】:Handling queries over related documents in RavenDB在 RavenDB 中处理对相关文档的查询
【发布时间】:2017-02-10 06:55:26
【问题描述】:

我有一个项目,其中有一组表格:

public class Form
{
    public string Id { get; set; }
    public string Name { get; set; }
    public IList<string> FieldValueIds { get; set; }
    public string UserId { get; set; } // the user who completed the form.
    public string FormTemplateId { get; set; }
}

每个“实现”在创建表单时选择的表单模板。

public class FormTemplate
{
    public string Id { get; set; }
    public string Name { get; set; }
    public IList<string> FieldIds { get; set; }
}

它定义了表单中存在哪些字段。每个字段

public class FormField
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Caption { get; set; }
    public ValueType DataType { get; set; } // Enum specifying the type of data this field accepts.
}

存储有关字段的信息,例如描述和期望的类型。每个 FormField 可以存在于多个 FormTemplate 中,表单的值存储为与表单本身相关的 FieldValue 对象。

public class FieldValue
{
    public string Id { get; set; }
    public string FieldId { get; set; }
    public string ValueAsJsonString { get; set; }
}

其他对象包括用户对象:

public class User
{
    public string Id { get; set; }
    public string Username { get; set; }
    public string GivenNames { get; set; }
    public string Surname { get; set; }
}

我希望能够执行查询以查找由具有指定名称的用户完成的所有表单,或名称为 X 的字段具有值 Y 的所有表单,等等。

我已经研究了文档 Indexing related documents 中指定的索引的使用情况,但是当我按如下方式实现示例时,文档中介绍的实现引发了 NotSupportedException:

class FormTemplates_ByFieldAndName : AbstractIndexCreationTask<FormTemplate>
{
    public class Result
    {
        public string Name { get; set; }
        public IList<string> FieldNames { get; set; }
    }

    public FormTemplates_ByFieldAndName()
    {
        Map = FormTemplates => from FormTemplate in FormTemplates
                             select new
                             {
                                 Name = FormTemplate.Name,
                                 FieldNames = FormTemplate.FieldIds.Select(x => LoadDocument<FormField>(x).Name)
                             };
    }
}

// in code:
IList<FormTemplate> TestResults = session.Query<FormTemplates_ByFieldAndName.Result, FormTemplates_ByFieldAndName>()
                    .Where(x => x.Name == "TemplateName" || x.FieldNames.Contains("FieldName"))
                    .OfType<FormTemplate>()
                    .ToList();

据我所知,这是正确实现的,但是我看到了一个建议,将 .Contains 替换为 .Any 实现。取而代之的是,我一直在通过应用连续的 .Where 参数来尝试不同的方法。像这样:

 var pre = session.Query<FormTemplates_ByFieldAndName.Result, FormTemplates_ByFieldAndName>();
 var pr2 = pre.Where(x => x.Name == "TypeTest25");

 List<FormTemplate> TestResults = pr2
     .Where(x => x.FieldNames.Any(a => a == "field25"))
     .OfType<FormTemplate>()
     .OrderByScoreDescending()
     .ToList();

通过基于提供的预先指定格式的字符串应用连续过滤器,修改系统以更面向工厂的方法执行。

这是我应该采用的实现方式吗?如果不是,我应该改变什么?特别是如果我要继续使用 Indexing 选项,我将如何通过 FormTemplates 将此技术应用于 Forms 和 FormFields 之间的嵌套关系。

【问题讨论】:

    标签: ravendb


    【解决方案1】:

    您似乎正在尝试以一种主要是关系性的方式来执行此操作,但您不必这样做。

    与其尝试拥有一组独立的文档,每个文档都有部分数据,不如将它们全部存储在一个文档中。

    public class Form
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public IList<FieldValue> FieldValues { get; set; }
        public string UserId { get; set; } // the user who completed the form.
        public string FormTemplateId { get; set; }
    }
    
    public class FieldValue
    {
        public string Id { get; set; }
        // can store the value directly!
        //public string ValueAsJsonString { get; set; }
        public object Value {get; set; }
    }
    

    这将生成如下所示的文档:

    {
      "Id": "forms/1234",
      "Name": "Tom",
      "FieldValues": [
       {
          "Id": "FromValues/SchoolDistrictName",
          "Value": "ABi195"
        }
      ],
      "UserId": "users/tom",
      "FormTemplateId": "FromTemplate/1234"
    }
    

    这是一种更自然的建模方式。 此时,您可以使用 RavenDB 索引动态数据的能力,请参阅此处的文档:

    https://ravendb.net/docs/article-page/3.5/Csharp/indexes/using-dynamic-fields

    【讨论】:

    • 哦,谢谢!我已经编辑了上面的示例以更准确地反映 fieldValue 的性质。是否可以让我的索引也代表 FormField 和 FieldValue 对象之间的关系?即我希望能够有一个查询,我可以在其中找到所有带有 formField X 和与之关联的 FormValue Y 的表单。到目前为止,我可以做一个或另一个不是那个特定的功能。
    • 是的,您当然可以这样做。这就是动态字段发挥作用的地方
    • 如何格式化一个 lucene 语法查询来访问这些动态字段?
    • 查看答案中的查询,这是: IList results = session .Advanced .DocumentQuery() .WhereEquals("MyDynamicField", "Red") .ToList( );
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-30
    • 1970-01-01
    相关资源
    最近更新 更多