Create query builder that the source table (FROM) is a join table in TypeORM - sql

I'm trying to implement the following SQL in TypeORM using QueryBuilder:
SELECT
user_places.user_id,
place.mpath
FROM
public.user_root_places_place user_places
INNER JOIN
public.place place
ON place.id = user_places.place_id
The entities are:
#Entity()
export class User {
#Column({ unique: true, primary: true })
id: string;
#ManyToMany(() => Place)
#JoinTable()
rootPlaces: Place[];
}
#Entity()
export class Place {
#PrimaryGeneratedColumn()
id: number;
#Column()
mpath: string;
}
When you create a query builder you have to use some entity or table but the join table is "hidden" by TypeORM
I know I can replace the inner join table order and it will solve the problem but I'm looking for when the source table is the join table

If you don't want to use the generated name just specify explicitly the join table name
#Entity()
export class User {
#Column({ unique: true, primary: true })
id: string;
#ManyToMany(() => Place)
#JoinTable({
name: 'user_places' // <---
})
rootPlaces: Place[];
}
And then:
createQueryBuilder('user_places')
.select(['user_places.userId', 'place.mpath'])
.innerJoin(Place, 'place', 'place.id = user_places.place_id')
.getMany();

Related

Typeorm - Converting SQL Left Join one to one relation into typeorm query builder

I have following entities in a postgresql database using Typeorm:
#Entity('issuer')
export class Issuer {
#PrimaryColumn()
issuer_id: string
#OneToOne(() => UserData, { cascade: true })
#JoinColumn({ name: 'issuer_id', referencedColumnName: 'public_address' })
u_data: UserData
#BeforeInsert()
newid() {
this.issuer_id = this.u_data.public_address
}
...remaining columns...
}
#Entity('user_data')
export class UserData {
#PrimaryColumn({ type: 'varchar', unique: true })
email: string
#Column({ type: 'varchar', nullable: false, unique: true })
public_address: string
...remaining columns...
}
Above in the Issuer entity, I am doing a small trick to be able to make a key both primary and foreign, issuer_id column, which is primary key of Issuer and foreign key of UserData which refers to public_address column of UserData. I wanna join both entities, and I am able to do it with the following query:
SELECT *
FROM user_data
LEFT OUTER JOIN issuer ON issuer.issuer_id = user_data.public_address
WHERE user_data.email = $1
I am unable to convert this simple SQL code into Typeorm query builder. Here is my failed attempt:
await this.userRepository
.createQueryBuilder('user')
.leftJoin('user.public_address', 'issuer')
.where('user.email = :email', { email })
.getOne()
Here is the error I am getting:
TypeORMError: Relation with property path public_address in entity was not found.
It seems when trying to left join (right join doesn't exist on typeorm) from an entity that has no direct relation to its relative, leftJoinAndSelect function should be used with a condition:
return await this.userRepo
.createQueryBuilder('user')
.leftJoinAndSelect(Issuer, 'issuer', 'user.public_address = issuer.issuer_id')
.where('user.email = :email', { email })
.getRawOne()

How do I join two entities linked with 1-1 relationship and add the joined entity on a particular property of the first entity?

I have 2 entities with a one to one relationship between them created with typeorm.
#ObjectType()
#Entity()
export class Interaction extends BaseEntity {
#Field()
#PrimaryGeneratedColumn()
id!: number;
#Field(() => AF_Metadata)
#JoinColumn()
#OneToOne(() => AF_Metadata, { eager: true })
metadata: AF_Metadata;
}
}
#ObjectType()
#Entity()
export class AF_Metadata extends BaseEntity {
#Field()
#PrimaryGeneratedColumn()
id!: number;
//Relationships
#OneToOne(() => Interaction, { onDelete: "CASCADE" })
interaction: Interaction;
}
How do I join these entities by adding the AF_Metadata entity to the metadata property in the Interaction entity. I am able to add the whole AF_Metadata object to the interaction but not to the metadata propery. This is my sql query so far.
select * from interaction int
left join af_metadata metadata on int."metadataId" = metadata.id
order by int."createdAt" DESC
Current result:
{
id: 8327,
metadataId: 1,
}
Desired result:
{
id: 8327,
metadata: {
metadataId: 1
}
}

Many To Many Relation only accecepts Unique Values

