如何在pg-promise中将嵌套查询结果合并到父数组
解决PostgreSQL + pg-promise嵌套评论查询的问题
我来帮你搞定这个问题!你遇到的核心问题是异步Promise的处理不当——你的代码里直接把t.any返回的Promise赋值给了post.coments(注意这里拼写应该是comments哦),但没有等待这些Promise完成就返回了posts数组,所以最终返回的结果里评论数据根本没被正确填充。下面给你两种可行的解决方案:
方案一:修正异步Promise处理(基于原代码调整)
我们需要用Promise.all来等待所有评论查询的Promise完成,确保每个帖子的评论都被正确赋值后再返回结果:
con.task(t => { return t.any( 'select *, avatar from post, users where usuario = $1 and usuario = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos] ).then(posts => { if (posts.length === 0) return posts; // 把每个帖子转换成一个Promise,负责获取并填充评论 const postWithCommentsPromises = posts.map(post => { return t.any('select * from comment where idPost = $1', post.id) .then(comments => { post.comments = comments; // 修正拼写错误:coments → comments return post; }); }); // 等待所有评论查询完成,返回填充好的帖子数组 return Promise.all(postWithCommentsPromises); }); }) .then(posts => { res.send(posts); }) .catch(error => { console.error('查询出错:', error); res.status(500).send('服务器内部错误'); });
关键说明:
posts.map会把每个帖子转换成一个Promise,这个Promise先执行评论查询,再把结果赋值给帖子的comments属性Promise.all会等待所有这些Promise全部完成,最终得到的是已经填充好评论的完整帖子数组
方案二:数据库层面聚合查询(更高效推荐)
上面的方案需要执行1(帖子查询)+ N(评论查询)次数据库请求,当帖子数量多的时候性能会受影响。更优的方式是用PostgreSQL的JSON_AGG函数,一次查询就把帖子和对应的评论嵌套好:
con.task(t => { return t.any(` SELECT p.*, u.avatar, -- 聚合评论为JSON数组,没有评论时返回空数组 COALESCE(JSON_AGG(c) FILTER (WHERE c.id IS NOT NULL), '[]') AS comments FROM post p JOIN users u ON p.usuario = u.alias -- LEFT JOIN确保没有评论的帖子也能被返回 LEFT JOIN comment c ON p.id = c.idPost WHERE p.usuario = $1 GROUP BY p.id, u.avatar ORDER BY p.time DESC LIMIT 10 OFFSET $2; `, [username, pos]); }) .then(posts => { res.send(posts); }) .catch(error => { console.error('查询出错:', error); res.status(500).send('服务器内部错误'); });
关键说明:
LEFT JOIN关联帖子和评论,保证即使没有评论的帖子也会出现在结果里JSON_AGG(c)把每个帖子对应的所有评论聚合为一个JSON数组COALESCE(..., '[]')确保没有评论的帖子返回的是[]而不是null,前端处理更友好- 只需要一次数据库请求,性能远优于多次查询的方案
另外还要提醒你:原SQL里的where user= $1应该是where usuario = $1,因为你的post表字段是usuario,否则会出现字段不存在的错误哦!
内容的提问来源于stack exchange,提问作者Sergio Rey
相关产品推荐
相关产品推荐

