MongoDB多対多協会


143

MongoDBと多対多の関連付けをどのように行いますか?

例えば; たとえば、UsersテーブルとRolesテーブルがあるとします。ユーザーには多くの役割があり、役割には多くのユーザーがいます。SQLランドでは、UserRolesテーブルを作成します。

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

MongoDBで同じ種類の関係はどのように処理されますか?


回答:


96

クエリのニーズに応じて、ユーザードキュメントにすべてを入れることができます。

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

すべてのエンジニアを取得するには、以下を使用します。

db.things.find( { roles : "Engineer" } );

別のドキュメントでロールを維持する場合は、名前の代わりにドキュメントの_idをロール配列に含めることができます。

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

次のような役割を設定します。

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}

7
利用可能なすべてのロールのリストを取得する必要があるため、後者の方が適しています。唯一の悪い点は、関連付けの両端をセットアップする必要があることです。SQLの方法で行う場合、UserRoleを追加すると、ユーザーはロールを認識し、ロールはユーザーを認識します。この方法では、ユーザーにロールを設定し、ロールにユーザーを設定する必要があります。私はそれでも大丈夫だと思います。
Josh Close

46
データベースがSQLをサポートしていないからといって、参照が有用なツールではないという意味ではありませんNoSQL!= NoReferenceこの説明を参照してください:mongodb.org/display/DOCS/Schema+Design
Tom Gruner

8
これは良い考えではないようです。役割が6つしかない場合は確かですが、20000以上のオブジェクトにリンクできる20000オブジェクトが(多対多の関係で)ある場合はどうでしょうか。MongoDBのドキュメントでさえ、参照の可変で巨大な配列を持つことは避けるべきであると示唆しています。docs.mongodb.org/manual/tutorial/...
CaptSaltyJack

明らかに、多数のオブジェクトとの多対多の関係では、別のソリューションを使用する必要があります(ドキュメントの出版社/本の例など)。この場合、問題なく機能し、ユーザーロールドキュメントを個別に作成した場合にのみ問題が複雑になります。
diederikh 2015

1
これは、ほとんどのシステムで機能します。通常、cozの役割は小さなセットであり、通常はユーザーを取得して、その役割を調べます。しかし、役割が大きい場合はどうなりますか?または、ロール== "エンジニア"を持つユーザーのリストを提供するように依頼した場合はどうなりますか?これで、ユーザーコレクション全体(エンジニアの役割も持たないすべてのユーザーにアクセスする)をクエリして、たとえば100万人のユーザーの中でこの役割を持つ可能性のある2人または3人のユーザーを取得する必要があります。個別のテーブルまたはコレクションの方がはるかに優れています。
プログラマー

31

RDBMSに関する長年の経験に基づいてモデル化を試みる代わりに、アトミックを考慮しながら、読み取りのユースケースを最適化することで、MongoDB、Redis、およびその他のNoSQLデータストアを使用してドキュメントリポジトリソリューションをモデル化する方がはるかに簡単であることがわかりました書き込みユースケースでサポートする必要がある書き込み操作。

たとえば、「Users in Roles」ドメインの使用法は次のとおりです。

  1. 役割-ユーザーの作成、読み取り、更新、削除、一覧表示、ユーザーの追加、ユーザーの削除、すべてのユーザーの削除、ユーザーのインデックス、または「役割内のユーザー」(コンテナー+独自のメタデータなどの操作)をサポートするための同様のもの。
  2. ユーザー-作成、読み取り、更新、削除(独立したエンティティのようなCRUD操作)

これは、次のドキュメントテンプレートとしてモデル化できます。

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

UserエンティティのRole関連の機能などの高頻度の使用をサポートするために、User.Rolesは意図的に非正規化され、UserとRole.Usersに重複して保存されます。

テキストではすぐにはわからないが、これがドキュメントリポジトリを使用するときに推奨されるタイプの考え方である場合。

これにより、操作の読み取り側に関するギャップを埋めることができます。

書き込み側では、アトミックな書き込みに従ってモデル化することが推奨されます。たとえば、ドキュメント構造がロックを取得し、1つのドキュメントを更新し、次に別のドキュメントを更新し、場合によってはさらに多くのドキュメントを更新し、ロックを解放する必要がある場合、モデルが失敗した可能性があります。分散ロックを構築できるからといって、それらを使用することになっているわけではありません。

