How to join a table using both id and a subjoin property in typeorm? - typeorm

I have three tables:
Parent {
#PrimaryGeneratedColumn({ type: 'bigint' })
id: string
#OneToMany(() => Child, l => l.parent)
children?: Child[]
}
ChildConfig {
#PrimaryGeneratedColumn({ type: 'bigint' })
id: string
#OneToMany(() => Child, l => l.config)
children: Child[]
#Column()
type: ChildType
}
Child {
#PrimaryGeneratedColumn({ type: 'bigint' })
id: string
#Column()
parentId: string
#Column()
configId: string
#ManyToOne(_ => ChildConfig, config => config.items)
#JoinColumn({ name: 'config_id' })
public config: ChildConfig
#ManyToOne(_ => Parent, item => item.children)
#JoinColumn([{ name: 'parent_id', referencedColumnName: 'id' }, { name: '' }])
public parent?: Parent
}
The Parent table has a one to many relationship with Child.
Child has a many to one relationship with ChildConfig.
I have a requirement that Parent only joins to Child where Child.parentId = Parent.id AND Child.config.type = ChildType.Good.
How do I do this?

Related

TypeORM #JoinTable() how to specify custom join columns?

this is my sample data model
I have declared the following classes:
#Entity({
name: 'user'
})
export class User {
#Column({ type: 'int4' })
#PrimaryColumn()
userid: number
#Column({name: 'name', type: 'varchar', length: 30})
name: string
#Column({name: 'age', type: 'int2'})
age: number
#ManyToMany(() => Department, (department)=> department.users)
#JoinTable({
name: 'department_user'
})
departments: Department[]
}
#Entity({ name: 'department' })
export class Department {
#Column({ type: 'int2' })
#PrimaryColumn()
departmentid: number
#Column({type: 'varchar', length: 50})
title: string
#Column({type:'text'})
notes: string
#ManyToMany(() => User, (user)=> user.departments)
#JoinTable({ name: 'department_user' })
users: User[]
}
whenever I run the app, it creates the departmentDepartmentId & userUserId columns and not utilize the columns in the corresponding join table. How can I tell typeorm to only use the predefined join column in join table?
Update 2 (as mentioned by #suvantorw)
I recreated the join table with the statement below:
create table department_user(
departmentid integer not null,
userid integer not null);
alter table department_user add constraint fk_dept_dept foreign key (departmentid) references department(departmentid);
alter table department_user add constraint fk_dept_user foreign key (userid) references "user"(userid);
alter table department_user add constraint pk_dept_user primary key (departmentid, userid);
and modified the entities like this:
user
#ManyToMany(() => Department, (department)=> department.users)
#JoinTable({
name: 'department_user',
joinColumn: { name: 'userid' },
inverseJoinColumn: { name: 'departmentid' }
})
departments: Department[]
}
department
#ManyToMany(() => User, (user)=> user.departments)
#JoinTable({
name: 'department_user',
joinColumn: { name: 'departmentid' },
inverseJoinColumn: { referencedColumnName: 'userid' }
})
users: User[]
}
it does run without errors but when it runs the table structure is modified to this
As you can see, ,y foreign key constraints are gone and new ones are created. Any clue what I'm doing wrong here?
Update 3
Finally I modified the classes as below and now the TypeORM accepts the relationships and does not create its own. it was a very painful experience to solve this and documentation about this decorator doesn't say much either.
user
#ManyToMany(() => Department, (department)=> department.users)
#JoinTable({
name: 'department_user',
joinColumn: {
name: 'userid',
foreignKeyConstraintName: 'fk_dept_user'
},
inverseJoinColumn: {
referencedColumnName: 'departmentid',
name: 'departmentid',
foreignKeyConstraintName: 'fk_dept_dept'
}
})
departments: Department[]
}
department
#ManyToMany(() => User, (user)=> user.departments)
#JoinTable({
name: 'department_user',
joinColumn: {
name: 'departmentid',
foreignKeyConstraintName: 'fk_dept_dept'
},
inverseJoinColumn: {
referencedColumnName: 'userid',
name: 'userid',
foreignKeyConstraintName: 'fk_dept_user'
}
})
users: User[]
}
You can specify the join column name and inverse join column name. Something like this should work for you:
#ManyToMany(() => Department, (department)=> department.users)
#JoinTable({
name: 'department_user',
joinColumn: { name: 'userid' },
inverseJoinColumn: { name: 'departmentid' }
})
departments: Department[]

