NestJS nodejs在具有关系的一个查询中加载嵌套注释?


10

我有以下型号:

UserCustomerComment

用户可以对发表评论Customer,用户可以递归地限制其他用户的评论。

我已经做到了,但仅限于一个答复,我想获得所有嵌套的答复:

public async getCommentsForCustomerId(customerId: string): Promise<CustomerComment[]> {
    return this.find({where: {customer: {id: customerId}, parentComment: null}, relations: ['childComments']});
}

但是我得到的响应仅嵌套在一个级别上:

[
    {
        "id": "7b5b654a-efb0-4afa-82ee-c00c38725072",
        "content": "test",
        "created_at": "2019-12-03T15:14:48.000Z",
        "updated_at": "2019-12-03T15:14:49.000Z",
        "childComments": [
            {
                "id": "7b5b654a-efb0-4afa-82ee-c00c38725073",
                "content": "test reply",
                "created_at": "2019-12-03T15:14:48.000Z",
                "updated_at": "2019-12-03T15:14:49.000Z",
                "parentCommentId": "7b5b654a-efb0-4afa-82ee-c00c38725072"
            }
        ]
    }
]

如何进行查询以将它们全部嵌套在typeorm中?

实体定义(请注意,客户已重命名为Lead)

@Entity('leads_comments')
export class LeadComment {

  @PrimaryGeneratedColumn('uuid')
  id: string;

  @ManyToOne(type => LeadComment, comment => comment.childComments, {nullable: true})
  parentComment: LeadComment;

  @OneToMany(type => LeadComment, comment => comment.parentComment)
  @JoinColumn({name: 'parentCommentId'})
  childComments: LeadComment[];

  @RelationId((comment: LeadComment) => comment.parentComment)
  parentCommentId: string;

  @ManyToOne(type => User, {cascade: true})
  user: User | string;

  @RelationId((comment: LeadComment) => comment.user, )
  userId: string;

  @ManyToOne(type => Lead, lead => lead.comments, {cascade: true})
  lead: Lead | string;

  @RelationId((comment: LeadComment) => comment.lead)
  leadId: string;

  @Column('varchar')
  content: string;

  @CreateDateColumn()
  created_at: Date;

  @UpdateDateColumn()
  updated_at: Date;
}

1
您可以添加实体定义吗?
zenbeni

@zenbeni添加了感谢
Ben Beri

Answers:


7

您基本上是在使用Adjacency list Tree

邻接表是带有自引用的简单模型。这种方法的好处是简单,但是缺点是您不能用它来处理深树。

有一种使用Adjacency列表进行递归的方法,但不适用于MySQL。

解决方案是使用另一种类型的树。其他可能的树是:

  • 嵌套集:对于读取非常有效,但对写入不利。嵌套集中不能有多个根。
  • 物化路径:(也称为路径枚举)简单有效。
  • 封闭表:将父子关系存储在单独的表中。在读取和写入方面均有效(尚未实现更新或删除组件的父对象)
@Entity()
@Tree("nested-set") // or @Tree("materialized-path") or @Tree("closure-table")
export class Category {

    @PrimaryGeneratedColumn()
    id: number;

    @TreeChildren()
    children: Category[];

    @TreeParent()
    parent: Category;
}

要加载树,请使用:

const manager = getManager();
const trees = await manager.getTreeRepository(Category).findTrees();

获取树存储库后,可以使用下一个功能:findTrees(), findRoots(), findDescendants(), findDescendantsTree()和其他功能 。有关更多信息,请参见文档

了解有关不同类型树的更多信息:分层数据模型


1

正如加布里埃尔(Gabriel)所说,其他数据模型更好地执行您想要的性能明智的选择。仍然,如果您不能更改数据库设计,则可以使用替代方法(性能较差或不错,但最终有效的是生产中可行的方法)。

当您在LeadComment中设置Lead值时,我建议您在回复创建的根注释上的回复中也设置此值(在代码中应该很容易)。这样,您可以在一个查询中获取有关客户的所有评论(包括答复)。

const lead = await leadRepository.findOne(id);
const comments = await commentRepository.find({lead});

当然,您将必须运行SQL批处理来填充缺少的列值,但这是一次性的事情,而且一旦对代码库进行了修补,您就无需再执行任何操作。而且它不会改变数据库的结构(只是填充数据的方式)。

然后,您可以在nodejs中构建全部内容(回复列表)。要获得“根”评论,只需按不回复(没有父母)的评论进行过滤。如果只想从数据库中获取根注释,甚至可以将查询更改为仅这些注释(SQL列中的parentComment为null)。

function sortComment(c1: LeadComment , c2: LeadComment ): number {
    if (c1.created_at.getTime() > c2.created_at.getTime()) {
    return 1;
    }
    if (c1.created_at.getTime() < c2.created_at.getTime()) {
        return -1;
    }
    return 0;
}
const rootComments = comments
    .filter(c => !c.parentComment)
    .sort(sortComment);

然后,您可以在rootComments上获得答复,并在node中递归构建整个列表。

function buildCommentList(currentList: LeadComment[], allComments: LeadComment[]): LeadComment[] {
    const lastComment = currentList[currentList.length - 1];
    const childComments = allComments
        .filter(c => c.parentComment?.id === lastComment.id)
        .sort(sortComment);
    if (childComments.length === 0) {
        return currentList;
    }
    const childLists = childComments.flatMap(c => buildCommentList([c], allComments));
    return [...currentList, ...childLists];
}

const listsOfComments = rootComments.map(r => buildCommentList([r], comments));

可能有更多优化的方法来计算这些列表,这对我来说是最简单的方法之一。

取决于注释的数量,它可能变慢(例如,您可以通过时间戳和数量来限制结果,以便它应该足够好?)所以要当心,不要在获得“ Justin Bieber”线索的情况下获取注释的范围很多评论...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.