TypeORM bulk insert? - express

How do i bulk insert multiple records in 1 query so my db will be efficient
I want to create Office and insert multiple new equipments into that office.
Table/model code:
OFFICE
#Entity({ name: 'offices' })
export class Office extends Timestamps {
#OneToMany(() => Equipment, (equipment: Equipment) => equipment.office, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
})
equipment: Array<Equipment>;
}
EQUIPMENT
#Entity({ name: 'equipment' })
export class Equipment extends Timestamps {
#Column({
name: 'equipment_id',
type: 'int',
nullable: false,
width: 2,
default: 0
})
equipment_id: number;
#ManyToOne(() => Office, (office: Office) => office.equipment)
#JoinColumn({ name: 'office_id' })
office: Office;
}

I suggest you to use cascades property in relationships:
#Entity({ name: 'offices' })
export class Office extends Timestamps {
#OneToMany(() => Equipment, (equipment: Equipment) => equipment.office, {
cascade: true, // <= here
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
})
equipment: Array<Equipment>;
}
Now if you set equipment when creating an instance of Office, it automatically insert an Equipment instance into database:
await Office.create({equipment: ['whatever']}).save();
Or if you use data mapper approach with repositories based on this link:
const officeRepository = connection.getRepository(Office);
const office = new Office();
const equipment1 = new Equipment(); // and set your properties or make more instances
office.equipment = [equipment1]; // or more instances
await officeRepository.save(office);

Related

Multiple OneToOne relation with TypeOrm doesn't work

I currently using NestJS 8.4.7 and TypeORM 0.3.10
I want to make two (or more) OneToOne relations in my entity class based on an existing SQL Database
Here is my code :
// Article Entity
#Entity({ name: 'node' })
export class Article {
#PrimaryGeneratedColumn('increment')
public articleId: number
#OneToOne(() => ArticleBody)
#JoinColumn({ name: 'articleId', referencedColumnName: 'bodyId' })
public body: ArticleBody
#OneToOne(() => ArticleTitle)
#JoinColumn({ name: 'articleId', referencedColumnName: 'titleId' })
public title: ArticleTitle
}
// Body Entity
#Entity({ name: 'article_body' })
export class ArticleBody {
#PrimaryGeneratedColumn()
public bodyId: number
#Column({ nullable: false })
public bodyValue: string
}
// Title Entity
#Entity({ name: 'article_title' })
export class ArticleTitle {
#PrimaryGeneratedColumn()
public titleId: number
#Column({ nullable: false })
public titleValue: string
}
And I have this error:
sqlMessage: ERROR [ExceptionsHandler] Unknown column 'Article__Article_body.titleId' in 'on clause'
sql: SELECT
`Article`.`articleId` AS `Article_nid`,
`Article__Article_body`.`bodyValue` AS `Article__Article_body_bodyValue`,
`Article__Article_title`.`titleValue` AS `Article__Article_title_titleValue`
FROM `Article` `Article`
LEFT JOIN `article_body` `Article__Article_body` ON Article__Article_body.titleId=`Article`.`articleId`
LEFT JOIN `article_title` `Article__Article_title` ON `Article__Article_title`.`titleId`=`Article`.`articleId`
WHERE (`Article`.`articleId` = 1)
It looks like if the last JoincolumnOptions overwrites all previous JoincolumnOptions
// Article Entity
#Entity({ name: 'node' })
export class Article {
#PrimaryGeneratedColumn('increment')
public articleId: number;
#OneToOne(() => ArticleBody)
#JoinColumn({ name: 'articleBodyId' })
public body: ArticleBody;
#OneToOne(() => ArticleTitle)
#JoinColumn({ name: 'articleTitleId' })
public title: ArticleTitle;
}
// Body Entity
#Entity({ name: 'article_body' })
export class ArticleBody {
#PrimaryGeneratedColumn("increment")
public bodyId: number;
#Column({ name: 'body_value', nullable: false })
public bodyValue: string;
}
// Title Entity
#Entity({ name: 'article_title' })
export class ArticleTitle {
#PrimaryGeneratedColumn("increment")
public titleId: number;
#Column({ name: 'title_value', nullable: false })
public titleValue: string;
}
In Article Entity your joinColumn name is same that's why the error is giving

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

How to filter the find method for a field on its relations?

