gpt4 book ai didi

javascript - FindAll 包含涉及复杂的多对(多对多)关系(sequelizejs)

转载 作者:搜寻专家 更新时间:2023-10-31 23:45:04 25 4
gpt4 key购买 nike

这有a sibling question in Software Engineering SE .

考虑 Company , ProductPerson .
Company之间存在多对多关系和 Product ,通过一个接线台Company_Product ,因为一个给定的公司可能生产不止一种产品(比如“汽车”和“自行车”),但是一个给定的产品,比如“汽车”,可以由多个公司生产。中联表Company_Product有一个额外的字段“价格”,它是给定公司销售给定产品的价格。
Company_Product之间还有另一种多对多关系和 Person ,通过一个接线台Company_Product_Person .是的,这是一种多对多关系,涉及一个已经是联结表的实体。这是因为一个人可以拥有多种产品,例如公司 1 的汽车和公司 2 的自行车,而同一个 company_product 可以由多个人拥有,因为例如,person1 和 person2 都可以从公司1.中联表Company_Product_Person有一个额外的字段“想法”,其中包含该人购买 company_product 时的想法。

我想用 sequelize 查询从数据库中获取 Company 的所有实例, 与所有相关 Products与各自的 Company_Product依次包括所有相关 Persons与各自的 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));
});
});

我的目标是获得一个数组 Companys已经有了所有的深嵌套PersonsCompany_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" });

更改 return 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 , 也与其他表本身有关系。

  • 严格来说,这在技术上并没有错,但是有一种更自然的方式来构建数据库以表示相同的事物。更好的是,使用这种新方法,使我想要的查询变得非常简单。

    解决方案:新方法

    当我们看到我们正在对可以购买的元素和购买本身进行建模时,解决方案就会出现。我们不会将这些信息“伪装”在多对多关系的连接表中,而是将它们作为我们方案中的显式实体,并具有自己的表。

    所以,首先,为了澄清,让我们重命名我们的模型:
  • Company住宿 Company
  • Product变成 ProductType
  • Company_Product变成 Product
  • Person住宿 Person
  • Company_Product_Person变成 Purchase

  • 然后我们看到:
  • A Product有一个 Company和一个 ProductType .反之,同Company可以关联多个Product和相同的 ProductType可以关联多个Product .
  • A Purchase有一个 Product和一个 Person .反之,同Product可以关联多个Purchase和相同的 Product可以关联多个Person .

  • 请注意,不再有多对多关系。关系变为:
    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 逻辑的问题,不再是涉及数据库或 Sequelize 的问题。

    结论

    虽然问题中提供的数据库方案本身在技术上没有错误,但通过稍微改变方案找到了解决方案。

    我们没有使用不仅仅是简单连接表的连接表,而是摆脱了多对多关系并将连接表“提升”为我们方案的成熟实体。事实上,表是一样的;变化只是在关系和看待它们的方式上。

    关于javascript - FindAll 包含涉及复杂的多对(多对多)关系(sequelizejs),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48713019/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com