TypeORM Tree Entity树形实体如何查询加载Post关联评论的全层级子回复
TypeORM Tree Entity 全层级嵌套评论查询解决方案
问题原因
你当前的写法只能查询一级子回复,是因为普通的leftJoinAndSelect只能显式关联指定层级的关系,想要获取任意深度的嵌套子回复,需要使用TypeORM针对树形实体提供的专属API,或者手动组装树形结构。
解决方案
前置准备
首先需要将Comments实体的仓库声明为TreeRepository类型(普通Repository没有树形操作相关方法),注入示例:
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { TreeRepository } from 'typeorm'; import { Comments } from './comment.entity'; @Injectable() export class PostsService { constructor( @InjectRepository(Comments) private readonly commentTreeRepo: TreeRepository<Comments>, ) {} }
方案一:使用TreeRepository内置方法(推荐)
先查询帖子基础信息,再为每个帖子加载全量评论树,TypeORM会自动处理closure表的递归查询逻辑:
// 第一步:查询帖子基础信息+作者信息 const posts = await this.postsRepo .createQueryBuilder('posts') .leftJoinAndSelect('posts.author', 'author') .where('posts.author = :userid', { userid }) .getMany(); // 第二步:为每个帖子加载完整嵌套评论树 for (const post of posts) { // 先查询该帖子下的所有根评论(无父级的一级评论) const rootComments = await this.commentTreeRepo .createQueryBuilder('comment') .leftJoinAndSelect('comment.user', 'commentUser') .where('comment.post_id = :postId', { postId: post.id }) .andWhere('comment.parentId IS NULL') .getMany(); // 为每个根评论加载全量后代树,自动递归关联所有层级的用户信息 post.comments = await Promise.all( rootComments.map(root => this.commentTreeRepo.findDescendantsTree(root, { relations: ['user', 'children.user'] }) ) ); }
方案二:全量查询后手动组装(适合评论量不大的场景)
只查询一次数据库拿到该帖子下所有评论,再手动组装为树形结构,性能更高:
// 第一步:查询帖子基础信息 const posts = await this.postsRepo .createQueryBuilder('posts') .leftJoinAndSelect('posts.author', 'author') .where('posts.author = :userid', { userid }) .getMany(); // 第二步:批量查询所有帖子的所有评论,一次性拿到所有层级数据 const postIds = posts.map(p => p.id); const allComments = await this.commentTreeRepo .createQueryBuilder('comment') .leftJoinAndSelect('comment.user', 'commentUser') .where('comment.post_id IN (:...postIds)', { postIds }) .getMany(); // 第三步:按帖子分组,手动组装每个帖子的评论树 const commentGroupByPost = new Map<number, Comments[]>(); allComments.forEach(comment => { if (!commentGroupByPost.has(comment.post.id)) { commentGroupByPost.set(comment.post.id, []); } commentGroupByPost.get(comment.post.id).push(comment); }); posts.forEach(post => { const comments = commentGroupByPost.get(post.id) || []; const commentMap = new Map<number, Comments>(); // 初始化所有评论的children字段,并放入索引map comments.forEach(c => { c.children = []; commentMap.set(c.id, c); }); const rootComments: Comments[] = []; comments.forEach(c => { if (c.parentId) { // 追加到父评论的子节点列表 commentMap.get(c.parentId)?.children.push(c); } else { rootComments.push(c); } }); post.comments = rootComments; });
注意事项
- TypeORM 0.3.x及以上版本对树形查询的关联加载支持更完善,建议使用最新稳定版避免兼容性问题
- 若评论嵌套层级极深、数据量极大,建议限制最大查询层级,避免出现性能问题
内容的提问来源于stack exchange,提问作者rickster
相关产品推荐
相关产品推荐

