TypeORM报错distinctAlias.post_id不存在,查询别名适配GraphQL问题
TypeORM QueryBuilder 字段别名失效与
distinctAlias.post_id错误问题 问题描述
使用TypeORM时遇到两个核心问题:
- 执行查询时报错
Column "distinctAlias.post_id" does not exist - 明明在QueryBuilder中给
post.id和post.createdAt设置了别名id、createdAt,但返回的原始数据里字段却变成了post_id和post_createdAt,与GraphQL定义的类型不兼容。
现有QueryBuilder代码
const qb = Post.createQueryBuilder('post') .select(['post.id AS id', 'post.createdAt AS createdAt']) .addSelect( "json_build_object('id', user.id, 'username', user.username, 'email', user.email, 'createdAt', user.createdAt, 'updatedAt', user.updatedAt)", 'creator' ) if (req.session.userId) qb.addSelect((qb) => { return qb .subQuery() .select('updoot.value') .from(Updoot, 'updoot') .where('"userId" = :userId AND "postId" = post.id', { userId: req.session.userId }) }, 'voteStatus') else qb.addSelect('null', 'voteStatus') qb.innerJoin('post.creator', 'user') .orderBy('post.createdAt', 'DESC') .take(realLimitPlusOne) if (cursor) qb.where('post.createdAt < :cursor', { cursor: new Date(parseInt(cursor)) }) qb.printSql() const posts = await qb.getRawAndEntities() console.log('posts: ', posts['raw'][0]) return { posts: posts['raw'].slice(0, realLimit), hasMore: posts['raw'].length === realLimitPlusOne }
期望返回数据结构
posts: { id: 29, createdAt: 2023-02-07T06:53:39.453Z, creator: { id: 44, username: 'prashant', email: 'prashant@gmail.com', createdAt: '2023-02-06T13:02:11.717504', updatedAt: '2023-02-06T13:02:11.717504' }, voteStatus: -1 }
问题原因与解决方法
1. 字段别名失效问题
TypeORM的QueryBuilder在使用数组形式的select参数时,无法正确识别别名规则,会自动给字段添加表前缀(如post_)。
解决方式:
将数组形式的select拆分为多次addSelect调用,或改用单个字符串参数的select:
// 方式1:拆分addSelect(推荐) const qb = Post.createQueryBuilder('post') .addSelect('post.id', 'id') .addSelect('post.createdAt', 'createdAt') // 方式2:单个字符串参数 const qb = Post.createQueryBuilder('post') .select('post.id AS id, post.createdAt AS createdAt')
2. distinctAlias.post_id错误问题
这个错误是因为TypeORM在处理take()分页时,会自动添加DISTINCT关键字,但你的查询中包含了json_build_object生成的聚合列,导致ORM无法正确关联原始表字段,进而找不到distinctAlias.post_id。
解决方式:
显式关闭自动去重,在QueryBuilder中添加.distinct(false):
qb.innerJoin('post.creator', 'user') .orderBy('post.createdAt', 'DESC') .take(realLimitPlusOne) .distinct(false) // 关闭自动去重,避免distinctAlias相关错误
修改后的完整代码示例
const qb = Post.createQueryBuilder('post') .addSelect('post.id', 'id') .addSelect('post.createdAt', 'createdAt') .addSelect( "json_build_object('id', user.id, 'username', user.username, 'email', user.email, 'createdAt', user.createdAt, 'updatedAt', user.updatedAt)", 'creator' ) if (req.session.userId) { qb.addSelect((subQb) => { return subQb .select('updoot.value') .from(Updoot, 'updoot') .where('"userId" = :userId AND "postId" = post.id', { userId: req.session.userId }) }, 'voteStatus') } else { qb.addSelect('null', 'voteStatus') } qb.innerJoin('post.creator', 'user') .orderBy('post.createdAt', 'DESC') .take(realLimitPlusOne) .distinct(false) if (cursor) { qb.where('post.createdAt < :cursor', { cursor: new Date(parseInt(cursor)) }) } const posts = await qb.getRawAndEntities() return { posts: posts.raw.slice(0, realLimit).map(item => ({ ...item, // 注意:json_build_object返回的是字符串,需要解析为对象 creator: JSON.parse(item.creator) })), hasMore: posts.raw.length === realLimitPlusOne }
备选方案:手动字段映射
如果上述修改仍未解决字段名匹配问题,可以手动做字段映射:
const mappedPosts = posts.raw.map(post => ({ id: post.post_id, createdAt: post.post_createdAt, creator: JSON.parse(post.creator), voteStatus: post.voteStatus }))
但优先解决QueryBuilder的别名问题,这才是根本解决方案。
内容的提问来源于stack exchange,提问作者jastor_007
相关产品推荐
相关产品推荐

