如何计算多对多关系并在 ORM Sequelize 中应用 where 子句

How to count a many to many relationships and apply where clause with ORM Sequelize

我需要使用 Sequelize ORM 执行 findAll 查询来计算某些关系并过滤最小数量。 让我解释一下,我有一个用户 table,它与食谱 table 有两个 M:N 关系(具体来说,有 2 个枢轴 table:RecipeFavorites、RecipeStores),以及1:M 与食谱的关系 table。

用户模型:

module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    email: {
      type: DataTypes.STRING(45),
      allowNull: false,
      unique: true,
    },
    password: {
      type: DataTypes.TEXT,
      allowNull: false
    }
  }, {
    tableName: 'Users',
    timestamps: false
  });
  User.associate = function(models) {

    User.belongsToMany(models.Recipe, {
      through: 'RecipeStore',
      as: 'storedRecipes'
    });

    User.belongsToMany(models.Recipe, {
      through: 'RecipeFavorite',
      as: 'favoriteRecipes'
    });

    User.hasMany(models.Recipe, {
      as: 'createdRecipes',
      foreignKey: 'createdById'
    });

  };
  return User;
};

这是当前查询吗:

User
    .findAll({
        attributes: {
            exclude: ['password'],
            include: [
                'createdAt',
               [Sequelize.fn("COUNT", Sequelize.col("storedRecipes.id")), "countStoredRecipes"],
               [Sequelize.fn("COUNT", Sequelize.col("favoriteRecipes.id")), "countFavoriteRecipes"]
               [Sequelize.fn("COUNT", Sequelize.col("createdRecipes.id")), "countCreatedRecipes"]
            ],
        },
        group: ['id'],
        include: [
            {
                model: Recipe,
                as: 'createdRecipes',
                attributes: []
            },
            {
                model: Recipe,
                as: 'favoriteRecipes',
                attributes: []
            },
            {
                model: Recipe,
                as: 'storedRecipes',
                attributes: []
            }
        ],
        where: {
            countCreatedRecipes: {
                [Op.gte]: 1,
            },
            countFavoriteRecipes: {
                [Op.gte]: 3,
            },
            countStoredRecipes: {
                [Op.gte]: 2,
            }
    })
    .then(users => {
        return res.status(200).json({
            ok: true,
            users
        });
    })
    .catch(error => {
        console.log(error);
        return res.status(400).json({
            ok: false,
            error
        });
    });

然而它不起作用。

我希望查询 returns 类似于:

{
    "ok": true,
    "users": [
        {
            "id": 1,
            "email": "email1@email.com",
            "countStoredRecipes": 10,
            "countFavoriteRecipes": 3,
            "countCreatedRecipes": 9
        },
        {
            "id": 5,
            "email": "email5@email.com",
            "countStoredRecipes": 10,
            "countFavoriteRecipes": 4,
            "countCreatedRecipes": 9
        },
        {
            "id": 7,
            "email": "email7@email.com",
            "countStoredRecipes": 2,
            "countFavoriteRecipes": 8,
            "countCreatedRecipes": 7
        },
    ]
}

required: false 添加到您的包含中,使它们成为 LEFT JOIN,并且在没有匹配项时仍会得到 return 结果。要按派生值(如 COUNT)进行过滤,您需要使用 having 子句,而不是 where.

User.findAll({
  attributes: {
    exclude: ["password"], // this should be a hash not an actual password
    include: [
      "createdAt",
      [
        sequelize.fn("COUNT", sequelize.col("storedRecipes.id")),
        "countStoredRecipes",
      ],
      [
        sequelize.fn("COUNT", sequelize.col("favoriteRecipes.id")),
        "countFavoriteRecipes",
      ][
        (sequelize.fn("COUNT", sequelize.col("createdRecipes.id")),
        "countCreatedRecipes")
      ],
    ],
  },
  include: [
    {
      model: Recipe,
      as: "createdRecipes",
      attributes: [],
      required: false,
    },
    {
      model: Recipe,
      as: "favoriteRecipes",
      attributes: [],
      required: false,
    },
    {
      model: Recipe,
      as: "storedRecipes",
      attributes: [],
      required: false,
    },
  ],
  group: ["id"],
  having: {
    countCreatedRecipes: {
      [Op.gte]: 1,
    },
    countFavoriteRecipes: {
      [Op.gte]: 3,
    },
    countStoredRecipes: {
      [Op.gte]: 2,
    },
  },
});

好的,我用 sequelize.literal() 代替 sequelize.fn() 来计数,用 having 代替 where。结果如下:

User.findAll({
  attributes: {
    exclude: ["password"],
    include: [
      "createdAt",
      [
        sequelize.literal(
        '(SELECT COUNT(*) FROM RecipeStores WHERE RecipeStores.userId = User.id)'),
        'countStoredRecipes'
      ],
      [
        sequelize.literal(
        '(SELECT COUNT(*) FROM RecipeFavorites WHERE RecipeFavorites.userId = User.id)'),
        'countFavoriteRecipes'
      ],
      [
        sequelize.literal(
        '(SELECT COUNT(*) FROM Recipes WHERE Recipes.createdById = User.id)'), 
        'countCreatedRecipes'
      ],
    ],
  },
  group: ["id"],
  having: {
    countStoredRecipes: {
      [Op.gte]: 2,
    },
    countCreatedRecipes: {
      [Op.gte]: 1,
    },
    countFavoriteRecipes: {
      [Op.gte]: 3,
    },
    
  },
});