125

しばらく答えようとしているのですが、わからない質問があります。

CouchDB ドキュメントをどのように設計または分割しますか?

ブログ投稿を例に取ります。

それを行う半「リレーショナル」な方法は、いくつかのオブジェクトを作成することです。

  • 役職
  • ユーザー
  • コメント
  • 鬼ごっこ
  • スニペット

これは非常に理にかなっています。しかし、私は同じことをモデル化するためにcouchdbを使用しようとしています(それが素晴らしいというすべての理由から)が、非常に困難でした。

そこにあるブログ投稿のほとんどは、これを行う方法の簡単な例を示しています. 彼らは基本的に同じように分割しますが、「任意の」プロパティを各ドキュメントに追加できると言っています。これは間違いなく素晴らしいことです。したがって、CouchDB では次のようになります。

  • 投稿 (ドキュメント内のタグとスニペット「疑似」モデル付き)
  • コメント
  • ユーザー

コメントとユーザーをそこに入れることができると言う人もいるので、次のようになります。


post {
    id: 123412804910820
    title: "My Post"
    body: "Lots of Content"
    html: "<p>Lots of Content</p>"
    author: {
        name: "Lance"
        age: "23"
    }
    tags: ["sample", "post"]
    comments {
        comment {
            id: 93930414809
            body: "Interesting Post"
        } 
        comment {
            id: 19018301989
            body: "I agree"
        }
    }
}

とても見栄えが良く、わかりやすいです。また、ユーザーとタグと同じように、すべての Post ドキュメントからコメントのみを抽出してコメント モデルに取得するビューを作成する方法も理解しています。

しかし、「サイト全体を 1 つのドキュメントにまとめてみませんか?」と思います。


site {
    domain: "www.blog.com"
    owner: "me"
    pages {
        page {
            title: "Blog"
            posts {
                post {
                    id: 123412804910820
                    title: "My Post"
                    body: "Lots of Content"
                    html: "<p>Lots of Content</p>"
                    author: {
                        name: "Lance"
                        age: "23"
                    }
                    tags: ["sample", "post"]
                    comments {
                        comment {
                            id: 93930414809
                            body: "Interesting Post"
                        } 
                        comment {
                            id: 19018301989
                            body: "I agree"
                        }
                    }
                }
                post {
                    id: 18091890192984
                    title: "Second Post"
                    ...
                }
            }
        }
    }
}

必要なものを見つけるためのビューを簡単に作成できます。

次に、ドキュメントをより小さなドキュメントに分割するタイミング、またはドキュメント間に「関係」を作成するタイミングをどのように決定しますか?

次のように分割すると、より「オブジェクト指向」になり、値オブジェクトにマップしやすくなると思います。


posts {
    post {
        id: 123412804910820
        title: "My Post"
        body: "Lots of Content"
        html: "<p>Lots of Content</p>"
        author_id: "Lance1231"
        tags: ["sample", "post"]
    }
}
authors {
    author {
        id: "Lance1231"
        name: "Lance"
        age: "23"
    }
}
comments {
    comment {
        id: "comment1"
        body: "Interesting Post"
        post_id: 123412804910820
    } 
    comment {
        id: "comment2"
        body: "I agree"
        post_id: 123412804910820
    }
}

...しかし、リレーショナル データベースのように見え始めます。また、「ドキュメント内のサイト全体」のように見えるものを継承することが多いため、リレーションでモデル化するのはより困難です。

リレーショナル データベースとドキュメント データベースをいつ、どのように使用するかについて多くのことを読んできたので、それはここでの主な問題ではありません。CouchDBでデータをモデル化するときに適用するのに適したルール/原則は何ですか。

もう 1 つの例は、XML ファイル/データの場合です。一部の XML データには 10 レベル以上の深さのネストがあり、ActiveRecord、CouchRest、またはその他のオブジェクト リレーショナル マッパーから JSON をレンダリングするのと同じクライアント (たとえば、Ajax on Rails、または Flex) を使用して視覚化したいと考えています。以下のようなサイト構造全体である巨大な XML ファイルを取得することがあります。それを値オブジェクトにマップして Rails アプリで使用する必要があるため、データをシリアル化/逆シリアル化する別の方法を記述する必要はありません。 :


<pages>
    <page>
        <subPages>
            <subPage>
                <images>
                    <image>
                        <url/>
                    </image>
                </images>
            </subPage>
        </subPages>
    </page>
</pages>

