別名「Has and Belongs To Many」
多対多アソシエーションは、1つのレコードが多くの他のレコードと関連付けられ、その逆もまた然りであることを示します。このタイプの関係には、レコード間の多くのリンクを追跡するための結合テーブルの作成が含まれます。Waterline は、2 つのモデルが、それらのvia
キー(下記参照)を通して互いを指すコレクション属性を持つことを検出すると、自動的に結合テーブルを作成します。
via
キーモデルが別のモデルに対して複数の多対多アソシエーションを持つことができるため、collection
属性にvia
キーが必要です。via
キーは、多対多アソシエーションの反対側の関連属性を示します。
User
とPet
の例を使用して、User
が多くのPet
レコードを持つことができ、Pet
が複数の飼い主を持つことができるスキーマの構築方法を見てみましょう。
// myApp/api/models/User.js
// A user may have many pets
module.exports = {
attributes: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
// Add a reference to Pet
pets: {
collection: 'pet',
via: 'owners'
}
}
};
// myApp/api/models/Pet.js
// A pet may have many owners
module.exports = {
attributes: {
breed: {
type: 'string'
},
type: {
type: 'string'
},
name: {
type: 'string'
},
// Add a reference to User
owners: {
collection: 'user',
via: 'pets'
}
}
};
レコードを関連付けるには、Modelメソッド.addToCollection()を使用します。これにより、関連付けられるレコードの主キーを設定できます。
// To add a Pet to a user's `pets` collection where the User has an id of
// 10 and the Pet has an id of 300.
await User.addToCollection(10, 'pets', 300);
複数のペットを一度に追加することもできます。
await User.addToCollection(10, 'pets', [300, 301]);
.removeFromCollection()メソッドを使用して、関連付けを削除することも同様に簡単です。addToCollection
と同様に機能します。
// To remove a User from a pet's collection of owners where the User has an id of
// 10 and the Pet has an id of 300.
await Pet.removeFromCollection(300, 'owners', 10);
複数の飼い主を一度に削除することもできます。
await Pet.removeFromCollection(300, 'owners', [10, 12]);
多対多関係の一方の側から関連レコードを追加または削除すると、もう一方の側にも自動的に影響します。たとえば、.addToCollection()
を使用してUser
モデルレコードのpets
属性にレコードを追加すると、リンクされたPet
レコードのowners
属性がすぐに影響を受けます。
.find()
または.findOne()
で取得したレコードと共に関連コレクションを返すには、.populate()
メソッドを使用します。
ほとんどの場合、Sailsはユーザーからの入力なしで多対多アソシエーションの結合テーブルを作成できます。ただし、アソシエーション内の2つのモデルが異なるデータストアを使用する場合は、結合テーブルをどのデータストアに含めるかを選択する必要がある場合があります。これは、関係のいずれかのアソシエーションの1つにdominant: true
を設定することで実行できます。
次のモデルを考えてみましょう
// User.js
module.exports = {
datastore: 'ourMySQL',
attributes: {
email: 'string',
wishlist: {
collection: 'product',
via: 'wishlistedBy'
}
}
};
// Product.js
module.exports = {
datastore: 'ourRedis',
attributes: {
name: 'string',
wishlistedBy: {
collection: 'user',
via: 'wishlist'
}
}
};
この場合、User
とProduct
のレコードは異なるデータベースに存在します。デフォルトでは、SailsはUser
のwishlist
属性をProduct
のwishlistedBy
属性にリンクする結合テーブルを含むデータストア(ourMySQL
またはourRedis
のいずれか)を任意に選択します。結合テーブルをourMySQL
データストアに強制的に存在させるには、wishlist
属性定義にdominant: true
を追加します。逆に、wishlistedBy
属性にdominant: true
を追加すると、結合テーブルがourRedis
データストアに作成されます。
結合テーブルを作成する場所の決定には、いくつかの要因が影響する可能性があります。
dominant
として設定することを選択できます。同様に、規制またはコンプライアンスの問題も決定に影響を与える可能性があります。関係に機密性の高い患者情報が含まれている場合(たとえば、Patient
とMedicine
の関係)、すべての関連データが他のデータベースではなく特定のデータベースに保存されていることを確認する必要があります(この場合、Patient
がdominant
である可能性が高いです)。Medicine
が読み取り専用のベンダーデータベースに接続されている可能性があります)、書き込みできないため、関係データが安全に反対側に永続化されることを確認する必要があります。