How to save a Many-To-Many relationship with custom properties with typeORM Query Builder?

I need to create a many-to-many relationship between the tables organisation and user, where each user can have a role (admin, follower...) in the organisation.
I have :
Organization
#Entity('organization')
class Organization extends BaseColumns {
#Column({ type: 'character varying', unique: true })
name: string;
#OneToMany(() => OrganizationUser, (organizationUser) => organizationUser.organization, { cascade: true })
organizationUser: OrganizationUser[];
}
User
#Entity('user')
class User extends BaseColumns {
#Column({ type: 'character varying', unique: true })
email: string;
#Column({ type: 'character varying', name: 'password-hash' })
passwordHash: string | null;
#OneToMany(() => OrganizationUser, (organizationUser) => organizationUser.user, { cascade: true })
organizationUser: OrganizationUser[];
}
Role
#Entity('role')
class Role extends BaseColumns {
#Column({ type: 'enum', enum: RoleType, default: RoleType.Contributor })
name: RoleType;
#OneToMany(() => OrganizationUser, (organizationUser: OrganizationUser) => organizationUser.role, { cascade: true })
organizationUser: OrganizationUser[];
}
OrganizationUser
#Entity('organization-user')
class OrganizationUser extends BaseColumns {
#Column({ name: 'user-id' })
userId: string;
#Column({ name: 'organization-id' })
organizationId: string;
#ManyToOne(() => Role, (role: Role) => role.organizationUser, { cascade: true })
role: Role;
#ManyToOne(() => User, (user: User) => user.organizationUser)
user: User;
#ManyToOne(() => Organization, (organization: Organization) => organization.organizationUser)
organization: Organization;
}
How can I add a new insertion in the database with the typeORM Query Builder please ?

TypeORM upsert entities with OneToMany relationships

I have a few entities created on TypeORM and I want to upsert an array of data with all the entities. Here I have the entities:
#Entity({name: "__sales_rep"})
export class SalesRep {
#PrimaryColumn()
ldap: string;
#Column("text")
name: string
#OneToMany(() => ParentCompany, (parent_company) => parent_company.sales_rep, { cascade: ['insert', 'update'] })
parent_companies: ParentCompany[]
}
#Entity({name: "__parent_company"})
export class ParentCompany {
#PrimaryColumn()
id: number;
#Column("text")
name: string
#OneToMany(() => Advertiser, (advertiser) => advertiser.parent_company, { cascade: ['insert', 'update'] })
advertisers: Advertiser[]
#ManyToOne(() => SalesRep, (sales_rep) => sales_rep.parent_companies)
sales_rep: SalesRep
}
#Entity({name: "advertiser"})
export class Advertiser {
#PrimaryColumn()
id: number;
#Column("text")
name: string
#ManyToOne(() => ParentCompany, (parent_company) => parent_company.advertisers)
parent_company: ParentCompany
}
And here is how I am trying to insert the data as cascading the data. I believe the problem is that when I insert two advertisers with the same parent_company for example the constraints of the foreign key aren't allowing me to make the entire insertion.
async function loadData(data) {
console.log("Beggning data insertion");
try{
const insertData = data.rows.map((row) => {
const currentSalesRep ? {
ldap: row.ldap,
name: row.full_name
},
currentParentCompany = {
id: row.parent_company_id,
name: row.parent_company_name,
sales_rep: currentSalesRep
};
return {
id: row.advertiser_id,
name: row.advertiser_name,
parent_company: currentParentCompany
}
})
salesRepRepository
.upsert(insertData, ['id']);
typeorm
}
catch(e){
logger.error(e)
throw e;
}
}

