How to select data by using typeorm from 3 tables which depend on each other? - postgresql

I have 3 entity which depend on each other and I have a problem with querying data from them by using one request.
First one User:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Column()
login: string;
#Column()
password: string;
#Column()
ownerId: number;
#OneToOne(() => Role, (role) => role.user)
#JoinColumn()
role?: Role;
}
Second one Role:
#Entity()
export class Role extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
description: string;
#ManyToOne(() => User, (user) => user.role)
#JoinColumn()
user: User | null;
#ManyToMany(() => Permission, { cascade: true })
#JoinTable({ name: 'roles_has_permissions' })
permissions: Permission[];
}
Third one Permission:
#Entity()
export class Permission {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: PossiblePermissions;
}
How to select data from database. I’m using typeorm with Postgresql and I want to get array like this.
[{
…user info by ownerId
role: {
…role which related this user
permissions: [{
…permissions which related this role
}, …]
}
}, …]

Use query builder https://typeorm.io/#/select-query-builder
In your case it should be something like this:
await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.role", "role")
.leftJoinAndSelect("role.permissions", "permissions")
.getMany()

Related

Nested populate not working for mikro-orm

I'm using mikro-orm with nest.js, I have Users, Roles and Permissions entities. I need to select user by id with it's role and permissions from database. I'm using the following code to select user with everything:
this._userRepo.findOne({ $or: [{ email }] }, { populate: ['role', 'role.permissions'] })
I need the result to look like the following code example, but permissions are not selected:
{
id: 1,
email: 'john.doe#inter.net',
firstName: 'john',
lastName: 'Doe',
...
role: {
id: 21,
name: 'Moderator',
permissions: [
{ id: 1, name: 'Create' },
{ id: 2, name: 'Update' },
{ id: 3, name: 'Delete' },
]
}
}
Here's how my entities and schema looks like:
// user.entity.ts
#Entity({ tableName: 'users' })
export class UserEntity {
#PrimaryKey() id: number;
#Property() email: string;
#Property() firstName: string;
#Property() lastName: string;
...
#ManyToOne(() => RoleEntity) role: ref<RoleEntity, 'id'>;
constructor(role: RoleEntity) {
this.role = ref(role);
}
}
// role.entity.ts
#Entity({ tableName: 'roles' })
export class RoleEntity {
#PrimaryKey() id: number;
#Property() name: string;
...
#ManyToMany(() => PermissionEntity) permissions = new Collection<PermissionEntity>(this);
}
// permission.entity.ts
#Entity({ tableName: 'permissions' })
export class PermissionEntity {
#PrimaryKey() id: number;
#Property() name: string;
}
And there's roles_permissions table generated in database:
|role_entity_id|permission_entity_id|
|--------------|--------------------|
| 1 | 1 |
How can I solve this issue?
After torturing for several hours, I found that I was missing some properties to be specified in #ManyToMany relation.
So, I changed my RoleEntity to:
#Entity({ tableName: 'roles' })
export class RoleEntity {
#PrimaryKey() id: number;
#Property() name: string;
...
#ManyToMany({
entity: () => PermissionEntity,
owner: true,
pivotTable: 'roles_permissions',
joinColumn: 'role_entity_id',
inverseJoinColumn: 'permission_entity_id',
hidden: true,
})
permissions = new Collection<PermissionEntity>(this);
}
And now I can select role with it's permissions from database.

Create foreign key that references another schema

I have a multi tenant app which uses sequelize-typescript. Each time a new user is created, a new schema is created for them.
There's only one table in public schema - "Users".
Each tenant schema has "Roles" and "UserRoles" as a junction table.
I want UserRoles to reference Users in a public schema, not in a tenant schema. How can I achieve that?
user model
#Table({ paranoid: true })
export default class User extends Model {
#AutoIncrement
#PrimaryKey
#Column(DataType.BIGINT)
id: number;
#Column(DataType.STRING)
name: string;
#BelongsToMany(() => Role, () => UserRole)
roles: Role[];
role model
#Table({ paranoid: true })
export default class Role extends Model {
#AutoIncrement
#PrimaryKey
#Column(DataType.BIGINT)
id: number;
#Column(DataType.STRING)
name: string;
#BelongsToMany(() => User, () => UserRole)
users: User[];
}
user-role model
#Table({ paranoid: true, freezeTableName: true })
export default class UserRole extends Model {
#AutoIncrement
#PrimaryKey
#Column(DataType.BIGINT)
id: number;
#ForeignKey(() => Role)
#Column(DataType.BIGINT)
roleId: number;
#Unique
#ForeignKey(() => User)
userId: number;
}
I tried to specify schema inside column, but it still references tenant schema
#Table({ paranoid: true, freezeTableName: true })
export default class UserRole extends Model {
#AutoIncrement
#PrimaryKey
#Column(DataType.BIGINT)
id: number;
#ForeignKey(() => Role)
#Column(DataType.BIGINT)
roleId: number;
#Unique
#ForeignKey(() => User)
#Column({
type: DataType.BIGINT,
references: {
model: {
tableName: 'users',
schema: 'public',
},
key: 'id',
},
})
userId: number;
}