ロール内のユーザーモデルの場合、ロックのアトミックな書き込み回避を拡張する操作は、ロールからユーザーを追加または削除することです。どちらの場合も、操作が成功すると、単一のユーザーと単一のロール文書の両方が更新されます。何かが失敗した場合、クリーンアップを実行するのは簡単です。これは、ドキュメントリポジトリが使用される作業ユニットパターンがかなり多く登場する1つの理由です。

ロックのアトミックな書き込み回避を実際に拡張する操作はRoleをクリアすることであり、User.roles配列からRole.nameを削除するために多くのユーザー更新が発生します。この場合、このクリア操作は通常推奨されませんが、必要に応じて、操作を注文することで実装できます。

  1. Role.usersからユーザー名のリストを取得します。
  2. 手順1のユーザー名を繰り返して、User.rolesからロール名を削除します。
  3. Role.usersをクリアします。

手順2で発生する可能性が最も高い問題の場合、手順1と同じユーザー名のセットを使用して回復または続行できるため、ロールバックは簡単です。


15

私はこの質問に出くわしました。古い質問ですが、回答に記載されていない可能性をいくつか追加すると便利だと思いました。また、ここ数年で状況は少し変化しているため、SQLとNoSQLが互いに近づいていることを強調する価値があります。

コメンターの一人は、「データがリレーショナルである場合はリレーショナルを使用する」という賢明な注意姿勢を示しました。ただし、そのコメントは、スキーマが常にアプリケーションの前にあるリレーショナルの世界でのみ意味があります。

RELATIONAL WORLD:Structure data> Write application to get it
NOSQL WORLD:Design application> Structure dataそれに応じて

データがリレーショナルであっても、NoSQLはオプションです。たとえば、1 対多の関係はまったく問題なく、MongoDBのドキュメントで広くカバーされています

2010年の問題に対する2015年の解決策

この質問が投稿されて以来、noSQLをSQLに近づけようとする深刻な試みがありました。カリフォルニア大学サンディエゴ校のYannis Papakonstantinou率いるチームは、SQL ++の実装であるFORWARDに取り組んでいます。これは、ここに掲載されているような永続的な問題の解決策となる可能性があります。

より実用的なレベルでは、Couchbase 4.0のリリースにより、初めてNoSQLでネイティブJOINを実行できるようになりました。彼らは独自のN1QLを使用します。これはJOIN彼らのチュートリアルからの例です:

SELECT usr.personal_details, orders 
        FROM users_with_orders usr 
            USE KEYS "Elinor_33313792" 
                JOIN orders_with_users orders 
                    ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

N1QLは、集約、フィルタリングなど、すべてではないにしてもほとんどのSQL操作を可能にします。

それほど新しいものではないハイブリッドソリューション

それでもMongoDBが唯一のオプションである場合は、アプリケーションがデータの構造よりも優先される必要があるという私の考えに戻りたいと思います。いずれの回答もハイブリッド埋め込みについて言及していないため、ほとんどのクエリされたデータはドキュメント/オブジェクトに埋め込まれ、参照は少数のケースで保持されます。

例:情報(ロール名以外)は待機できますか?ユーザーがまだ必要としないものを要求しないことで、アプリケーションのブートストラップはより速くなるでしょうか?

これは、ユーザーがログインし、ユーザーが所属するすべてのロールのすべてのオプションを表示する必要がある場合に当てはまります。ただし、ユーザーは「エンジニア」であり、このロールのオプションはほとんど使用されません。つまり、エンジニアは、エンジニアがオプションをクリックしたい場合に備えて、オプションを表示するだけで済みます。

これは、最初にアプリケーションに(1)ユーザーが属するロールと(2)特定のロールにリンクされたイベントに関する情報を取得する場所を通知するドキュメントで実現できます。

   {_id: ObjectID(),
    roles: [[“Engineer”, ObjectId()”],
            [“Administrator”, ObjectId()”]]
   }

または、さらに良いのは、ロールコレクションのrole.nameフィールドにインデックスを付けることで、ObjectID()を埋め込む必要がない場合もあります。

別の例:常に要求されるすべての役割に関する情報はありますか?

また、ユーザーがダッシュボードにログインし、90%の時間が「エンジニア」ロールにリンクされたタスクを実行する場合もあります。ハイブリッド埋め込みは、その特定の役割に対して完全に実行され、残りの参照のみを保持します。

{_id: ObjectID(),
  roles: [{name: Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, ObjectId()”]
         ]
}

スキーマレスであることは、NoSQLの特性だけではなく、この場合の利点にもなります。ユーザーオブジェクトの「Roles」プロパティに異なるタイプのオブジェクトをネストすることは完全に有効です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.