18

ドキュメント指向データベースに興味があり、MongoDBで遊んでみたいと思います。それで、私はかなり単純なプロジェクト(課題追跡システム)を開始しましたが、非リレーショナルな方法で考えるのに苦労しています。

私の問題:

  1. 相互に関連する2つのオブジェクトがあります(たとえばissue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}、ここには問題に関連するユーザーがいます)。別のドキュメント「user」を作成し、「issue」ドキュメントでそのIDで参照する必要がありますか(リレーショナルデータベースのように)、またはすべてのユーザーのデータをサブドキュメントに残す必要がありますか?

  2. ドキュメントにオブジェクト(サブドキュメント)がある場合、それらすべてを1つのクエリで更新できますか?

4

6 に答える 6

4

私はドキュメント指向のデータベースにはまったく慣れていません。現在、node.js と mongodb を使用してある種の CMS を開発しようとしているので、あなたと同じ問題に直面しています。

試行錯誤の結果、次の経験則を見つけました。クエリの「サブジェクト」になる可能性のあるすべてのエンティティのコレクションを作成し、残りを他のオブジェクト内に埋め込みます。

たとえば、ブログ エントリのコメントを埋め込むことができます。通常、コメントはエントリ自体にバインドされており、すべてのコメントに対してグローバルに作成される有用なクエリについては考えられないためです。一方、投稿に添付されたタグは、独自のコレクションに値する場合があります。投稿にバインドされている場合でも、すべてのタグについてグローバルに推論したい場合があるためです (たとえば、トレンド トピックのリストを作成するなど)。

于 2011-11-05T11:12:55.100 に答える
1

mongodb やその他の「NoSQL」製品の優れた点は、設計するスキーマがないことです。私は MongoDB を使用していますが、SQL クエリやひどい JOIN クエリを作成する必要がないため、MongoDB が気に入っています。だからあなたの2つの質問に答えるために。

1 - 複数のドキュメントを作成する場合、DB に対して 2 つの呼び出しを行う必要があります。それが悪いと言っているわけではありませんが、すべてを 1 つのドキュメントにまとめることができるのであれば、いかがでしょうか? MySQL を使用していたときのことを思い出してください。「ブログ」テーブルと「コメント」テーブルを作成していました。ここで、同じコレクション (別名テーブル) 内のレコードにコメントを追加し、それを基に作成を続けます。

2 - はい...

于 2010-03-04T18:47:54.517 に答える
0

元の回答は、誤って読んだために関係が間違った方向に進んだため、この回答をやり直しました。

issue = {コード:"asdf-11"、タイトル:"asdf"、レポーター:{ユーザー名:"qwer"、役割:"マネージャー"}}

チケットのユーザー (作成者) に関する重要な情報を埋め込むことが賢明な決定であるかどうかは、システムの仕様によって異なります。

これらのユーザーがログインして見つけた問題を報告できるようにしていますか? もしそうなら、そのリレーションをユーザー コレクションから切り離したいかもしれません。

一方、そうでない場合は、このスキーマを簡単に回避できます。ここで私が目にする 1 つの問題は、レポーターに連絡したいのに、レポーターの職務が変更された場合、それはやや厄介なことです。ただし、これは現実世界のジレンマであり、データベースのジレンマではありません。

サブドキュメントはレポーターとの 1 対 1 の関係を表しているため、元の回答で述べた断片化の問題も発生しないはずです。

このスキーマには明らかな問題が 1 つあります。それは、繰り返しデータの変更 (正規化された形式のもの) の重複です。

例を見てみましょう。先ほどお話しした現実世界のジレンマに直面し、電話をかけられたユーザーNigelが、自分の役割に今後の新しい職務を反映させたいと考えていると想像してください。Nigelこれは、レポーターがいるすべての行を更新し、彼roleをその新しい位置に変更する必要があることを意味します。これは、MongoDB にとって、時間のかかる、リソースを消費するクエリになる可能性があります。

繰り返しになりますが、ユーザーごとに 100 チケット (管理可能なもの) しかない場合、更新操作はそれほど悪くはなく、実際、データベースを非常に簡単に管理できます。さらに、ドキュメントの移動が (うまくいけば) ないため、これは完全に適切な更新になります。

