12

ドキュメント指向のデータベース (特に 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

これをドキュメント データベースでモデル化するために、次の 3 つのアイデアを考えました。

  1. タスクを別のドキュメントとしてモデル化します。ほとんどの場合、会社や連絡先を見ると、タスクのリストを見たいと思うので、これは一種のアンチドキュメントデータベースのように思えます。

  2. 連絡先に関連付けられていないタスクをリストに保持し、連絡先に関連付けられたCompany.Tasksタスクを個々の連絡先のリストに入れます。これは残念なことに、会社のすべてのタスクを表示したい場合 (おそらく大量になるでしょう)、会社のすべてのタスクと個々の連絡先のすべてのタスクを結合する必要があることを意味します。また、連絡先からタスクを会社に移動する必要があるため、連絡先からタスクの関連付けを解除したい場合、これは複雑だと思います

  3. すべてのタスクをCompany.Tasksリストに保持します。各連絡先には、関連付けられているタスクの ID 値のリストがあります。Taskこれは、id 値を手動で取得する必要があり、連絡先のエンティティのサブリストを作成する必要があることを除けば、良いアプローチのようです。

ドキュメント指向データベースでこのデータをモデル化するための推奨される方法は何ですか?

4

2 に答える 2

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 でモデル化する必要があります。上記の方法を使用すると、これが非常に簡単になります。

于 2011-06-09T08:22:27.383 に答える
1

私もdbsを文書化するのは初めてです...だから一粒の塩で...

対照的な例として...Twitterを使用していて、フォローしているユーザーのリストがあり、そのリストにツイートのリストが含まれている場合...ツイートを読むためにTwitterアカウントに移動することはありません。リツイートすると、オリジナルではなくコピーしかありません。

ですから、同じように、タスクが会社に属している場合、それらは会社内にとどまるというのが私の意見です。会社はタスクの集約ルートです。連絡先は、タスクの参照(ID)またはコピーのみを保持でき、それらを直接変更することはできません。連絡先にタスクの「コピー」を保持させる場合は問題ありませんが、タスクを変更する(たとえば、完了のマークを付ける)には、集約ルート(会社)を使用してタスクを変更します。コピーはすぐに古くなる可能性があるため、メモリ内にコピーを存在させたいだけで、連絡先を保存するときは、タスクへの参照のみを保存するようです。

于 2011-06-08T22:10:48.160 に答える