The structure of my application is that I have:
Contacts
Groups
Lanes (belong to groups)
GroupContacts (join table to assign a contact to a group and assign a lane)
My entities are:
lane.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
Unique,
OneToMany,
} from 'typeorm';
import { Group } from './group.entity';
import { GroupContact } from './groupcontact.entity';
#Entity('lanes')
#Unique('UQ_NAMES', ['group', 'name'])
export class Lane {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
sequence: number;
#Column({ nullable: false, default: 30 })
updateFrequencyDays: number;
#Column({
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
type: 'timestamp',
})
createdAt: Date;
#ManyToOne(
type => Group,
group => group.lanes,
)
group: Group;
#OneToMany(
type => GroupContact,
groupcontact => groupcontact.lane,
)
groupContacts: GroupContact[];
}
groupcontact.entity.ts
import { Contact } from '../contacts/contact.entity';
import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm';
import { Group } from './group.entity';
import { Lane } from './lane.entity';
#Entity('groups_contacts')
export class GroupContact {
#PrimaryColumn()
groupId: number;
#PrimaryColumn()
contactId: number;
#Column()
laneId: number;
#Column({
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
type: 'timestamp',
})
createdAt: Date;
#ManyToOne(
type => Group,
group => group.contactConnection,
)
#JoinColumn({ name: 'groupId' })
group: Group;
#ManyToOne(
type => Contact,
contact => contact.groupConnection,
)
#JoinColumn({ name: 'contactId' })
contact: Contact;
#ManyToOne(
type => Lane,
lane => lane.groupContacts,
)
#JoinColumn({ name: 'laneId' })
lane: Lane;
}
contact.entity.ts
import { Group } from 'src/groups/group.entity';
import { GroupContact } from '../groups/groupcontact.entity';
import {
Entity,
Column,
PrimaryGeneratedColumn,
JoinTable,
ManyToMany,
OneToMany,
} from 'typeorm';
#Entity('contacts')
export class Contact {
#PrimaryGeneratedColumn()
public id: number;
#Column()
public firstName: string;
#Column()
public lastName: string;
#Column({
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
type: 'timestamp',
})
public createdAt: Date;
#OneToMany(
type => GroupContact,
gc => gc.contact,
)
groupConnection: GroupContact[];
}
What I am trying to do is that I am trying to return all contacts that belong to a particular group. But when returning those contacts, I'd also like to return the groupConnection object.
Here is the code I am using:
async getContacts(groupId) {
const group = await this.findOneByIdOrThrow(groupId);
const contacts = await this.contactRepository.find({
//where: { groupConnection: { groupId: groupId } },
relations: ['groupConnection'],
});
return contacts;
}
So far, am I able to return what I wanted, which is a list of contacts together with their relations. But I have not found a way to filter those results to retrieve only the ones that belong to a particular groupId.
Where clause commented.
How do I filter for that?
I was able to fix it by using QueryBuilder
const contacts = await this.contactRepository
.createQueryBuilder('contacts')
.innerJoinAndSelect('contacts.groupConnection', 'groupConnection')
.where('groupConnection.groupId = :groupId', { groupId })
.getMany();

EntityColumnNotFound: No entity column "authors" was found. many to many relations on TypeORM and NestJS

