MongoDB NoSQLデータベースでの結合の使用

NoSQLでのlookUp lookUpの使用

この記事をピアレビューするのを親切に手伝ってくれたJulian Motzに感謝します。

SQLデータベースとNoSQLデータベースの最大の違いの1つはJOINです。 リレーショナルデータベースでは、SQL JOIN句を使用すると、2つ以上のテーブルの行を、それらのテーブル間で共通のフィールドを使用して結合できます。 たとえば、booksおよびpublishersの表がある場合は、次のようなSQLコマンドを記述できます:

SELECT book.title, publisher.nameFROM bookLEFT JOIN book.publisher_id ON publisher.id;

つまり、bookテーブルにはpublisher_idフィールドがあり、publisherテーブルのidフィールドを参照しています。

単一の出版社が何千冊もの本を提供できるので、これは実用的です。 パブリッシャーの詳細を更新する必要がある場合は、単一のレコードを変更できます。 すべての本について出版社の情報を繰り返す必要がないため、データの冗長性が最小限に抑えられます。 この技術は正規化として知られています。

SQLデータベースには、関係を維持するための一連の正規化機能と制約機能が用意されています。

NoSQL==結合しませんか?

必ずしも…

MongoDBのようなドキュメント指向のデータベースは、非正規化されたデータを格納するように設計されています。 理想的には、コレクション間には関係がないはずです。 複数の文書で同じデータが必要な場合は、繰り返す必要があります。

リレーショナルデータを必要としない状況がほとんどないため、これはイライラする可能性があります。 幸いなことに、MongoDB3.2では、2つ以上のコレクションに対して左外部結合のような操作を実行できる新しい$lookup演算子が導入されています。 しかし、キャッチがあります…

MongoDB集約

$lookupは集約操作でのみ許可されます。 これらは、結果を照会、フィルタリング、グループ化する演算子のパイプラインと考えてください。 ある演算子の出力は、次の演算子の入力として使用されます。

集約は、単純なfindクエリよりも理解が難しく、一般的に実行が遅くなります。 しかし、それらは強力で、複雑な検索操作のための非常に貴重なオプションです。

集約は、例を使って説明するのが最善です。 userコレクションを使用してソーシャルメディアプラットフォームを作成していると仮定します。 すべてのユーザーの詳細を別々の文書に保存します。 例えば:

{ "_id": ObjectID("45b83bda421238c76f5c1969"), "name": "User One", "email: "[email protected]", "country": "UK", "dob": ISODate("1999-09-13T00:00:00.000Z")}

必要な数のフィールドを追加できますが、すべてのMongoDBドキュメントには一意の値を持つ_idフィールドが必要です。 _idはSQLの主キーに似ており、必要に応じて自動的に挿入されます。

私たちのソーシャルネットワークには、ユーザーからの多数の洞察に満ちた更新を格納するpostコレクションが必要です。 ドキュメントには、テキスト、日付、評価、およびそれを書いたユーザーへの参照がuser_idフィールドに格納されます:

{ "_id": ObjectID("17c9812acff9ac0bba018cc1"), "user_id": ObjectID("45b83bda421238c76f5c1969"), "date: ISODate("2016-09-05T03:05:00.123Z"), "text": "My life story so far", "rating": "important"}

ここでは、すべてのユーザーからの「重要な」評価を逆の時系列で最後の20件の投稿を表示したいと考えています。 返される各文書には、テキスト、投稿の時刻、および関連するユーザーの名前と国が含まれている必要があります。

MongoDB集約クエリには、各操作を順番に定義するパイプライン演算子の配列が渡されます。 まず、$matchフィルタを使用して、正しい評価を持つpostコレクションからすべてのドキュメントを抽出する必要があります:

{ "$match": { "rating": "important" } }

ここで、$sort演算子を使用して、一致した項目を逆の日付順にソートする必要があります:

{ "$sort": { "date": -1 } }

必要なのは20個の投稿だけなので、$limitステージを適用することができるので、MongoDBは必要なデータのみを処理する必要があります:

{ "$limit": 20 }

新しい$lookup演算子を使用して、userコレクションのデータを結合できるようになりました。 これには、四つのパラメータを持つオブジェクトが必要です:

  • localField: 入力文書内の参照項目
  • from: 参加するコレクション
  • foreignField: fromコレクション内で検索するフィールド
  • as: 出力フィールドの名前。

:

{ "$lookup": { "localField": "user_id", "from": "user", "foreignField": "_id", "as": "userinfo"} }

これにより、userinfoという名前の新しいフィールドが出力に作成されます。 これには、各値が一致するuserドキュメントである配列が含まれています:

"userinfo": 

投稿には著者が一人しかいないため、post.user_iduser._idの間には一対一の関係があります。 したがって、userinfo配列には1つの項目のみが含まれます。 $unwind演算子を使用して、それをサブドキュメントに分解することができます:

{ "$unwind": "$userinfo" }

出力は、さらに演算子を適用できるより実用的な形式に変換されます:

"userinfo": { "name": "User One", "email: "[email protected]", …}

最後に、パイプラインの$projectステージを使用して、テキスト、投稿の時刻、ユーザーの名前、国を返すことができます:

{ "$project": { "text": 1, "date": 1, "userinfo.name": 1, "userinfo.country": 1} }

すべてをまとめる

最終的な集計クエリは、投稿と一致し、順序にソートし、最新の20項目に制限し、ユーザーデータを結合し、ユーザー配列をフラット化し、必要なフィー 完全なコマンド:

db.post.aggregate();

その結果、最大20件の文書が収集されます。 例えば:

素晴らしい! 私は最終的にNoSQLに切り替えることができます!

MongoDB$lookupは便利で強力ですが、この基本的な例でさえ複雑な集約クエリが必要です。 これは、SQLで提供されるより強力なJOIN句の代替ではありません。 MongoDBはどちらも制約を提供しません。userドキュメントが削除された場合、孤立したpostドキュメントは残ります。

理想的には、$lookup演算子は頻繁に必要ではありません。 たくさん必要な場合は、間違ったデータストアを使用している可能性があります…

リレーショナルデータをお持ちの場合は、リレーショナル(SQL)データベースを使用してください。

つまり、$lookupはMongoDB3.2への歓迎された追加です。 NoSQLデータベースで少量のリレーショナルデータを使用する場合、よりイライラする問題のいくつかを克服することができます。

You might also like

コメントを残す

メールアドレスが公開されることはありません。