How to select all entities by a list of relation id's in TypeORM

I have 2 entities with the following many-to-one relation:
Child.ts:
#Entity({name: "children"})
export class Child {
#PrimaryGeneratedColumn()
child_id: number;
#Column()
name: string;
#ManyToOne(type => Parent, parent => parent.children)
#JoinColumn({ name: "parent_id" })
parent: Parent;
}
Parent.ts
#Entity({name: "parents"})
export class Parent {
#PrimaryGeneratedColumn()
parent_id: number;
#Column()
name: string;
#OneToMany(type => Child, children=> children.parent)
children: Child[];
}
Now, i have a list of parent ids and i want to get all the children of all the parent id's, and i expected the following code to work:
const children: Child[] = await this.entityManager.find(Child, {
where: {
parent: In([1,2,3,4,5,6])
}
});
How can i do this without joining? and is it possible to do this without query builder?

typeorm - how to add extra field to "many to many" table?

for example:
3 tables
user
user_business_lines_business_line
business_line
those created by typeorm with the declaration in User
#ManyToMany(type => BusinessLine)
#JoinTable()
businessLines: BusinessLine[]
then, how to add columns fields like
#CreateDateColumn({ type: 'timestamp' })
createdAt: Date
#UpdateDateColumn({ type: 'timestamp' })
updatedAt: Date
to user_business_lines_business_line
It is not possible to add custom column in the auto created many-to-many bridge table. So create another table and give one-to-many and many-to-one relationship between them.
for example:
Three tables
User -> Table 1
BusinessLine -> Table 2
UserBusinessLine -> Bridge Table between User table and BusinessLine table
UserBusinessLine table will contain the foreign key of both parent tables and also we can add custom columns into it.
In User Table
#OneToMany(() => UserBusinessLine, (userBusinessLine) => userBusinessLine.user)
public userBusinessLines: UserBusinessLine[];
In BusinessLine Table
#OneToMany(() => UserBusinessLine, (userBusinessLine) => userBusinessLine.businessLine)
public userBusinessLines: UserBusinessLine[];
In UserBusinessLine Table
#ManyToOne(() => User, (user) => user.userBusinessLines)
public user: User;
#ManyToOne(() => BusinessLine, (businessLine) => businessLine.userBusinessLines)
public businessLine: BusinessLine;
// Custom Colums
#CreateDateColumn({ type: 'timestamp' })
createdAt: Date;
#UpdateDateColumn({ type: 'timestamp' })
updatedAt: Date;
So now the custom table has the foreign keys of User table and BusinessLine table. Also the CreateddateColumn and UpdatedDateColumn
You can specify custom-join table for ManyToMany relation
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number
#Column()
name: string
#ManyToMany(type => BusinessLine, businessLine => businessLine.users)
#JoinTable({
name: 'user_business_line',
joinColumn: {
name: 'userId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'businessLineId',
referencedColumnName: 'id',
},
})
businessLines: BusinessLine[]
}
#Entity('user_business_line')
export class UserBusinessLine {
#CreateDateColumn({ type: 'timestamp' })
createdAt: Date
#UpdateDateColumn({ type: 'timestamp' })
updatedAt: Date
#Column()
#IsNotEmpty()
#PrimaryColumn()
userId: number;
#Column()
#IsNotEmpty()
#PrimaryColumn()
businessLineId: number;
}
#Entity()
export class BusinessLine {
#PrimaryGeneratedColumn()
id: number
#Column()
name: string
#ManyToMany(type => User, user => user.businessLines)
users: User[]
}
If it is just for a createdAt timestamp for example, you could just add an extra column to your user_business_lines_business_line table using migrations like this (I am using Postgres):
ALTER TABLE "user_business_lines_business_line" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT LOCALTIMESTAMP

Resources