したがって、一般的な CouchDB の質問は次のとおりです。

  1. ドキュメントを分割するためにどのような規則/原則を使用していますか (関係など)?
  2. サイト全体を 1 つのドキュメントにまとめてもよろしいですか?
  3. もしそうなら、任意の深さレベル (上記の大きな json の例や xml の例など) のドキュメントのシリアライズ/デシリアライズをどのように処理しますか?
  4. それとも、それらを VO に変換しないで、「これらは Object-Relational Map にネストされすぎているので、未加工の XML/JSON メソッドを使用してアクセスするだけです」と判断しますか?

お世話になりました、CouchDBでデータをどう分割するかという問題は、「これからはこうすればいい」と言い切るのが難しかったです。私はすぐにそこに着くことを願っています。

以下のサイト/プロジェクトを調査しました。

  1. CouchDB の階層データ
  2. CouchDB ウィキ
  3. ソファ - CouchDB アプリ
  4. CouchDB 決定版ガイド
  5. PeepCode CouchDB スクリーンキャスト
  6. カウチレスト
  7. CouchDB README

...しかし、彼らはまだこの質問に答えていません。

4

4 に答える 4

26

これにはすでにいくつかの優れた回答がありましたが、viatropos によって説明された元の状況を処理するためのオプションの組み合わせに、いくつかの最近の CouchDB 機能を追加したいと考えました。

ドキュメントを分割する重要なポイントは、競合が発生する可能性がある場所です (前述のとおり)。完全に無関係な更新 (たとえば、サイト ドキュメント全体にリビジョンを追加するコメントの追加) に対して単一のリビジョン パスが得られるため、大量に「絡み合った」ドキュメントを 1 つのドキュメントにまとめておくべきではありません。さまざまな小さなドキュメント間の関係または接続を管理することは、最初は混乱する可能性がありますが、CouchDB には、異なる部分を組み合わせて単一の応答にするためのオプションがいくつか用意されています。

最初の大きなものは、ビューの照合です。map/reduce クエリの結果にキーと値のペアを出力すると、キーは UTF-8 照合に基づいて並べ替えられます (「a」が「b」の前に来る)。map/reduce から複雑なキーを JSON 配列として出力することもできます: ["a", "b", "c"]. そうすることで、配列キーから構築されたソートの「ツリー」を含めることができます。上記の例を使用して、post_id、参照しているもののタイプ、その ID (必要な場合) を出力できます。次に、参照されたドキュメントの ID を返された値のオブジェクトに出力すると、「include_docs」クエリ パラメータを使用して、これらのドキュメントを map/reduce 出力に含めることができます。

{"rows":[
  {"key":["123412804910820", "post"], "value":null},
  {"key":["123412804910820", "author", "Lance1231"], "value":{"_id":"Lance1231"}},
  {"key":["123412804910820", "comment", "comment1"], "value":{"_id":"comment1"}},
  {"key":["123412804910820", "comment", "comment2"], "value":{"_id":"comment2"}}
]}

「?include_docs=true」で同じビューをリクエストすると、「値」オブジェクトで参照される「_id」を使用する「doc」キーが追加されるか、「値」オブジェクトに存在しない場合は使用されます行が発行されたドキュメントの「_id」(この場合は「投稿」ドキュメント)。これらの結果には、発行元のソース ドキュメントを参照する「id」フィールドが含まれることに注意してください。スペースと読みやすさのために省略しました。

次に、'start_key' および 'end_key' パラメータを使用して、結果を 1 つの投稿のデータに絞り込むことができます。

?start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]
または、特定のタイプのリストを具体的に抽出することもできます:
?start_key=["123412804910820", "コメント"]&end_key=["123412804910820", "コメント", {}]
空のオブジェクト (" {}") は常に照合の一番下にあり、null または "" は常に一番上にあるため、これらのクエリ パラメータの組み合わせが可能です。

このような状況で CouchDB から追加された 2 つ目の便利な機能は、_list 関数です。これにより、何らかのテンプレート システム (HTML、XML、CSV などが必要な場合) を介して上記の結果を実行したり、投稿のコンテンツ全体 (オーサーおよびコメント データ) を 1 つのリクエストで送信し、クライアント側/UI コードのニーズに一致する 1 つの JSON ドキュメントとして返されます。これを行うと、投稿の統合出力ドキュメントを次のようにリクエストできます。

/db/_design/app/_list/posts/unified??start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]&include_docs=true
_list 関数 (この場合は "unified" という名前) は、view map/reduce (この場合は "posts" という名前) の結果を取得し、JavaScript 関数を介してそれらを実行し、コンテンツ タイプで HTTP 応答を返します。必要 (JSON、HTML など)。

これらのことを組み合わせることで、更新、競合、および複製に対して有用で「安全」であると思われるレベルでドキュメントを分割し、要求されたときに必要に応じてそれらを元に戻すことができます。

