get items with no child relation or child with certain parameters - typeorm

how can i get all bookings with no payments or with payments with status pending.
Something like "whereDoesntHave" on Laravel
Thanks!
#Entity()
export class Booking extends BaseEntity{
#Column({type: 'varchar', length: 10})
code: string
#OneToMany(() => Payment, Payment => Payment.booking, { cascade: true })
payments: Payment
}
#Entity()
export class Payment extends BaseEntity {
#ManyToOne(() => Booking, { nullable: false })
booking: Booking
#Column({ type: "float" })
amount: number
#Column()
status: PaymentStatus;
}

First, you should add a foreign key to Payment entity called bookId:
#Entity()
export class Payment extends BaseEntity {
#Column({nullable: true})
bookId:number;
#ManyToOne(() => Booking, { nullable: false })
#JoinColumn({ name: 'bookId' }) // <= and add JoinColumn here
booking: Booking
#Column({ type: "float" })
amount: number
#Column()
status: PaymentStatus;
}
Then, you should get all bookings where bookId on Payment is null:
await Booking.find({
relations:['payments'],
where:[
'payments.status': 'pending',
bookId: null
]
})
[] in where represents OR in the query.

Related

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

TypeORM relation through third table won't work

Following the TypeORM tutorial (this part - https://typeorm.io/#/many-to-many-relations/many-to-many-relations-with-custom-properties) i created the models in my app:
#Entity({ name: 'organizations'})
export class OrganizationEntity extends AbstractEntity {
#Column()
name: string;
#Column({ nullable: true })
description: string;
#Column({ nullable: true })
logo: string;
#ManyToOne(() => UserEntity, { nullable: true })
mainContactUser: UserEntity;
#OneToOne(() => LicenseEntity, license => license.organization)
license: LicenseEntity;
#OneToMany(() => UserRoleEntity, userRole => userRole.user)
users!: UserRoleEntity[]
}
#Entity({ name: 'users' })
export class UserEntity extends AbstractEntityWithDates {
#Column({ nullable: false })
firstName: string;
#Column({ nullable: false})
lastName: string;
#Column({ nullable: true })
jobTitle: string;
#Column({ unique: true })
email: string;
#Column({ nullable: true })
phone: string;
#Column({ nullable: true })
avatar: string;
#Column({ nullable: true })
oAuthIdentityId: string;
#OneToMany(() => UserRoleEntity, userRole => userRole.user)
userRoles: UserRoleEntity[]
}
and finally:
#Entity({ name: 'user_roles' })
export class UserRoleEntity extends AbstractEntity {
#ManyToOne(() => UserEntity, user => user.userRoles)
user: UserEntity;
#ManyToOne(() => OrganizationEntity, organization => organization.users, { nullable: true })
organization: OrganizationEntity;
#ManyToOne(() => ProjectEntity, { nullable: true })
project: ProjectEntity;
#Column({
type: "enum",
enum: RoleType,
nullable: true
})
role: string;
}
Then in a service i'm performing a query where i want to fetch all organizations with at least related user roles:
const found = await this.organizationRepository
.createQueryBuilder("organization")
.leftJoinAndSelect("organization.users", "users")
.getMany();
No errors but the query generated by TypeORM is as follows:
SELECT "organization"."id" AS "organization_id", "organization"."name" AS "organization_name", "organization"."description" AS "organization_description", "organization"."logo" AS "organization_logo", "organization"."main_contact_user_id" AS "organization_main_contact_user_id", "users"."id" AS "users_id", "users"."role" AS "users_role", "users"."user_id" AS "users_user_id", "users"."organization_id" AS "users_organization_id", "users"."project_id" AS "users_project_id"
FROM "organizations" "organization"
LEFT JOIN "user_roles" "users" ON "users"."user_id"="organization"."id"
This part:
...ON "users"."user_id"="organization"."id"
is obviously incorrect but i can't see why?
Any help would be appreciated.
Your OrganizationEntity has an error in the OneToMany config; You set userRole.user, but it should be userRole.organization, which is the field in that table that links to this field.
#OneToMany(() => UserRoleEntity, userRole => userRole.organization)
users!: UserRoleEntity[]
Also, you should add primary keys to your tables. A safe approach by default is to use a Generated column. You can add it to your 3 tables. For the many-to-many table you could create a compound key, or simply an index with user_id, and company_id, but with tradeoffs - so just use an id on every table to get started.
#PrimaryGeneratedColumn()
id: string;

Typeorm BaseEntity create function: how to deeply create an entity?