Orders can have 0 to n Items in them. One Item can belongs to 0 to n Orders.
I have the Relationship set up the following way
#Entity()
export class Order {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => Customer, (customer) => customer.orders, {
eager: true,
})
customer: Customer;
#JoinTable()
#ManyToMany(() => Item, { eager: true })
items: Item[];
}
But I can only add Unique Items to my order. When I try to safe a item twice, it gets ignored?
This is the code for adding Items to a order
async addItemToOrder(orderId: number, itemId: number) {
const order = await this.findOne(orderId);
const item = await this.itemService.findOne(itemId);
if (!order.items) {
order.items = [];
order.items = [...order.items, item];
} else {
order.items = [...order.items, item];
}
order.totalPrice = this.calcTotalPrice(order.items);
await this.orderRepository.save(order);
return order;
}
This is the item
#Entity()
export class Item {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
price: number;
}
It is working as intended. In the underlying join table a itemId gets connected to a orderId. Typeorm only inserts a new entry if it cannot find a combination of itemId and orderId.
For your usecase it would make sense to define a own join table that includes a amount attribute. So your join table looks like this
itemId
orderId
amount
1
1
2
1
2
1
2
2
5
You can achive this using typeorm like this:
You create a new Entity that is the join entity between an item and an order and includes a attribute amount
#Entity()
export class OrderItem {
#Column('int')
amount: number;
#ManyToOne(() => Item, item => item.orders, { primary: true })
item: Item;
#ManyToOne(() => Order, order => order.items, { primary: true })
order: Order;
}
#Entity()
export class Order {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => Customer, (customer) => customer.orders, {
eager: true,
})
customer: Customer;
#OneToMany(() => OrderItem, { eager: true })
items: OrderItem[];
}
#Entity()
export class Item {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
price: number;
#OneToMany(() => OrderItem, { eager: true })
orders: OrderItem[];
}
The reason you do this is normalization. Relational databases rely on normalization to prevent inconsistent data.
You can read more about normalization here

About how to use many to one in TypeORM

For the table which has Foreign key, I want to assign ManyToOne's decorator.
I know #ManyToOne(() => User, user => user.photos) is just table relation,
What its argument () => User, user => user.photos means?
And please tell me user: User's property and value mean.
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
import {User} from "./User";
#Entity()
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#ManyToOne(() => User, user => user.photos)
user: User;
}
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm";
import {Photo} from "./Photo";
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToMany(() => Photo, photo => photo.user)
photos: Photo[];
}
it just sets the inverse relationship so if you want you could query the other way back. For example:
await this.photoRepository.find({
loadEagerRelations: true,
relations: ['user'],
})
and you would have something like :
[
{
"id": 1,
"url": "https://twetew",
"user": {
"id": 1,
...
...
}
}
]
TypeORM needs this to understand the relation and create the correct reference. In your photos database table it will create a user_id column. User won't have a photo_id.
So:
#ManyToOne(() => User, user => user.photos)
user: User;
creates a user_id column on photos database table and lets you query the other way back.

TypeORM getRepository.find() does not include Foreign Key Fields

I am trying to fetch all the columns included on my entity, but I only able to fetch the columns that does not have any relationship from the other entity.
I use this block of codes to fetch the all the rows to this repository.
private translationTextRepository = getRepository(TranslationText);
async all(request: Request, response: Response, next: NextFunction) {
return this.translationTextRepository.find();
}
And here's the entity for this repository.
#Entity('TranslationText')
export class TranslationText {
#PrimaryGeneratedColumn()
ID: number;
#Column()
CreatedBy: string;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
CreatedDate: Date;
#Column()
Status: boolean;
#Column({ nullable: true, default: null })
ModifiedBy: string;
#Column({ type: 'timestamp', nullable: true, default: null })
ModifiedDate: Date;
#Column()
Text: string;
#ManyToOne((type) => Locale, (locale) => locale.ID)
#JoinColumn({ name: 'LocaleID' })
LocaleID: Locale;
#ManyToOne((type) => TranslationTitle, (translationTitle) => translationTitle.ID)
#JoinColumn({ name: 'TranslationTitleID' })
TranslationTitleID: TranslationTitle;
}
But I was only able to fetch all the columns except the LocaleID and the TranslationTitleID.
How can I achieve this?
Check this document:
https://typeorm.io/#/relations-faq/how-to-use-relation-id-without-joining-relation
solution:
define new column:
#column()
LocaleID: number
rename old one to : Locale
But typeOrm cannot sync your table due to foreign key problem.
use eager option in #ManyToOne({eager: true})
The search result will contain relation Locale object, you can take id from it.
Can you try to specify the relations like that:
async all(request: Request, response: Response, next: NextFunction) {
return this.translationTextRepository.find({
relations:["LocaleID","TranslationTitleID"]
});
}
Because you have to make explicit that you want your relations on the query.