gpt4 book ai didi

data-modeling - 如何在 RavenDB 等面向文档的数据库系统中对分层和关系数据进行建模?

转载 作者:行者123 更新时间:2023-12-04 01:03:04 25 4
gpt4 key购买 nike

面向文档的数据库(尤其是 RavenDB)真的很吸引我,我想尝试一下它们。然而,作为一个非常习惯关系映射的人,我试图思考如何在文档数据库中正确地建模数据。

假设我的 C# 应用程序中有一个包含以下实体的 CRM(省略了不需要的属性):

public class Company
{
public int Id { get; set; }
public IList<Contact> Contacts { get; set; }
public IList<Task> Tasks { get; set; }
}

public class Contact
{
public int Id { get; set; }
public Company Company { get; set; }
public IList<Task> Tasks { get; set; }
}

public class Task
{
public int Id { get; set; }
public Company Company { get; set; }
public Contact Contact { get; set; }
}

我想把这一切都放在 Company文档,因为联系人和任务在公司之外没有目的,大多数时候查询任务或联系人也会显示有关关联公司的信息。

问题来自 Task实体。假设业务要求任务始终与公司相关联,但也可选择与任务相关联。

在关系模型中这很容易,因为您只有 Tasks表并拥有 Company.Tasks与公司的所有任务相关,而 Contact.Tasks仅显示特定任务的任务。

为了在文档数据库中对此进行建模,我想到了以下三个想法:
  • 将任务建模为单独的文档。这似乎是一种反文档数据库,因为大多数时候您查看公司或联系人您会希望查看任务列表,因此必须对文档执行很多连接。
  • 将与联系人无关的任务保留在 Company.Tasks 中列出每个联系人的列表并将与联系人相关联的任务放入列表中。不幸的是,这意味着如果您想查看公司的所有任务(可能很多),您必须将公司的所有任务与每个联系人的所有任务结合起来。当您想解除任务与联系人的关联时,我也看到这很复杂,因为您必须将其从联系人移动到公司
  • 将所有任务保存在 Company.Tasks 中列表,每个联系人都有一个与其关联的任务的 id 值列表。除了必须手动获取 id 值并且必须创建 Task 的子列表之外,这似乎是一个不错的方法。联系人的实体。

  • 在面向文档的数据库中对这些数据建模的推荐方法是什么?

    最佳答案

    使用非规范化引用:

    http://ravendb.net/faq/denormalized-references

    本质上你有一个 DenormalizedReference 类:

    public class DenormalizedReference<T> where T : INamedDocument
    {
    public string Id { get; set; }
    public string Name { get; set; }

    public static implicit operator DenormalizedReference<T> (T doc)
    {
    return new DenormalizedReference<T>
    {
    Id = doc.Id,
    Name = doc.Name
    }
    }
    }

    你的文档看起来像 - 我已经实现了 INamedDocument 接口(interface) - 这可以是你需要的任何东西:
    public class Company : INamedDocument
    {
    public string Name{get;set;}
    public int Id { get; set; }
    public IList<DenormalizedReference<Contact>> Contacts { get; set; }
    public IList<DenormalizedReference<Task>> Tasks { get; set; }
    }

    public class Contact : INamedDocument
    {
    public string Name{get;set;}
    public int Id { get; set; }
    public DenormalizedReference<Company> Company { get; set; }
    public IList<DenormalizedReference<Task>> Tasks { get; set; }
    }

    public class Task : INamedDocument
    {
    public string Name{get;set;}
    public int Id { get; set; }
    public DenormalizedReference<Company> Company { get; set; }
    public DenormalizedReference<Contact> Contact { get; set; }
    }

    现在保存任务的工作方式与以前完全一样:
    var task = new Task{
    Company = myCompany,
    Contact = myContact
    };

    但是,将所有这些都拉回来意味着您只会获得子对象的非规范化引用。为了水合这些,我使用了一个索引:
    public class Tasks_Hydrated : AbstractIndexCreationTask<Task>
    {
    public Tasks_Hydrated()
    {
    Map = docs => from doc in docs
    select new
    {
    doc.Name
    };

    TransformResults = (db, docs) => from doc in docs
    let Company = db.Load<Company>(doc.Company.Id)
    let Contact = db.Load<Contact>(doc.Contact.Id)
    select new
    {
    Contact,
    Company,
    doc.Id,
    doc.Name
    };
    }
    }

    并使用您的索引来检索水合任务是:
    var tasks = from c in _session.Query<Projections.Task, Tasks_Hydrated>()
    where c.Name == "taskmaster"
    select c;

    我认为这很干净:)

    作为设计对话 - 一般规则是,如果您需要单独加载子文档 - 不是父文档的一部分。无论是用于编辑还是查看 - 您都应该使用它自己的 Id 作为它自己的文档对其进行建模。使用上面的方法使这变得非常简单。

    关于data-modeling - 如何在 RavenDB 等面向文档的数据库系统中对分层和关系数据进行建模?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6285711/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com