I have a Patient entity:
#Entity()
#ObjectType()
#InputType('PatientInput')
export class Patient extends BaseEntity {
#PrimaryGeneratedColumn()
#Field(type => Int, { nullable: true })
id: number
#Column({ length: 500 })
#Field({ nullable: true })
firstName?: string
#Column({ length: 500 })
#Field({ nullable: true })
lastName?: string
#Field(type => MedicalCondition, { nullable: true })
#OneToMany(type => MedicalCondition, medicalCondition => medicalCondition.patient, { cascade: true })
medicalConditions?: MedicalCondition[]
}
And a MedicalCondition entity:
#ObjectType()
#Entity()
#InputType('medicalCondition')
export class MedicalCondition extends BaseEntity {
#Field({nullable: true})
#PrimaryGeneratedColumn()
id: number
#Field(type => Int, { nullable: true })
#RelationId((medicalCondition: MedicalCondition) => medicalCondition.patient)
#Column()
patientId?: number
#Field(type => Patient, { nullable: true })
#ManyToOne(type => Patient, patient => patient.medicalConditions, {
onDelete: 'CASCADE',
})
#JoinColumn()
patient?: Patient
#Field()
#Column()
name: string
#Field({nullable: true})
#Column()
startDate?: Date
}
When trying to create an instance of patient using the BaseEntity create function, the patient is created but in the medical conditions array only containes the last element and all the rest disappear even if there were many elements.
#Mutation(returns => Patient)
async create(#Args('patient') newPatientInput: Patient, #CurrentUser() user: User, #Useragent() useragent): Promise<Patient> {
const newPatient: Patient = Patient.create(newPatientInput)
const event = createEvent({ useragent, actionType: 'add_patient', userId: user.id })
const p = await this.patientService.create(newPatient, event)
return p
}
create = async (patient: Patient, event: EventLog): Promise<Patient> => {
const isExist = await this.isPatientExist({ firstName: patient.firstName, lastName: patient.lastName, birthDate: patient.birthDate })
if (isExist > 0) {
throw new Error('Patient already exist')
} else {
const transactionResult = runTransaction(async (em) => {
const eventLog = EventLog.create(event)
await em.save(eventLog)
return await em.save(patient)
})
return transactionResult
}
}
I tried to directly save the entity without invoting create():
return await em.save(Patient, patient)
and that was the result of the saving:
medicalConditions:
[ { id: 36,
name: 'heartDisease',
startDate: 2016-07-31T21:00:00.000Z,
kin: 'father',
note: '',
patientId: 26,
createdAt: 2019-11-24T17:11:22.376Z,
updatedAt: 2019-11-24T17:11:22.376Z },
{ id: null,
name: 'previousHeartAttack',
startDate: 2018-04-30T21:00:00.000Z,
kin: 'brother',
note: '' },
{ id: null,
name: 'highBloodPressure',
startDate: 2018-03-31T21:00:00.000Z,
kin: 'sister',
note: '' } ],
Tried google for it and didn't find any known issue.
So the willing result would be to create an entity deeply, is that a possible behavior?

TypeORM: Insert an entity which has many to many relation with another entity (based on existing records)

I have 2 following entities with Many To Many relationship
User entity (relationship owned by the User entity)
import { Entity, Column, PrimaryGeneratedColumn, UpdateDateColumn, ManyToMany, JoinTable, CreateDateColumn } from 'typeorm';
import { Role } from './role.schema';
#Entity('Users')
export class User {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({
name: 'Email',
length: 100,
unique: true
})
email: string;
#Column({
name: 'FirstName',
length: 30
})
firstName: string;
#Column({
name: 'LastName',
length: 30
})
lastName: string;
#ManyToMany(type => Role, { eager: true })
#JoinTable({
name: 'UserRoles',
joinColumns: [
{ name: 'UserId' }
],
inverseJoinColumns: [
{ name: 'RoleId' }
]
})
roles: Role[];
}
Role entity (with two existing roles: Admin and User)
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany } from 'typeorm';
import { User } from './user.schema';
#Entity("Roles")
export class Role {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({
name: 'Name',
length: 50,
unique: true
})
name: string;
#ManyToMany(type => User, user => user.roles)
users: User[];
}
the design of application is such that Role name is sent in request in the form of an array e.g. ['Admin', 'User']
Now while inserting a User,
currently I first retrieve Role object from database based on the role name array received in request (to assign a desired role),
then assign it to the User object (roles property) and
then finally call save method on User object to insert the record in User table.
Snippet:
createConnection(connectionConfig as ConnectionOptions).then(async connect => {
let userRepo = connect.getRepository(User);
let roleRepo = connect.getRepository(Role);
let roles = ['Admin', 'User'];
let user = userRepo.create();
return roleRepo.createQueryBuilder('role').where('role.name IN (:roleNames)', { roleNames: roles }).getMany().then((roles) => {
user.email = 'test1#test.test';
user.firstName = 'TestFName';
user.lastName = 'TestLName';
user.roles = roles;
return userRepo.save(user)
})
}).catch(error => {
console.log(error);
});
This results in many database calls. It would be great if some one can enlighten me with smarter and more elegant way ( using fluent QueryBuilder to achieve above result)

Resources