在面向文档的数据库系统(如RavenDB)中,我该如何对层次结构和关系数据进行建模? [英] How would I model data that is heirarchal and relational in a document-oriented database system like RavenDB?

查看:120
本文介绍了在面向文档的数据库系统(如RavenDB)中,我该如何对层次结构和关系数据进行建模?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

面向文档的数据库(尤其是RavenDB)确实让我着迷,我想与他们一起玩一点.但是,作为一个非常习惯关系映射的人,我试图考虑如何在文档数据库中正确建模数据.

Document oriented databases (particularly RavenDB) are really intriguing me, and I'm wanting to play around with them a bit. However as someone who is very used to relational mapping, I was trying to think of how to model data correctly in a document database.

假设我在C#应用程序中拥有一个具有以下实体的CRM(保留了不需要的属性):

Say I have a CRM with the following entities in my C# application (leaving out unneeded properties):

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文档中,因为联系人和任务没有公司的目的,并且在大多数情况下,查询任务或联系人还会显示有关关联公司的信息.

I was thinking of putting this all in a Company document, as contacts and tasks do not have a purpose out side of companies, and most of the time query for a task or contacts will also show information about the associated company.

Task实体附带该问题.假设业务要求任务始终与公司相关联,但也可以选择与任务相关联.

The issue comes with Task entities. Say the business requires that a task is ALWAYS associated with a company but optionally also associated with a task.

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

In a relational model this is easy, as you just have a Tasks table and have the Company.Tasks relate to all tasks for the company, while Contact.Tasks only show the tasks for the specific Task.

为了在文档数据库中对此建模,我想到了以下三个想法:

For modeling this in a document database, I thought of the following three ideas:

  1. 模型任务作为单独的文档.这似乎是一种反文档数据库,因为大多数情况下,您在查看公司或联系人时都希望看到任务列表,因此必须对文档执行很多连接.

  1. Model Tasks as a separate document. This seems kind of anti-document db as most of the time you look at a company or contact you will want to see the list of tasks, thus having to perform joins over documents a lot.

将与联系人不相关的任务保留在Company.Tasks列表中,并将与联系人关联的任务放在每个单独联系人的列表中.不幸的是,这意味着如果您要查看公司的所有任务(可能很多),则必须将公司的所有任务与每个联系人的所有任务结合起来.当您要将任务从联系人中取消关联时,我也看到这很复杂,因为您必须将其从联系人移至公司

Keep tasks that are not associated with a contact in the Company.Tasks list and put tasks assocaited with a contact in the list for each individual contacts. This unfortunately means that if you want to see all tasks for a company (which will probably be a lot) you have to combine all tasks for the company with all tasks for each individual contact. I also see this being complicated when you want to disassociate a task from a contact, as you have to move it from the contact to the company

保留Company.Tasks列表中的所有任务,每个联系人都有与其关联的任务的ID值列表.这似乎是一种好方法,除了必须手动获取id值并必须为联系人创建Task实体的子列表.

Keep all tasks in the Company.Tasks list, and each contact has a list of id values for tasks it is associated with. This seems like a good approach except for having to manually take id values and having to make a sub-list of Task entities for a contact.

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

What is the recommended way to model this data in a document oriented database?

推荐答案

使用非规范化引用:

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

从本质上讲,您具有DenormalizedReference类:

in essence you have a DenormalizedReference class:

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接口-但是您可以根据需要使用它:

your documents look like - i've implemented the INamedDocument interface - this can be whatever you need it to be though:

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; }
}

现在保存任务的方式与以前完全一样:

Now saving a Task works exactly as it did before:

var task = new Task{
    Company = myCompany,
    Contact = myContact
};

然而,将所有这些撤回将意味着您仅将获得子对象的非规范化引用.为了补充水分,我使用了一个索引:

However pulling all this back will mean you're only going to get the denormalized reference for the child objects. To hydrate these I use an index:

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
                                                    };
    }
}

使用索引检索水化任务是:

And using your index to retrieve the hydrated tasks is:

var tasks = from c in _session.Query<Projections.Task, Tasks_Hydrated>()
                    where c.Name == "taskmaster"
                    select c;

我认为这很干净:)

作为设计对话,一般规则是,如果您永远需要像以下那样单独加载子文档 -而不是父文档的一部分.无论是用于编辑还是查看-您都应该使用自己的ID和自己的文档对其进行建模.使用上面的方法使这个过程非常简单.

As a design conversation - the general rule is that if you ever need to load the child documents alone as in - not part of the parent document. Whether that be for editing or viewing - you should model it with it's own Id as it's own document. Using the method above makes this quite simple.

这篇关于在面向文档的数据库系统(如RavenDB)中,我该如何对层次结构和关系数据进行建模?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