1. ホーム
  2. couchdb

[解決済み] CouchDBドキュメントをモデル化するための原則

2022-08-19 02:10:03

質問

以前から答えようと思っていたのですが、どうしてもわからないことがあります。

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

例えば、ブログ投稿を取る。

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

  • ポスト
  • ユーザー
  • コメント
  • タグ
  • スニペット

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

そこにあるブログ記事のほとんどは、これを行う方法の簡単な例を示しています。 基本的には同じように分割しますが、各ドキュメントに「任意の」プロパティを追加できると言っています。 だから、あなたはCouchDBでこのようなものを持っていると思います。

  • コメント
  • ユーザー名

CommentとUserを放り込めばいいという人もいるので、こうなりますね。


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文書からコメントだけを抽出し、コメントモデルに取り込むようなビューを書くこともできますし、UsersやTagsについても同様です。

でも、それならサイト全体を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"
                    ...
                }
            }
        }
    }
}

これで欲しいものを探すためのビューを簡単に作れそうですね。

そこで疑問なのですが、ドキュメントを分割するタイミングや、ドキュメント間に"RELATIONS"を作るタイミングはどのように判断しているのでしょうか?

このように分割すれば、よりオブジェクト指向になり、バリューオブジェクトへのマッピングが容易になると思います。


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

... でも、そうすると、リレーショナル・データベースのように見えてきます。 そして、しばしば、quot;whole-site-in-a-document"のようなものを継承することがあるので、リレーションでモデル化するのがより難しくなっています。

リレーショナルデータベースとドキュメントデータベースをいつ、どのように使い分けるかについては、いろいろと読みましたが、ここでの主な論点はそこではありません。 私はより多くのちょうど疑問に思っている、CouchDBのデータをモデル化するときに適用する良いルール/原則は何ですか。

別の例では、XMLファイル/データである。 いくつかのXMLデータは10以上の深いレベルの入れ子を持っており、私はActiveRecord、CouchRest、または他のオブジェクトリレーショナルマッパーからJSONをレンダリングすると同じクライアント(例えば、Rails、またはFlex上のAjax)を使用してそれを視覚化したい。 時々、以下のようなサイト構造全体を含む巨大な XML ファイルを受け取ることがあります。


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

ということで、一般的なCouchDBの質問です。

  1. サイト全体を1つのドキュメントにまとめてもいいのでしょうか?
  2. もしそうなら、任意の深さのレベル(上記の大きなjsonの例やxmlの例のように)を持つドキュメントのシリアライズ/デシリアライズはどのように扱うのですか?
  3. あるいは、それらを VO に変換せず、単に "これらはオブジェクト リレーショナル マップにはあまりにもネストされているので、生の XML/JSON メソッドを使用してそれらにアクセスします" を決定しますか?

ご協力ありがとうございました!CouchDBでデータをどのように分割するかという問題は、私にとって"これは私がこれからそれを行うべき方法です"と言うことは困難でした。 早く到達したいです。

以下のサイト/プロジェクトで勉強させていただきました。

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

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

どのように解決するのですか?

すでにこれにいくつかの素晴らしい答えがあったが、私はviatroposによって説明された元の状況で動作するためのオプションのミックスにいくつかのより多くの最近のCouchDBの機能を追加したいと思いました。

ドキュメントを分割するためのキーポイントは、(先に述べたように)競合があるかもしれない場所です。まったく関係のない更新 (たとえば、サイト全体のドキュメントにリビジョンを追加するコメントの追加) に対して単一のリビジョン パスを取得することになるので、大量の "tangled" ドキュメントを単一のドキュメントにまとめておくことは決してありません。様々な、より小さい文書間の関係や接続を管理することは、最初は混乱することができますが、CouchDBは、単一の応答に異種の部分を結合するためのいくつかのオプションを提供します。

最初の大きなものは、ビューの照合です。あなたがマップ/還元クエリの結果にキー/値ペアを発するとき、キーはUTF-8照合("a"は"b"の前に来る)に基づいてソートされています。また、map/reduce から複雑なキーを JSON 配列として出力することもできます。 ["a", "b", "c"] . そうすることで、配列のキーから構築された一種のquot;tree"を含めることができます。上記の例では、post_id、参照するものの種類、そのID(必要であれば)を出力できます。それから、参照されたドキュメントの ID を返された値のオブジェクトに出力すれば、 'include_docs' クエリーパラメータを使用して、マップ/リダクション出力にそれらのドキュメントを含めることができます。

{"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' とリクエストすると、 'doc' キーが追加され、 'value' オブジェクトで参照されている '_id' を使用するか、 'value' オブジェクトにそれがない場合は、 行を出力したドキュメント (この場合は 'post' ドキュメント) の '_id' を使用します。これらの結果には、生成元のドキュメントを参照する 'id' フィールドが含まれることに注意しましょう。私はスペースと読みやすさのためにそれを省きました。

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

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

これらの状況でCouchDBから第二の有用な追加は、_list関数です。これは、あなたが何らかのテンプレートシステム(あなたが戻ってHTML、XML、CSVまたは何をしたい場合)を介して上記の結果を実行する、またはあなたが単一のリクエストで全体のポストのコンテンツ(著者とコメントデータを含む)を要求し、あなたのクライアント側/ UIのコードが必要とするものと一致する単一のJSONドキュメントとして返されることができるしたい場合は統一JSON構造を出力できるようになるであろう。そうすることで、この方法で投稿の統一された出力ドキュメントを要求することができるようになります。

/db/_design/app/_list/posts/unified??start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]&include_docs=true
あなたの _list 関数 (この場合 "unified") はビューマップ/リデュースの結果 (この場合 "posts") を受け取り、必要なコンテンツタイプ (JSON, HTML など) で HTTP 応答を送り返す JavaScript 関数を介してそれらを実行することになります。

これらを組み合わせることで、更新、競合、および複製のために有用で "安全"だと思う任意のレベルで文書を分割し、それらが要求されたときに必要に応じてそれらを元に戻すことができるのです。

お役に立てれば幸いです。