Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting join with sequelize with multiple includes

I'm using the count method of mysql in sequelize but when I have multiple nested includes the count attribute makes the request returns less results. The problem is the same with the sum method.

I tried with the attributes group and duplicate but none of them resolved the problem.

I'm using Sequelize 4.33.4 but I tried with Sequelize 5 and the result is still the same.

Model :

// User
module.exports = function(sequelize, DataTypes) {
    var User = sequelize.define('User', {
        userId: {
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        // ...
    }, {
        tableName: 'user'
    });

    User.associate = function (models) {
        models.User.hasMany(models.Proposition, {foreignKey: 'userId'});
        models.User.belongsToMany(models.Proposition, {as: 'Fan', through: 'like', foreignKey: 'userId'});
    };

    return User;
};

// Proposition
module.exports = function(sequelize, DataTypes) {
    const Proposition = sequelize.define('Proposition', {
        propositionId: {
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        photo: DataTypes.STRING(150),
    }, {
        tableName: 'proposition'
    });

    Proposition.associate = function (models) {
        models.Proposition.belongsTo(models.Challenge, {foreignKey: 'challengeId'});
        models.Proposition.belongsTo(models.User, {foreignKey: 'userId'});
        models.Proposition.belongsToMany(models.User, {as: 'Fan', through: 'like', foreignKey: 'propositionId'});
    };

    return Proposition;
};

// Challenge
module.exports = function(sequelize, DataTypes) {
    const Challenge = sequelize.define('Challenge', {
        challengeId: {
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        name: DataTypes.STRING(100),
        startDate: DataTypes.DATE,
        endDate: DataTypes.DATE,
        image: DataTypes.STRING(150),
        description: DataTypes.STRING(256),
    }, {
        tableName: 'challenge'
    });

    Challenge.associate = function (models) {
        models.Challenge.hasMany(models.Proposition, {foreignKey: 'challengeId'});
    };

    return Challenge;
};

Controller :

exports.getCurrentChallenge = function (req, res) {
    logger.debug('Start getCurrentChallenge');
    models.Challenge.findOne({
        where: {
            startDate: {
                [Op.lte]: new Date()
            },
            endDate: {
                [Op.gte]: new Date()
            }
        },
        include: [{
            model: models.Proposition,
            include: [{
                model: models.User,
                attributes: ['userId', 'firstname', 'lastname', 'profilePicture']
            }, {
                model: models.User,
                as: 'Fan',
                attributes: []
            }],
            required: false,
            attributes: [
                'propositionId',
                [models.sequelize.literal("CONCAT('"+ propositionsPicturesPath + "', `Propositions`.`photo`)"), 'photo'],
                [models.sequelize.literal('COUNT(`Propositions->Fan->like`.`userId`)'), 'likes'], // Show less results
                [models.sequelize.literal('IFNULL(SUM(`Propositions->Fan->like`.`userId` = ' + req.decoded.userId + '), 0)'), 'liked'] // Show less results
            ],
            group: '`Propositions->Fan->like`.`propositionId`',
        }]
    }).then(function (challenge) {
        return res.status(200).send(challenge);
    }, function (err) {
        logger.debug(err.name, err.message);
        return res.sendStatus(500);
    });
};

The request only returns the first proposition (the one with the lowest propositionId) when I have the 2 attributes. When I remove them it sends me all the results.

like image 753
Simon Avatar asked Jan 22 '26 10:01

Simon


1 Answers

a) Your results suggest that the nested includes should be outer joins .... so adding required: false (shown below) might fix this.

b) Sequelize might be mixing up the two associations from Proposition to User. You might try adding an alias to the belongsTo, e.g. models.Proposition.belongsTo(models.User, {as: 'whatever', foreignKey: 'userId'}); and use the alias in the find:

...
include: [{
            model: models.Proposition,
            include: [{
                model: models.User,
                as: 'whatever',         /**** b) ****/
                required: false,        /**** a) #1 ****/ 
                attributes: ['userId', 'firstname', 'lastname', 'profilePicture']
            }, {
                model: models.User,
                as: 'Fan',
                required: false,        /**** b) #2 ****/ 
                attributes: []
            }],
            required: false,   /** this one is for challenge -> proposition **/
 ...

Hope this helps....

like image 86
KenOn10 Avatar answered Jan 24 '26 08:01

KenOn10



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!