Typeorm Listener trigger another entity that have relation with

I am using Typeorm and I want to trigger the listener of my entity if an entity that have relation with has been updated relation, here is an example:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column({ type: 'character varying'})
Name: string;
#Column({ type: 'character varying'})
email: string;
#OneToMany(() => Photo, (photo) => photo.user)
photos: Photo[]
}
and
#Entity()
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column({ type: 'integer'})
size: string;
#Column({ type: 'character varying'})
userEmail: string;
#OneToOne(() => User, (user) => user.photo)
#JoinColumn()
user: User
#BeforeUpdate()
async emailChange() {
try{
let repo: any = await Object.values(require(`../../../repository/User.repository`),)[0];
const userRepository:any = getCustomRepository(repo);
const result = await userRepository.findOne({ where: {'id' : this.user}});
this.userEmail=result.email
}catch(err){
console.log('emailChange Error =====> ',err);
}
}
}
Thanks for any help in advance
Typeorm: 0.2.45
Postgresql: 13

How can I create a UUID FK column in NestJS?

I am running into an odd issue where I can't create a FK relationship between two entities.
// organization.entity.ts
#PrimaryGeneratedColumn('uuid')
id: string;
...
#OneToMany(() => User, (user) => user.organization)
users: User[];
// user.entity.ts
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({
type: 'uuid',
})
organizationId: string;
...
#ManyToOne(() => Organization, (organization) => organization.users)
organization: Organization;
In my ormconfig.json file I have these settings (among connection creds)
...
"logging": true,
"entities": [
"dist/**/*.entity{.ts,.js}"
],
"synchronize": true
...
I am using "typeorm": "^0.2.45" in my package.json file.
Key columns "organizationId" and "id" are of incompatible types: character varying and uuid.
How can I create an FK relationship between users & organizations?
So from your question I understood is you want a "organizationId" field in your users table which will be a FK.
To create OnetoMany Relation between Organization and users do as below:
// organization.entity.ts
#Entity({ name: 'organizations' })
export class Organization {
#PrimaryGeneratedColumn('uuid')
id: string;
...
#OneToMany(() => User, (user) => user.organization)
users: User[];
}
// user.entity.ts
#Entity({ name: 'users' })
export class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ type: 'uuid' })
organizationId: string;
...
#ManyToOne(() => Organization, (organization) => organization.users)
#JoinColumn({ name: 'organizationId' })
organization: Organization;
}

TypeOrm inserts same row

Here is my entities class
#Entity('leg')
export class Leg {
#PrimaryGeneratedColumn()
id: number;
#Column() startDateTime: Date;
#Column() endDateTime: Date;
#Column() cost: number;
#Column() price: number;
#OneToOne( type => Station,{
cascade: true,
})
#JoinColumn()
startStation: Station;
#OneToOne( type => Station,{
cascade: true,
})
#JoinColumn()
endStation: Station;
}
#Entity('station')
export class Station {
#PrimaryGeneratedColumn()
id: number;
#Column() stationCode: string;
#Column() lat: string;
#Column() lng: string;
#Column() publicNameShort: string;
#Column() publicNameLong: string;
#OneToOne( type => Address,{
cascade: true,
})
#JoinColumn()
address: Address;
}
#Entity('address')
export class Address {
#PrimaryGeneratedColumn()
id: number;
#PrimaryColumn() line1: string;
#PrimaryColumn() line2: string;
#PrimaryColumn() country: string;
#PrimaryColumn() state: string;
#PrimaryColumn() zip: number;
#PrimaryColumn() city: string;
#OneToMany( type => Billing,
billing => billing.address,)
billings: Billing[];
}
When I insert a new leg, TypeOrm inserts a new address each time even if that address exists in the table.
This is the address table I end up with. I do not want to insert the address if it exists.