したがって、これを埋め込む必要があるかどうかは、クエリやドキュメントなどに大きく依存しますが、このスキーマはお勧めできません。具体的には、多くのルート ドキュメント間で変更データが重複しているためです。技術的には、はい、それでうまくいく可能性がありますが、私は試しません.

代わりに、2つを分割します。

ドキュメントにオブジェクト (サブドキュメント) がある場合、1 つのクエリでそれらすべてを更新できますか?

私の元の回答の関係スタイルと同じように、はい、簡単に。

たとえば、 to の役割を更新しNigelMD(前述のように)、チケットのステータスを完了に変更してみましょう。

db.tickets.update({'reporter.username':'Nigel'},{$set:{'reporter.role':'MD', status: 'completed'}})

したがって、この場合、単一のドキュメント スキーマによって CRUD が容易になります。

英語に由来する 1 つの注意点として、位置演算子を使用してルート ドキュメントの下のすべてのサブドキュメントを更新することはできません。代わりに、最初に見つかったものだけを更新します。

繰り返しますが、それが理にかなっていることを願っています。HTH


元の回答

ここに問題に関連するユーザーがいます)。別のドキュメント 'user' を作成し、'issue' ドキュメントでその ID を参照する必要がありますか (リレーショナル データベースの場合と同様)、またはすべてのユーザーのデータをサブドキュメントに残す必要がありますか?

これはかなりの質問であり、先に進む前にある程度の背景知識が必要です。

最初に考慮すべきことは、問題のサイズです。

issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}

それほど大きくはなく、(ルート ドキュメントにある) 情報はもう必要ないので、reporter小さくすることもできますが、問題はそれほど単純ではありません。たとえば、MongoDB JIRA を見ると: https://jira.mongodb.org/browse/SERVER-9548 (私の主張を証明するランダムなページとして) 「チケット」の内容は、実際にはかなりの量になる可能性があります。

チケットを埋め込むことで真のメリットを得る唯一の方法は、すべてのユーザー情報を、BSON ドキュメントの最大サイズである連続ストレージの単一の 16 MB ブロックに格納できる場合です (mongod現在は によって課されています)。

すべてのチケットを 1 人のユーザーに保存することはできないと思います。

チケットをコード、タイトル、および説明に縮小したとしても、 MongoDBのドキュメントの定期的な更新と変更によって引き起こされる「スイスチーズ」の問題に苦しむ可能性があります。 .10gen.com/presentations/storage-engine-internalsは、私が言いたいことの良いリファレンスです。

通常、ユーザーがルート ユーザー ドキュメントに複数のチケットを追加すると、この問題が発生します。チケット自体も同様に変更されますが、劇的または頻繁に変更されることはありません。

もちろん、この問題は、2 の累乗のサイズ割り当てを使用して少し改善できます: http://docs.mongodb.org/manual/reference/command/collMod/#usePowerOf2Sizesは、缶に記載されているとおりに動作します。

仮説としては、チケットをサブドキュメントとしてルート ユーザーに保存するだけcodetitle問題なく保存できます。

ドキュメントにオブジェクト (サブドキュメント) がある場合、1 つのクエリでそれらすべてを更新できますか?

はい、とても簡単です。これは、埋め込みによって簡単になることの 1 つです。次のようなクエリを使用できます。

db.users.update({user_id:uid,'tickets.code':'asdf-1'}, {$set:{'tickets.$.title':'Oh NOES'}})

ただし、位置演算子を使用して一度に 1 つのサブドキュメントしか更新できないことに注意してください。つまり、1 回のアトミック操作で、1 人のユーザーのすべてのチケットの日付を 5 日先に更新することはできません。

新しいチケットの追加に関しては、非常に簡単です:

db.users.update({user_id:uid},{$push:{tickets:{code:asdf-1,title:"Whoop"}}})

そうです、クエリによっては、ユーザー データ全体を 1 回の呼び出しで簡単に更新できます。

それはかなり長い答えだったので、何も見逃していないことを願っています。

于 2013-05-03T09:27:19.297 に答える