NestJS nodejs завантажує вкладені коментарі в один запит із відносинами?


10

У мене є такі моделі:

User, Customer,Comment

Користувач може коментувати а 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"
            }
        ]
    }
]

Як я можу зробити запит, щоб вкласти їх усіх в типі?

Визначення юридичної особи (зверніть увагу, що клієнт перейменований на 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 Додано подяки
Бен Бері

Відповіді:


7

Ви в основному використовуєте Adjacency list Tree.

Список суміжності - це проста модель з самонасиланням. Перевага такого підходу - простота, але недолік полягає в тому, що ви не можете з цим впоратися з глибокими деревами.

Існує рекурсивний спосіб зробити це зі списком суміжності, але це не працює з 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

Як сказав Габріель, в інших моделях даних краще робити те, що потрібно з розумом. Однак якщо ви не можете змінити дизайн бази даних, ви можете використовувати альтернативи (які менш ефективні чи гарні, але все, що працює у виробництві, все, що зрештою має значення).

Коли ви встановлюєте значення Lead у своєму LeadComment, я можу запропонувати встановити це значення також у відповідях на корінний коментар про створення відповіді (у коді має бути просто). Таким чином ви можете отримати всі коментарі до свого замовника за один запит (включаючи відповіді).

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

Звичайно, вам доведеться запустити пакет SQL, щоб заповнити пропущені значення стовпців, але це разова річ, і як тільки ваша кодова база буде також виправлена, вам більше нічого не доведеться запускати. І це не змінює структуру вашої бази даних (лише спосіб заселення даних).

Тоді ви можете зібрати в nodejs цілий матеріал (списки відповідей). Щоб отримати "кореневий" коментар, просто відфільтруйте коментар, який не відповідає (що не має батьків). Якщо ви просто хочете корінних коментарів із бази даних, ви навіть можете змінити запит лише на ці (з zeroComment zeroll у стовпці 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);

Тоді ви можете отримати відповіді на rootComments і складати весь список рекурсивно у вузлі.

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

Напевно, є більш оптимізовані способи обчислення цих списків, це для мене один із найпростіших, що можна зробити.

Залежно від кількості коментарів це може повільно (ви можете обмежити результати часовою міткою та номером, наприклад, таким чином, щоб воно було достатньо хорошим?). Отож, будьте обережні, не забирайте всесвітню коментарі щодо ведучого "Джастін Бібер". багато коментарів ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.