FindAll incluye incluye una relación complicada de muchos a muchos (secuela)

Esto tieneuna pregunta entre hermanos en Software Engineering SE.

ConsiderarCompany, Product yPerson.

Hay una relación de muchos a muchos entreCompany yProduct, a través de una mesa de conexionesCompany_Product, porque una empresa determinada puede producir más de un producto (como "automóvil" y "bicicleta"), pero también un producto determinado, como "automóvil", puede ser producido por varias empresas. En la mesa de conexionesCompany_Product hay un "precio" de campo adicional, que es el precio en el cual la compañía dada vende el producto dado.

Hay otra relación de muchos a muchos entreCompany_Product yPerson, a través de una mesa de conexionesCompany_Product_Person. Sí, es una relación de muchos a muchos que involucra a una entidad que ya es una tabla de unión. Esto se debe a que una persona puede tener múltiples productos, como un automóvil de la compañía1 y una bicicleta de la compañía2, y a su vez, el mismo producto de la compañía puede ser propiedad de más de una persona, ya que, por ejemplo, tanto la persona1 como la persona2 podrían haber comprado un automóvil empresa1. En la mesa de conexionesCompany_Product_Person hay un campo adicional "pensamientos" que contiene los pensamientos de la persona en el momento en que compró el producto de la compañía.

Quiero hacer una consulta consecuenciar para obtener de la base de datos todas las instancias deCompany, con todo lo relacionadoProducts con el respectivoCompany_Product que a su vez incluyen todos los relacionadosPersons con el respectivoCompany_Product_Persons.

Obtener los elementos de ambas tablas de unión también es importante, porque los campos "precio" y "pensamientos" son importantes.

Y no pude averiguar cómo hacer esto.

Hice el código lo más corto que pude para investigar esto.Parece grande, pero la mayor parte es un modelo de declaración: (para ejecutarlo, primero haznpm 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));
        });
    });

Mi objetivo es obtener una variedad deCompanys ya con todos los anidados profundosPersons yCompany_Product_Persons de una vez:

// 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": "..."
                            }
                        }
                    ]
                }
            }
        ]
    }
];

¿Cómo puedo hacer esto?

Nota: Podría hacer ambas consultas por separado y escribir un código para "unir" los objetos recuperados, pero eso sería computacionalmente costoso y feo. Estoy buscando la forma correcta de hacer esto.

Respuestas a la pregunta(1)

Su respuesta a la pregunta