繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM