【问题标题】:How to count associations in sequelize in postgres database如何在postgres数据库中计算sequelize中的关联
【发布时间】:2024-04-12 16:15:02
【问题描述】:

我使用 sequelize 框架在 Node.js 中开发了 API。我使用 Postgres 数据库。请在下面找到我的表结构,如下所示。

category post category_post user_like
id id category_id user_id
category_name title post_id post_id
detail image_url

我需要开发一个 API 来通过提供的类别 id 过滤类别并返回类别的帖子,并且每个帖子都应该包含喜欢的总数。

示例 JSON 响应如下:

{
    "data":{
        "id":1,
        "category_name":"category 1",
        "posts":[
            {
                "id":1,
                "title":"post 1",
                "total_likes":50,
                "image_url":"image.png"
            }
        ]
    },
    "success":true
}

请找到我的代码如下:

sequelize.transaction().then(result => {
  return category.findOne({
    where: { "id": request.params.id }, include: [{
      model: post,
      as: "posts",
      attributes: ["id","title","image_url"],
      through: {
        attributes: []
      }
    }],
    attributes: ["id", "category_name"],
  });
}).then(posts => {
  let r = { "data": posts, "success": true }
  response.status(200).json(r)
}).catch(function (err) {
  console.log(err)
});

请指导我如何获取帖子的点赞总数。这个功能对我不起作用Counting associated entries with Sequelize

【问题讨论】:

    标签: node.js postgresql orm sequelize.js


    【解决方案1】:

    有两个联结表可供考虑,即category_postuser_like 表。这些可以使用many to many associations 处理。为简单起见,在下面的代码中,我将 user_like 表视为父表 posts 的简单子表,留下一个剩余的联结表来考虑。

    此外,打开sequelize logging 对处理困难的查询有很大帮助。

    以下是我将如何设置映射。

    let Category = sequelize.define('categories', {
            id: {
                type: DataTypes.INTEGER,
                allowNull: false,
                primaryKey: true
            },
            category_name: DataTypes.STRING,
            detail: DataTypes.STRING
        },
        {
            tableName: 'categories',
            timestamps: false
        })
    
    let Post = sequelize.define('posts', {
            id: {
                type: DataTypes.INTEGER,
                allowNull: false,
                primaryKey: true
            },
            title: DataTypes.STRING,
            image_url: DataTypes.STRING
        },
        {
            tableName: 'posts',
            timestamps: false
        })
        
    let UserLike = sequelize.define('UserLike', {
            post_id: {
                type: DataTypes.INTEGER,
                allowNull: false,
                primaryKey: true
            },
            user_id: {
                type: DataTypes.INTEGER,
                allowNull: false,
                primaryKey: true
            }
        },
        {
            tableName: 'user_likes',
            timestamps: false
        })
        
    Category.belongsToMany(Post, {
        through: 'category_posts',
        timestamps: false,
        foreignKey: 'category_id',
        otherKey: 'post_id'
    })
    Post.belongsToMany(Category, {
        through: 'category_posts',
        timestamps: false,
        foreignKey: 'post_id',
        otherKey: 'category_id'
    })
    
    Post.hasMany(UserLike, {
        foreignKey: 'post_id',
        sourceKey: 'id'
    })
    UserLike.belongsTo(Post, {
        foreignKey: 'post_id',
        targetKey: 'id'
    })
    

    然后我会使用这样的子查询。请注意,为了使其成为sequelize subquery,文字选择语句周围的括号是必要的。

    let results = await Category.findAll({
            attributes: {
                exclude: [ 'detail' ]
            },
            where: { id: request.params.id },
            include: {
                model: Post,
                attributes: {
                    include: [
                        [ sequelize.cast(sequelize.literal('(select count(*) from user_likes as ul where ul.post_id = posts.id)'), 'integer'), 'likes' ]
                    ]
                }
            }
        })
    

    这会产生以下结果:

    [
      {
        "id": 1,
        "category_name": "category 1",
        "posts": [
          {
            "id": 1,
            "title": "Post 1",
            "image_url": "http://example.com/image1.png",
            "likes": 3,
            "category_posts": {
              "category_id": 1,
              "post_id": 1
            }
          },
          {
            "id": 2,
            "title": "Post 2",
            "image_url": "http://example.com/image2.png",
            "likes": 2,
            "category_posts": {
              "category_id": 1,
              "post_id": 2
            }
          }
        ]
      }
    ]
    

    或者,正如 HarshIT 所建议的那样,您可以使用聚合和 group by 子句与以下查询:

    let results = await Category.findAll({
            attributes: {
                exclude: [ 'detail' ],
                include: [[sequelize.cast(sequelize.fn('COUNT', Sequelize.col('posts->UserLikes.post_id')), 'integer'), 'total_likes']]
            },
            where: { id: request.params.id },
            include: {
                model: Post,
                include: {
                    model: UserLike
                }
            },
            group: [
                'categories.id',
                'posts.id',
                'posts->category_posts.category_id',
                'posts->category_posts.post_id',
                'posts->UserLikes.post_id',
                'posts->UserLikes.user_id'
            ]
        })
    

    会产生如下形式的结果:

    [
      {
        "id": 1,
        "category_name": "category 1",
        "total_likes": 1,
        "posts": [
          {
            "id": 1,
            "title": "Post 1",
            "image_url": "http://example.com/image1.png",
            "category_posts": {
              "category_id": 1,
              "post_id": 1
            },
            "UserLikes": [
              {
                "post_id": 1,
                "user_id": 5
              },
              {
                "post_id": 1,
                "user_id": 6
              },
              {
                "post_id": 1,
                "user_id": 9
              }
            ]
          },
          {
            "id": 2,
            "title": "Post 2",
            "image_url": "http://example.com/image2.png",
            "category_posts": {
              "category_id": 1,
              "post_id": 2
            },
            "UserLikes": [
              {
                "post_id": 2,
                "user_id": 5
              },
              {
                "post_id": 2,
                "user_id": 8
              }
            ]
          }
        ]
      }
    ]
    

    【讨论】:

    • 感谢您的回复@Andrew,我与您上面提到的关联相同。当我尝试上面提到的查询时,它会给出错误“关系“user_likes”不存在”。
    • 我尝试使用“[sequelize.cast(sequelize.fn("COUNT", Sequelize.col("posts->user_likes.id")), 'integer'), "total_likes"]"它工作,虽然我需要添加组:[“category.id”,“posts.id”]。
    • 如果用户已经喜欢视频,是否可以获得布尔值?
    • @HarshIT 太棒了!我必须使用 sequelize 日志记录来查看 sequelize 发送到数据库的查询。至于如果用户已经喜欢视频,则返回布尔值,您可以尝试使用sql case statement 将另一个字面元素添加到include 数组的第二个子查询中。此外,如果查询变得过于复杂,总是值得考虑写一个raw sql query。希望这会有所帮助!
    • @HarshIT 可以通过将“user_likes”替换为“posts->user_likes”来修改上述答案中的查询,以修复您遇到的错误。当 Sequelize 自动生成查询以移交给数据库时,它可能会为表名提供一些奇怪的别名,这可能导致您收到有关“关系“user_likes”不存在的错误。跨度>