I am trying to get the books per user from the get request
I have the following problem, it turns out that I am doing a many-to-many relationship but it indicates that the authors entity was not found, I have already searched the TypeORM documentation but I cannot find anything, this is my code:
book.controller.ts
#Controller('book')
export class BookController {
constructor(
private readonly _bookService: BookService
) { }
#Get('author/:authorId')
getBooksByAuthor(
#Param('authorId', ParseIntPipe) authorId: number,
): Promise<ReadBookDto[]> {
return this._bookService.getBooksByAuthor(authorId);
}
}
book.service.ts
#Injectable()
export class BookService {
constructor(
#InjectRepository(BookRepository)
private readonly _bookRepository: BookRepository,
#InjectRepository(UserRepository)
private readonly _userRepository: UserRepository
) { }
async getBooksByAuthor(authorId: number): Promise<ReadBookDto[]> {
const books: Book[] = await this._bookRepository.find({
// This is where I have the entity of authors, it should be noted that the entity of books if I have it
where: { status: status.ACTIVE, authors: In([authorId]) },
})
console.log(books)
return books.map(book => plainToClass(ReadBookDto, book));
}
}
book.entity.ts
import {
BaseEntity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
Entity,
JoinColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { User } from '../user/user.entity';
#Entity('books')
export class Book extends BaseEntity {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar', length: 100, nullable: false })
name: string;
#Column({ type: 'varchar', length: 500 })
description: string;
#ManyToMany(type => User, user => user.books, { eager: true, primary: true})
#JoinColumn()
authors: User[];
#Column({ type: 'varchar', default: 'ACTIVE', length: 8 })
status: string;
#CreateDateColumn({ type: 'timestamp', name: 'created_at' })
createdAt: Date;
#UpdateDateColumn({ type: 'timestamp', name: 'updated_at' })
updatedAt: Date;
}
This is the user entity where I make the many to many relation
user.entity.ts
#Entity('users')
export class User extends BaseEntity {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar', unique: true, length: 25, nullable: false })
username: string;
#Column({ type: 'varchar', nullable: false })
email: string;
#Column({ type: 'varchar', nullable: false })
password: string;
#OneToOne(type => UserDetails, {
cascade: true,
nullable: false,
eager: true,
})
#JoinColumn({ name: 'detail_id' })
details: UserDetails;
#ManyToMany(type => Book, book => book.authors)
#JoinTable({ name: 'user_books' })
books: Book[];
}
If you could help me find the error it would be very helpful
Thanks
You can use queryBuilder to get the books:
#Injectable()
export class BookService {
constructor(
#InjectRepository(BookRepository)
private readonly _bookRepository: BookRepository,
#InjectRepository(UserRepository)
private readonly _userRepository: UserRepository
) { }
async getBooksByAuthor(authorId: number): Promise<ReadBookDto[]> {
const books: Book[] = await this._bookRepository.createQueryBuilder('books')
.leftJoinAndSelect("books.authors", "users")
.where('books.status = :status',{status : status.ACTIVE})
.andWhere("users.id = :id ", { id: authorId })
.getMany();
console.log(books)
return books.map(book => plainToClass(ReadBookDto, book));
}
}

TypeORM - Getting objects of provided id, which is one relation away

I want to get objects from table providing id, which is in relation with table, which is in another relation. It looks like this:
Hand is in relation manyToOne with Action (hand can have only one action),
Action is in relation manyToOne with Situation (action can have only one situation)
I'm trying to make GET request for hands in which I'm providing situationId.
Simplified entities:
#Entity()
export class Hand {
#PrimaryGeneratedColumn()
hand_id: number;
#Column()
hand: string;
#ManyToOne(type => Action, action => action.simplifiedhands, { eager: true, onDelete: 'CASCADE', onUpdate: 'CASCADE' })
action: Action;
}
#Entity()
export class Action {
#PrimaryColumn()
action_id: number;
#ManyToOne(type => Situation, situation => situation.actions, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
#JoinColumn({name: 'situation'})
situation: Situation;
#OneToMany(type => Hand, hand => hand.action)
hands: Hand[];
#OneToMany(type => Hand, hand => hand.action)
hands: Hand[];
}
#Entity()
export class Situation {
#PrimaryColumn()
situation_id: number;
#ManyToOne(type => Strategy, strategy => strategy.situations, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
strategy: Strategy;
#OneToMany(type => Action, action => action.situation)
actions: Action[];
}
What approaches didn't work for me so far (just example variants):
return await this.handsRepository.find({
relations: ["action", "action.situation"],
where: {
"situation": id
}
});
and
return await this.handsRepository.find({
join: {
alias: "hands",
leftJoinAndSelect: {
"action": "hand.action",
"situation": "action.situation"
}
},
where: {
"situation": id
}
});
Generally both 'works' but provide all the records, like there were no where condition.
The keys in the object you assign to where should be members of the entity of the repository, in your case Hand, since situation is a member of action it's not working. I'm surprised you didn't mention any errors.
You can do one of the following (example for postgres)
Using query builder:
return await this.handsRepository.createQueryBuilder(Hand, 'hand')
.leftJoin('hand.action', 'action')
.leftJoin('action.situation', 'situation')
.where('situation.id = :id', { id })
.getMany();
Or, you can try the following (success is not guaranteed):
return await this.handsRepository.find({
relations: ["action", "action.situation"],
where: {
action: {
situation: { id }
}
}
});