Search code examples
postgresqlsequelize.jseager-loading

Sequelize Eager Loading tag map association


I am trying to implement a three table solution for project/post tags.

enter image description here

These are my models:

projects.model.js

const Projects = db.sequelize.define (
    'Projects',
    {
        project_id: {
            type: Sequelize.DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true,
            allowNull: false,
        },
        
        creator_id: {
            type: Sequelize.DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: User,
                key: 'id'
            }
        },

        project_title: {
            type: Sequelize.DataTypes.STRING,
            allowNull: false
        },

        project_description: {
            type: Sequelize.DataTypes.STRING,
            allowNull: false
        },

        creation_date: {
            type: Sequelize.DataTypes.DATE,
            allowNull: false,
            defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
        },

        last_updated_date: {
            type: Sequelize.DataTypes.DATE,
            allowNull: false,
            defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
        }
    }, {
    timestamps: false
    }
)

export default Projects

project-tags.model.js

const ProjectTags = db.sequelize.define(
    'Project Tags',
    {
        id: {
            type: Sequelize.DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        },

        project_id: {
            type: Sequelize.DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: Projects,
                key: 'project_id'
            }
        },

        project_tag_id: {
            type: Sequelize.DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: Tags,
                key: 'id'
            }
        },

    }, {
    timestamps: false
});
Tags.belongsToMany(Projects, { through: ProjectTags, foreignKey: 'project_id', as: 'tags' });
Projects.belongsToMany(Tags, { through: ProjectTags, foreignKey: 'project_tag_id', as: 'tags' });

export default ProjectTags

tags.model.js

const Tags = db.sequelize.define(
    'Tags',
    {
        id: {
            type: Sequelize.DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        },

        tag: {
            type: Sequelize.DataTypes.STRING,
            allowNull: false
        }
    }, {
    timestamps: false
});

export default Tags

And I am eager loading it like that:

const projects = Projects.findAll({
            attributes: ['project_id','creator_id','project_title','project_description','creation_date','last_updated_date'],
            include: [{
                model: Tags,
                as: 'tags',
                attributes: ['tag'],
            }]
        })
        .catch(error => {
            throw new Error(error);
        })
        return projects;

It returns the project data fine, but tags is an empty array although the table is not empty and the references seem to be correct. What am I missing here?


Solution

  • You mixed up the foreign key options in associations. foreignKey option on belongsToMany should indicate the field that links the main model on which you call belongsToMany and not the one that you pass as the first argument. And of course you need to indicate different aliases (they should correspond to the passed model (as the first argument):

    Tags.belongsToMany(Projects, { through: ProjectTags, foreignKey: 'project_tag_id', 
    otherKey: 'project_id', as: 'projects' });
    Projects.belongsToMany(Tags, { through: ProjectTags, foreignKey: 'project_id', otherKey: 'project_tag_id', as: 'tags' });
    

    If you want to indicate the key for the passed model you need to use otherKey option next to foreignKey option.