如何计算多对多关系并在 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,
},
},
});
我需要使用 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,
},
},
});