web-dev-qa-db-ja.com

MongoDB多対多協会

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

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

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

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

125
Josh Close

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

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

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

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

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

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

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

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

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

たとえば、「ロールのユーザー」ドメインの使用は次のとおりです。

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

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

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

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

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

これが、操作の読み取り側に関するギャップを埋めるのに役立つことを願っています。

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

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

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

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

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

29
paegun

私はちょうどこの質問に出くわしました。それは古い質問ですが、与えられた答えに記載されていない可能性をいくつか追加するのが役立つと思いました。また、ここ数年で状況が少し変わったため、SQLとNoSQLが互いに近づいていることを強調する価値があります。

解説者の1人は、「データがリレーショナルであれば、リレーショナルを使用する」という賢明な注意態度を持ち出しました。ただし、そのコメントは、スキーマが常にアプリケーションの前に来るリレーショナルの世界でのみ意味があります。

リレーショナルワールド:構造データ>アプリケーションを作成して取得する
NOSQL WORLD:アプリケーションの設計>データの構造化

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

2010年の問題に対する2015年のソリューション

この質問が投稿されて以来、noSQLをSQLに近づけようとする深刻な試みがありました。カリフォルニア大学(サンディエゴ)のYannis Papakonstantinou率いるチームは [〜#〜] 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()”]]
   }

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

別の例:常にリクエストされたすべてのロールに関する情報はありますか?

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

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

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

13
cortopy

従業員と会社が entity-object の場合、次のスキーマを使用してみてください。

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}