O FindAll with inclui um relacionamento complicado de muitos para (muitos para muitos) (sequelizejs)

Este temuma pergunta sobre irmãos na Engenharia de Software SE.

ConsiderarCompany, Product ePerson.

Existe uma relação de muitos para muitos entreCompany eProduct, através de uma tabela de junçãoCompany_Product, porque uma determinada empresa pode produzir mais de um produto (como "carro" e "bicicleta"), mas também um determinado produto, como "carro", pode ser produzido por várias empresas. Na tabela de junçãoCompany_Product existe um campo "preço" extra, que é o preço pelo qual a empresa em questão vende o produto em questão.

Existe outra relação muitos-para-muitos entreCompany_Product ePerson, através de uma tabela de junçãoCompany_Product_Person. Sim, é um relacionamento muitos para muitos envolvendo uma entidade que já é uma tabela de junção. Isso ocorre porque uma Pessoa pode possuir vários produtos, como um carro da empresa1 e uma bicicleta da empresa2 e, por sua vez, o mesmo produto_empresa pode pertencer a mais de uma pessoa, uma vez que, por exemplo, pessoa1 e pessoa2 poderiam ter comprado um carro na empresa1 empresa1. Na tabela de junçãoCompany_Product_Person existe um campo extra "pensamentos" que contém os pensamentos da pessoa no momento em que eles compraram o produto da empresa.

Eu quero fazer uma consulta comsequestrar para obter do banco de dados todas as instâncias deCompany, com todos os relacionadosProducts com o respectivoCompany_Product que por sua vez incluem todos osPersons com o respectivoCompany_Product_Persons.

Obter os elementos de ambas as tabelas de junção também é importante, porque os campos "preço" e "pensamentos" são importantes.

E não consegui descobrir como fazer isso.

Fiz o código o mais curto possível para investigar isso.Parece grande, mas a maioria é modelo de declaração de modelo: (para executá-lo, primeiro façanpm 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));
        });
    });

Meu objetivo é obter uma variedade deCompanys já com todo o profundo aninhadoPersons eCompany_Product_Persons de uma só 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": "..."
                            }
                        }
                    ]
                }
            }
        ]
    }
];

Como posso fazer isso?

Nota: Eu poderia fazer as duas consultas separadamente e escrever algum código para "juntar" os objetos recuperados, mas isso seria computacionalmente caro e feio. Eu estou procurando o caminho certo para fazer isso.

questionAnswers(1)

yourAnswerToTheQuestion