Self-Referencing ManyToMany Relationship TypeORM

混江龙づ霸主 提交于 2021-01-21 07:24:13

问题


I have just started using TypeORM and I'm struggling getting the following relationship to work:

User->Friends, whereas a Friend is also a User Object. My getters, getFriends & getFriendsInverse are working, however; I do now want to distinguish between the two. In other words; when I perform a mysql join I do not want to do a left join on friends and another one on inverseFriends.

The getter getFriends() needs to return all friends, regardless of which "side" the object I'm on.

Does that make sense?

This is my model definition:

getFriends() {
    // This method should also return inverseFriends;
    // I do not want to return the concat version; this should
    // happen on the database/orm level
    // So I dont want: this.friends.concat(this.inverseFriends) 
    return this.friends;
}

@ManyToMany(type => User, user => user.friendsInverse, {
    cascadeInsert: false,
    cascadeUpdate: false,
})
@JoinTable()
friends = [];

@ManyToMany(type => User, user => user.friends, {
    cascadeInsert: true,
    cascadeUpdate: true,
    cascadeRemove: false,
})
friendsInverse = [];

I hope someone understands my question :D Thanks Matt


回答1:


You can self-reference your relations. Here is an example of a simple directed graph (aka a node can have a parent and multiple children).

@Entity()
export class Service extends BaseEntity {

  @PrimaryGeneratedColumn()
  id: number;
  
  @Column()
  @Index({ unique: true })
  title: string;

  @ManyToOne(type => Service, service => service.children)
  parent: Service;

  @OneToMany(type => Service, service => service.parent)
  children: Service[];
}

An important note to keep in mind is that these relations are not auto loaded when reading an object from the DB with find* functions.

To actually load them, you have to use query builder at the moment and join them. (You can join multiple levels.) An example:

let allServices = await this.repository.createQueryBuilder('category')
  .andWhere('category.price IS NULL')
  .innerJoinAndSelect('category.children', 'product')
  .leftJoinAndSelect('product.children', 'addon')
  .getMany();

Please note how I used different names to reference them (category, product, and addon).




回答2:


I hope someone understands my question

Yeah, here is the problem. I don't know what are trying to do. Friend is a separate class or it's still User->User[] relation?

Maybe this will help you: https://typeorm.github.io/tables-and-columns.html#closure-table




回答3:


I believe I'm 3 years late, but better late than ever. The most upvoted answer does not answer the question, as it only works for tree-like and hierarchical structures, so if you follow that example, this would happen:

               Fred
               /  \
          Albert  Laura
          /   \
       John   Foo

In this example, Foo can't be friends with Fred, because he can only have one parent. Friends is not a tree structure, it's like a net. The answer would be the following:

import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';

@Entity(UserModel.MODEL_NAME)
export class UserModel {
  static MODEL_NAME = 'users';

  @PrimaryGeneratedColumn()
  id?: number;

  @Column({ type: 'varchar', unique: true, length: 50 })
  username: string;

  @Column({ type: 'varchar', length: 50, unique: true })
  email: string;

  @ManyToMany(type => UserModel)
  @JoinTable()
  friends: UserModel[];

  @Column({ type: 'varchar', length: 300 })
  password: string;
}

This would create a table where relations between people would be saved. Now for the next important stuff. How do you query this and get a user's friends? It's not as easy as it seems, I've played hours with this and haven't been able to do it with TypeORM methods or even query builder. The answer is: Raw Query. This would return an array with the user's friends:

async findFriends(id: Id): Promise<UserModel[]> {
    return await this.userORM.query(
      ` SELECT * 
        FROM users U
        WHERE U.id <> $1
          AND EXISTS(
            SELECT 1
            FROM users_friends_users F
            WHERE (F."usersId_1" = $1 AND F."usersId_2" = U.id )
            OR (F."usersId_2" = $1 AND F."usersId_1" = U.id )
            );  `,
      [id],
    );
  }

(users_friends_users is the autogenerated name that typeORM gives to the table where the relations between users are saved)



来源:https://stackoverflow.com/questions/43747765/self-referencing-manytomany-relationship-typeorm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!