Search code examples
nestjstypeorm

How to update a ManyToMany relation with TypeORM?


I have a candidate entity with many "ManyToMany" relations, I need to create an update function for this entity. Here is my candidate entity:

@Entity()
export class Candidate {
    @PrimaryGeneratedColumn()
        id: number;
    
    ...

    @ManyToMany(() => Language, { nullable: true })
    @JoinTable()
        languages: Language[];

    @ManyToMany(() => Tool, { nullable: true })
    @JoinTable()
        tools: Tool[];

    @ManyToMany(() => ActivityArea, { nullable: true })
    @JoinTable()
        activity_areas: ActivityArea[];

    ...

    @Column({ nullable: true })
        desired_starting_date: string;

    ...
}

Language entity (the others are similar):

@Entity()
export class Language {
    @PrimaryGeneratedColumn()
        id: number;

    @Column({unique: true})
        name: string;
}

I tried to use the update function of typeORM :

async update(id: number, updateCandidateDto: UpdateCandidateDto) {
    try {
        const candidate = this.generateaCandidate(updateCandidateDto); // this is the new value of candidate

        return await this.candidateRepository.update({
            id
        }, {
            languages: candidate.languages,
            tools: candidate.tools,
            activity_areas: candidate.activity_areas,
            desired_starting_date: candidate.desired_starting_date
        });
    } catch (error) {
        throw new HttpException(error.detail, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

But I got the following error : Error: Cannot query across many-to-many for property tools

I check the following post before posting this one: Update a many-to-many relationship with TypeORM Typeorm (Nestjs) update ManyToMany relation when using querybuilder violates not-null constraint TypeORM updating entity/table

Can you help me to build an update function that allows me to update several ManyToMany parameters?


Solution

  • I finaly found a way to did it :

    async update(id: number, updateCandidateDto: UpdateCandidateDto) {
        try {
            const new_candidate = await this.generateCandidate(updateCandidateDto);
    
            for (const prop in new_candidate) {
                if (typeof new_candidate[prop] == 'object') {
                    const actualRelationships = await this.candidateRepository
                        .createQueryBuilder()
                        .relation(Candidate, prop)
                        .of(id)
                        .loadMany();
                    
                    await this.candidateRepository
                        .createQueryBuilder()
                        .relation(Candidate, prop)
                        .of(id)
                        .addAndRemove(new_candidate[prop], actualRelationships);
                } else {
                    const json = JSON.parse(`{ "${prop}" : "${new_candidate[prop]}" }`);
                        
                    this.candidateRepository
                       .createQueryBuilder()
                       .update(Candidate)
                       .set(json)
                       .where('id = :id', {id: id})
    .execute();
                }
            }
    
            return await this.findOne(id);
        } catch (error) {
            console.error(error);
            throw new HttpException('Update failed', HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    

    I test each property of the candidate I want to modify, if the property is of type 'object' it means that it is a foreign key. In this case, I retrieve all its relations from the database and replace them with the new relations.

    This solution works, I will use it until a better solution is proposed.