それが役立つことを願っています。

于 2011-09-30T13:55:54.980 に答える
17

これは古い質問であることは知っていますが、まったく同じ問題に対する最善のアプローチを見つけようとして、この質問に出くわしました。Christopher Lenz は、 CouchDB で「結合」をモデル化する方法について、すばらしいブログ投稿を書きました。私の結論の 1 つは、「関連データを競合せずに追加できるようにする唯一の方法は、その関連データを別のドキュメントに入れることです」というものでした。したがって、簡単にするために、「非正規化」に傾倒する必要があります。ただし、特定の状況では書き込みが競合するため、自然な障壁にぶつかります。

投稿とコメントの例では、1 つの投稿とそのすべてのコメントが 1 つのドキュメントに含まれている場合、2 人のユーザーが同時にコメントを投稿しようとすると (つまり、ドキュメントの同じリビジョンに対して) 競合が発生します。これは、「単一のドキュメント内のサイト全体」のシナリオではさらに悪化します。

したがって、経験則は「問題が生じるまで非正規化する」ことだと思いますが、「問題がある」のは、ドキュメントの同じリビジョンに対して複数の編集が投稿される可能性が高い場合です。

于 2011-02-17T23:33:11.707 に答える
16

このは、私の記憶が正しければ、ドキュメントが更新される頻度を念頭に置きながら、「痛い」まで非正規化するように言っています。

  1. ドキュメントを分割するためにどのような規則/原則を使用していますか (関係など)?

経験則として、問題のアイテムに関するページを表示するために必要なすべてのデータを含めます。言い換えれば、誰かに渡す現実世界の紙に印刷するすべてのものです。たとえば、株価文書には、数字に加えて、会社名、取引所、通貨が含まれます。契約文書には、取引相手の名前と住所、日付と署名者に関するすべての情報が含まれます。しかし、異なる日付の株価は別の文書を形成し、別の契約は別の文書を形成します。

  1. サイト全体を 1 つのドキュメントにまとめてもよろしいですか?

いいえ、それはばかげているでしょう。

  • 更新のたびにサイト全体 (ドキュメント) を読み書きする必要があり、非常に非効率的です。
  • ビューキャッシングのメリットはありません。
于 2009-10-07T11:10:53.480 に答える
6

Jakeの応答は、CouchDBを使用する上で最も重要な側面の1つであり、スコーピングの決定に役立つ可能性があります。それは競合です。

投稿自体の配列プロパティとしてコメントがあり、Jakeや他の人が正しく指摘しているように、「投稿」DBに巨大な「投稿」ドキュメントがたくさんある場合は、次のシナリオを想像できます。 2人のユーザーが投稿ドキュメントに同時に編集を送信し、そのドキュメントの衝突とバージョンの競合が発生する、非常に人気のあるブログ投稿。

ASIDE:この記事が指摘しているように、そのドキュメントをリクエスト/更新するたびに、ドキュメント全体を取得/設定する必要があることも考慮してください。そのため、サイト全体または多数の投稿を表す大量のドキュメントを渡す必要があります。コメントの数は、避けたい問題になる可能性があります。

投稿がコメントとは別にモデル化され、2人がストーリーにコメントを送信する場合、それらは競合の問題なしに、そのDB内の2つの「コメント」ドキュメントになります。「コメント」データベースに2つの新しいコメントを追加するための2つのPUT操作。

次に、投稿のコメントを返すビューを作成するには、postIDを渡してから、その親の投稿IDを参照するすべてのコメントを論理的な順序で並べ替えて送信します。たぶん、[postID、byUsername]のようなものを「コメント」ビューへのキーとして渡して、親の投稿と、結果をどのようにソートするか、またはそれらの行に沿って何かを示すこともできます。

MongoDBはドキュメントの処理方法が少し異なり、ドキュメントのサブ要素にインデックスを作成できるため、MongoDBメーリングリストに同じ質問が表示され、「コメントを親投稿のプロパティにする」という質問が表示される場合があります。

Mongoの書き込みロックとシングルマスターの性質により、コメントを追加する2人の競合するリビジョンの問題は発生せず、前述のように、コンテンツのクエリ機能は、サブのためにあまり影響を受けません。インデックス。

そうは言っても、どちらかのDBのサブ要素が巨大になる場合(たとえば数万のコメント)、これらの別々の要素を作成することは両方の陣営の推奨事項だと思います。ドキュメントとそのサブ要素のサイズには上限があるため、Mongoの場合は確かにそうです。

于 2011-09-17T02:02:47.110 に答える