![](/img/trans.png)
[英]How to limit joined rows (many-to-many association) in Sequelize ORM?
[英]How to count a many to many relationships and apply where clause with ORM Sequelize
我需要使用 Sequelize ORM 执行一个 findAll 查询,该查询计算某些关系并过滤最小数量。 让我解释一下,我有一个 Users 表,它与 Recipes 表有两个 M:N 关系(具体来说,有 2 个数据透视表:RecipeFavorites、RecipeStores),以及与 Recipes 表的 1:M 关系。
用户模型:
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
});
});
但是它不起作用。
我希望查询返回如下内容:
{
"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 并在没有匹配时仍然返回结果。 要按 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()
来计数,并使用而不是在哪里。 结果如下:
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,
},
},
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.