別名「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が読み取り専用のベンダーデータベースに接続されている可能性があります)、書き込みできないため、関係データが安全に反対側に永続化されることを確認する必要があります。