12

面向文檔的數據庫(特別是RavenDB)真的很吸引我,我想與他們玩一下。然而作爲一個非常習慣於關係映射的人,我試圖想到如何在文檔數據庫中正確地建模數據。我如何模擬像RavenDB這樣的面向文檔的數據庫系統中的heirarchal和關係數據?

說我有一個在我的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僅顯示特定任務的任務。

對於在文檔數據庫建模這一點,我認爲以下三種思路:

  1. 型號任務作爲一個單獨的文件。這似乎是一種反文檔數據庫,因爲大多數情況下,當您查看公司或聯繫人時,您會想要查看任務列表,因此必須對文檔進行大量連接。

  2. 保留與Company.Tasks列表中未與聯繫人關聯的任務,並將與聯繫人關聯的任務與每個單個聯繫人列表相關聯。這不幸意味着如果你想看到公司的所有任務(這可能會很多),你必須將公司的所有任務與每個聯繫人的所有任務結合起來。當您想從聯繫人中分離任務時,我也會發現這很複雜,因爲您必須將其從聯繫人移動到公司

  3. 將所有任務都保留在Company.Tasks列表中,並且每個聯繫人都有一個id列表與其相關聯的任務的值。這似乎是一種很好的方法,除了必須手動獲取id值並且必須爲聯繫人創建Task實體的子列表。

在面向文檔的數據庫中對此數據建模的建議方法是什麼?

回答

10

使用非規範化的引用:

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接口 - 這可以是任何你需要它是:

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作爲自己的文檔進行建模。使用上面的方法使得這很簡單。

+0

好吧,所以我想我把反規範化做得太過分了,但是把它們分開放棄基於文檔的db的好處,因爲我必須不斷地在文檔之間進行連接? – KallDrexx 2011-06-09 12:57:02

+0

您不會因爲這些索引閃電般快速,並且db.Load 發生在服務器上,所以成本很低。你應該考慮你的交易界限在哪裏,只有當你真的需要時才使用這種方法 - 但這確實意味着你可以從兩個世界中獲益。我忘記提及更新非規範化引用(如果名稱更改),則需要運行修補程序來更新引用。這又是一件非常簡單的事 - 但你需要管理一個過程。我發現這是一個很小的代價,它被一個無模式數據庫的好處大大超過了:) – iwayneo 2011-06-09 16:00:17

+0

這很有道理:)。我非常喜歡文檔(更重要的是無模式)數據庫的想法。謝謝! – KallDrexx 2011-06-09 16:23:10

1

我是新來記錄DBS以及...所以用一粒鹽...

作爲對比的例子......如果你在Twitter上,你有人民的名單你跟着,其中包含他們的推文列表... ...你不會將他們的推文轉移到你的Twitter帳戶,以閱讀它們,如果你重新推特,你只會有一個副本,而不是原來的。

因此,同樣,我的觀點是,如果任務屬於一家公司,那麼他們留在公司內。公司是任務的總根。聯繫人只能保存引用(ID)或任務副本,不能直接修改它們。如果您的聯繫人持有該任務的「副本」,那沒問題,但爲了修改任務(例如將其標記爲已完成),您將通過其聚合根(公司)修改該任務。由於副本可能會很快過時,因此您似乎只想在內存中存儲副本並保存聯繫人時,只會保存對任務的引用。

相關問題