NestJS nodejs โหลดความคิดเห็นซ้อนในหนึ่งแบบสอบถามที่มีความสัมพันธ์?


10

ฉันมีโมเดลดังต่อไปนี้:

User, Customer,Comment

ผู้ใช้สามารถแสดงความคิดเห็นในCustomerผู้ใช้สามารถตอบกลับความคิดเห็นของผู้ใช้คนอื่นไม่ จำกัด ซ้ำ

ฉันได้ทำสิ่งนี้แล้ว แต่ จำกัด ไว้เพียงคำตอบเดียวและฉันต้องการได้รับคำตอบทั้งหมด NESTED:

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"
            }
        ]
    }
]

ฉันจะสร้างเคียวรีเพื่อวางซ้อนเคียวรีในรูปแบบทั้งหมดได้อย่างไร?

ข้อกำหนดเอนทิตี(ลูกค้าหมายเหตุเปลี่ยนชื่อเป็นลูกค้าเป้าหมาย) :

@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

คำตอบ:


7

Adjacency list Treeคุณพื้นใช้

รายการ Adjacency เป็นรูปแบบง่าย ๆ ที่มีการอ้างอิงตนเอง ข้อดีของวิธีนี้คือความเรียบง่าย แต่ข้อเสียเปรียบคือคุณไม่สามารถจัดการกับต้นไม้ที่ลึกล้ำได้

มีวิธีเรียกใช้ซ้ำด้วยรายการ 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

กาเบรียลกล่าวว่าโมเดลข้อมูลอื่นดีกว่าที่จะทำสิ่งที่คุณต้องการให้มีประสิทธิภาพ อย่างไรก็ตามหากคุณไม่สามารถเปลี่ยนการออกแบบฐานข้อมูลคุณสามารถใช้ทางเลือกอื่น (ซึ่งมีประสิทธิภาพน้อยกว่าหรือสวย แต่สิ่งที่ใช้งานได้จริงคือสิ่งที่สำคัญที่สุด)

ในขณะที่คุณตั้งค่าลูกค้าเป้าหมายใน LeadComment ของคุณฉันสามารถแนะนำให้คุณตั้งค่านี้ในการตอบกลับเกี่ยวกับความคิดเห็นรูทในการสร้างการตอบกลับ (ควรง่ายในรหัส) วิธีนี้คุณสามารถดึงความคิดเห็นทั้งหมดในลูกค้าของคุณในแบบสอบถามเดียว (รวมถึงคำตอบ)

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

แน่นอนว่าคุณจะต้องเรียกใช้แบทช์ SQL เพื่อเติมค่าคอลัมน์ที่หายไป แต่มันเป็นเพียงครั้งเดียวและเมื่อโค้ดเบสของคุณได้รับการติดตั้งแล้วคุณจะไม่ต้องทำงานอะไรอีกหลังจากนั้น และจะไม่เปลี่ยนโครงสร้างของฐานข้อมูลของคุณ (เพียงแค่วิธีการเติมข้อมูล)

จากนั้นคุณสามารถสร้างใน nodejs ทุกสิ่ง (รายการคำตอบ) หากต้องการรับความคิดเห็น "root" เพียงกรองตามความคิดเห็นที่ไม่ใช่คำตอบ (ที่ไม่มีพ่อแม่) หากคุณต้องการความคิดเห็นรูทจากฐานข้อมูลคุณสามารถเปลี่ยนคิวรีให้เป็นคำเหล่านี้ได้เท่านั้น (ด้วย parentComment null ในคอลัมน์ SQL)

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);

จากนั้นคุณสามารถรับคำตอบบนรูทคอมเมนต์และสร้างรายการทั้งหมดซ้ำในโหนด

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" ความคิดเห็นมากมาย ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.