【问题标题】:Sequelize returning one result when counting AVG in Eager LoadingSequelize 在 Eager Loading 中计算 AVG 时返回一个结果
【发布时间】:2021-08-18 19:02:54
【问题描述】:

我已经尝试解决这个问题一天了,但没有成功。我正在使用带有 Nodejs 和 MySQL 方言的 Sequelize。

我正在查询影响者,同时还根据 InfluencerRating 记录计算他们的平均评分。它们通过 oneToMany 关系关联。

这是我的影响者模式:

const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
  return sequelize.define('influencer', {
    id: {
      autoIncrement: true,
      type: DataTypes.INTEGER,
      allowNull: false,
      primaryKey: true
    },
    display_name: {
      type: DataTypes.STRING(255),
      allowNull: false
    },
    link_facebook: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_soundcloud: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_twitter: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_bandcamp: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_spotify: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_instagram: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_deezer: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_youtube: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    link_website: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    description_english: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    description_french: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    information_english: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    information_french: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    enabled: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: 0
    },
    profile_image: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    banner_image: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
    admin_public_status: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: 1
    }
  }, {
    sequelize,
    tableName: 'influencer',
    timestamps: false,
    underscored: true,
    indexes: [
      {
        name: "PRIMARY",
        unique: true,
        using: "BTREE",
        fields: [
          { name: "id" },
        ]
      },
    ]
  });
};

这是我的 InfluencerRating 模式:

module.exports = function(sequelize, DataTypes) {
  return sequelize.define('influencer_rating', {
    id: {
      autoIncrement: true,
      type: DataTypes.INTEGER,
      allowNull: false,
      primaryKey: true
    },
    description: {
      type: DataTypes.STRING(255),
      allowNull: false
    },
    influencer_id: {
      type: DataTypes.INTEGER,
      allowNull: false,
      references: {
        model: 'influencer',
        key: 'id'
      }
    },
    rating: {
      type: DataTypes.INTEGER,
      allowNull: false
    }
  }, {
    sequelize,
    tableName: 'influencer_rating',
    timestamps: false,
    underscored: true,
    indexes: [
      {
        name: "PRIMARY",
        unique: true,
        using: "BTREE",
        fields: [
          { name: "id" },
        ]
      },
      {
        name: "type_id",
        using: "BTREE",
        fields: [
          { name: "influencer_id" },
        ]
      },
    ]
  });
};

这是我对它们的关联:

influencer_rating.belongsTo(influencer, { as: "influencer", foreignKey: "influencer_id"});
influencer.hasMany(influencer_rating, { as: "influencer_ratings", foreignKey: "influencer_id"});

这是我查找影响者并计算他们的平均评分的查询:

var influencers = await Influencer.findAll(
        {
            where: {
                [db.Op.and]: [
                    {
                        enabled: true
                    },
                    {
                        display_name: {
                            [db.Op.like]: '%' + filter + '%'
                        }
                    },
                    {
                        admin_public_status: true
                    }
                ],
            },
            include: [
              [
                {
                    model: InfluencerRating,
                    as: "influencer_ratings",
                    required: false,
                    attributes: [[Sequelize.fn('AVG', Sequelize.col('rating')), 'rating']]
                }
            ],
            limit: 10,
            offset: 0,
            group: ['id']
        }
    );

查询应该返回 2 个影响者对象,但我只得到一个。

如果我删除我的 :

,我会得到想要的行为
attributes: [[Sequelize.fn('AVG', Sequelize.col('rating')), 'rating']]

我已尝试计算我的 InfluencerRating 包括但不包括运气之外的平均值。

【问题讨论】:

    标签: mysql node.js sequelize.js eager-loading


    【解决方案1】:

    对 findAll 进行了一些更改。可以试试看:

    var influencers = await Influencer.findAll({
      // whenever hasMany used in include, the main query gets converted to subquery 
      // if limit + offset is applied
      subQuery: false, // <----- instructs not to make a sub query 
      attributes: {
        include: [
          [Sequelize.fn('AVG', Sequelize.col('influencer_ratings.rating')), 'total_rating']
        ],
      },
      where: {
        [db.Op.and]: [
          { enabled: true },
          { display_name: { [db.Op.like]: '%' + filter + '%' } },
          { admin_public_status: true }
        ],
      },
      include: [ // removed extra nesting of array
        {
          model: InfluencerRating,
          as: "influencer_ratings",
          required: false,
          attributes: [],
        }
      ],
      limit: 10,
      offset: 0,
      group: [
        'id',
        // 'influencer_ratings.influencer_id', // add if sql error occurs
      ],
      order: [ // if needed
        [Sequelize.literal('total_rating'), 'DESC'],
      ]
    });
    

    几个参考链接:

    替代方法:

    var influencers = await Influencer.findAll({
      attributes: {
        include: [
          [Sequelize.literal(`(
            SELECT
                AVG("influencer_ratings"."rating") AS "avg_rating"
            FROM
                "influencer" AS "influencer1"
                LEFT OUTER JOIN (
                    "influencer_ratings" AS "influencer_ratings"
                ) ON "influencer1"."id" = "influencer_ratings"."influencer_id"
            WHERE
                "influencer1"."id" = "influencer"."id"
            GROUP BY
                "influencer1"."id"
        )`), 'avg_rating'], // now this is just a subquery and will not interfere  with other includes
        ],
      },
      where: {
        [db.Op.and]: [
          { enabled: true },
          { display_name: { [db.Op.like]: '%' + filter + '%' } },
          { admin_public_status: true }
        ],
      },
      include: [ // removed extra nesting of array
        // influencer_ratings include not needed now
        // add other includes as needed
      ],
      limit: 10,
      offset: 0,
      order: [ // if needed
        [Sequelize.literal('avg_rating'), 'DESC'],
      ]
    });
    

    【讨论】:

    • 删除额外的数组嵌套并在包含之外调用 AVG,但内部属性解决了我的问题!谢谢你。仍然不太清楚 subQuery 是如何工作的,将研究它!
    猜你喜欢
    • 2015-01-20
    • 1970-01-01
    • 2022-06-13
    • 2017-12-08
    • 2018-04-27
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多