javascript-FindAll及其包括涉及复杂的多对多(多对多)关系(sequelizejs)
作者:互联网
这有a sibling question in Software Engineering SE.
考虑公司,产品和人员.
通过联结表Company_Product,公司与产品之间存在多对多关系,因为给定的公司可能会生产多个产品(例如“汽车”和“自行车”),也可能会生产给定的产品,例如“汽车”,可以由多家公司生产.在联结表Company_Product中,有一个额外的字段“ price”,它是给定公司出售给定产品的价格.
通过联结表Company_Product_Person,Company_Product和Person之间存在另一对多关系.是的,这是一个涉及一个实体的多对多关系,该实体已经是一个联结表.这是因为一个人可以拥有多个产品,例如,公司1的汽车和公司2的自行车,而同一公司产品又可以由一个以上的人拥有,因为例如person1和person2都可以从公司1.在联结表Company_Product_Person中,有一个额外的字段“ thoughts”,其中包含该人在购买company_product时的想法.
我想使用sequelize进行查询,以从数据库中获取Company的所有实例,以及具有相关Company_Product的所有相关产品,而这些产品又包括具有Company_Product_Persons的所有相关人员.
获取两个联结表的元素也很重要,因为“价格”和“想法”字段很重要.
而且我不知道该怎么做.
我将代码做得尽可能短以进行调查.看起来很大,但其中大多数是模型声明样板:(要运行它,请先执行npm install sequelize sqlite3)
const Sequelize = require("sequelize");
const sequelize = new Sequelize({ dialect: "sqlite", storage: "db.sqlite" });
// ================= MODELS =================
const Company = sequelize.define("company", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
name: Sequelize.STRING
});
const Product = sequelize.define("product", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
name: Sequelize.STRING
});
const Person = sequelize.define("person", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
name: Sequelize.STRING
});
const Company_Product = sequelize.define("company_product", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
companyId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "company",
key: "id"
},
onDelete: "CASCADE"
},
productId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "product",
key: "id"
},
onDelete: "CASCADE"
},
price: Sequelize.INTEGER
});
const Company_Product_Person = sequelize.define("company_product_person", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
companyProductId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "company_product",
key: "id"
},
onDelete: "CASCADE"
},
personId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "person",
key: "id"
},
onDelete: "CASCADE"
},
thoughts: Sequelize.STRING
});
// ================= RELATIONS =================
// Many to Many relationship between Company and Product
Company.belongsToMany(Product, { through: "company_product", foreignKey: "companyId", onDelete: "CASCADE" });
Product.belongsToMany(Company, { through: "company_product", foreignKey: "productId", onDelete: "CASCADE" });
// Many to Many relationship between Company_Product and Person
Company_Product.belongsToMany(Person, { through: "company_product_person", foreignKey: "companyProductId", onDelete: "CASCADE" });
Person.belongsToMany(Company_Product, { through: "company_product_person", foreignKey: "personId", onDelete: "CASCADE" });
// ================= TEST =================
var company, product, person, company_product, company_product_person;
sequelize.sync({ force: true })
.then(() => {
// Create one company, one product and one person for tests.
return Promise.all([
Company.create({ name: "Company test" }).then(created => { company = created }),
Product.create({ name: "Product test" }).then(created => { product = created }),
Person.create({ name: "Person test" }).then(created => { person = created }),
]);
})
.then(() => {
// company produces product
return company.addProduct(product);
})
.then(() => {
// Get the company_product for tests
return Company_Product.findAll().then(found => { company_product = found[0] });
})
.then(() => {
// person owns company_product
return company_product.addPerson(person);
})
.then(() => {
// I can get the list of Companys with their Products, but couldn't get the nested Persons...
return Company.findAll({
include: [{
model: Product
}]
}).then(companies => {
console.log(JSON.stringify(companies.map(company => company.toJSON()), null, 4));
});
})
.then(() => {
// And I can get the list of Company_Products with their Persons...
return Company_Product.findAll({
include: [{
model: Person
}]
}).then(companyproducts => {
console.log(JSON.stringify(companyproducts.map(companyproduct => companyproduct.toJSON()), null, 4));
});
})
.then(() => {
// I should be able to make both calls above in one, getting those nested things
// at once, but how??
return Company.findAll({
include: [{
model: Product
// ???
}]
}).then(companies => {
console.log(JSON.stringify(companies.map(company => company.toJSON()), null, 4));
});
});
我的目标是一次性获得具有所有深层人员和Company_Product_Persons的一系列公司:
// My goal:
[
{
"id": 1,
"name": "Company test",
"createdAt": "...",
"updatedAt": "...",
"products": [
{
"id": 1,
"name": "Product test",
"createdAt": "...",
"updatedAt": "...",
"company_product": {
"id": 1,
"companyId": 1,
"productId": 1,
"price": null,
"createdAt": "...",
"updatedAt": "...",
"persons": [
{
"id": 1,
"name": "Person test",
"createdAt": "...",
"updatedAt": "...",
"company_product_person": {
"id": 1,
"companyProductId": 1,
"personId": 1,
"thoughts": null,
"createdAt": "...",
"updatedAt": "..."
}
}
]
}
}
]
}
];
我怎样才能做到这一点?
注意:我可以分别进行两个查询,并编写一些代码以“联接”检索到的对象,但这将在计算上昂贵且丑陋.我正在寻找正确的方法来做到这一点.
解决方法:
在这里操作.
简短答案
解决方案的关键是重新考虑关联.将关联更改为:
Company.hasMany(Company_Product, { foreignKey: "companyId" });
Company_Product.belongsTo(Company, { foreignKey: "companyId" });
Product.hasMany(Company_Product, { foreignKey: "productId" });
Company_Product.belongsTo(Product, { foreignKey: "productId" });
Company_Product.hasMany(Company_Product_Person, { foreignKey: "companyProductId" });
Company_Product_Person.belongsTo(Company_Product, { foreignKey: "companyProductId" });
Person.hasMany(Company_Product_Person, { foreignKey: "personId" });
Company_Product_Person.belongsTo(Person, { foreignKey: "personId" });
更改退货company.addProduct(product);至
return Company_Product.create({
companyId: company.id,
productId: product.id,
price: 99
}).then(created => { company_product = created });
将return company_product.addPerson(person)更改为
return Company_Product_Person.create({
companyProductId: company_product.id,
personId: person.id,
thoughts: "nice"
}).then(created => { company_product_person = created });
回答问题的查询是
Company.findAll({
include: [{
model: Company_Product,
include: [{
model: Product
}, {
model: Company_Product_Person,
include: [{
model: Person
}]
}]
}]
})
生成的JSON结构不完全是所提到的“目标”,而仅仅是重新排序的问题.
长答案
我找到了一个解决方案,其中涉及重新处理表之间的关联,即使问题中给出的关联在技术上没有错.解决问题的一种新方法是更改关联,这是找到一种实现我想要的方法的关键.
分析旧方法
首先,我的问题中给出的两个联结表都不仅仅是“只是”联结表.它们不仅是定义哪些元素与哪些元素相关的工具,还更多:
>他们还具有其他信息(分别为“价格”和“思想”字段);
>第一个公司Company_Product也与其他表本身有关系.
从严格意义上讲,这在技术上并没有错,但是存在一种更自然的方法来构造数据库以表示相同的事物.更好的是,使用这种新方法,使我想要的查询变得非常简单.
解决方案:新方法
当我们看到我们正在建模可以购买的物品以及自己购买的物品时,解决方案就会出现.与其将这些信息“伪装”在多对多关系的联结表内,不如将它们作为自己的表中的显式实体作为方案中的显式实体.
因此,首先要澄清一下,让我们重命名模型:
>公司留在公司
>产品成为ProductType
>公司_产品成为产品
>人住人
>公司_产品_人员成为采购人员
然后我们看到:
>产品具有一个公司和一个产品类型.相反,同一公司可以与多个产品相关,而同一产品类型可以与多个产品相关.
>一件商品有一个产品和一个人.相反,同一产品可以与多个购买相关,而同一产品可以与多个人相关.
请注意,不再存在多对多关系.关系变为:
Company.hasMany(Product, { foreignKey: "companyId" });
Product.belongsTo(Company, { foreignKey: "companyId" });
ProductType.hasMany(Product, { foreignKey: "productTypeId" });
Product.belongsTo(ProductType, { foreignKey: "productTypeId" });
Product.hasMany(Purchase, { foreignKey: "productId" });
Purchase.belongsTo(Product, { foreignKey: "productId" });
Person.hasMany(Purchase, { foreignKey: "personId" });
Purchase.belongsTo(Person, { foreignKey: "personId" });
然后,旧的company.addProduct(product);变成
Product.create({
companyId: company.id
productTypeId: productType.id,
price: 99
})
类似于company_product.addPerson(person);变成
Purchase.create({
productId: product.id,
personId: person.id,
thoughts: "nice"
})
现在,我们可以轻松地看到进行所需查询的方式:
Company.findAll({
include: [{
model: Product,
include: [{
model: ProductType
}, {
model: Purchase,
include: [{
model: Person
}]
}]
}]
})
上面查询的结果不是100%等同于问题中提到的“目标”,因为交换了Product和ProductType的嵌套顺序(Person和Purchase也是如此),但是现在转换为所需的结构只是一个编写一些javascript逻辑的问题,而不再是涉及数据库或续集的问题.
结论
尽管问题中提供的数据库方案本身在技术上并没有错,但通过稍微更改方案即可找到解决方案.
我们不再使用简单的联结表以外的联结表,而是摆脱了多对多关系,并将联结表“提升”为方案的成熟实体.实际上,这些表是相同的.变化只是在关系和看待它们的方式上.
标签:eager-loading,sequelize-js,node-js,many-to-many,javascript 来源: https://codeday.me/bug/20191109/2013029.html