Typeorm how to get relations of relations - mysql

I am getting the Object ChatRoomEntity with entitymanager.findOne method. The ChatRoomEntity has the variable messages which is a OneToMany - ManyToOne Relation. I have no problems to select that but how do I get the user which sent the message. Its a variable on MessageEntity with a OneToMany Relation.
So basically I want to select a room and all messages of it. But all messages should also have their values on fromUser.
I select the room like this:
this.entityManager.findOne(ChatRoomEntity, {where: {id: roomToJoin.id}, relations: ['activeUsers', 'messages']}).then(roomEntity => {
// some code
}
Here my entities:
UserEntity
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#CreateDateColumn()
registrationDate: Date;
#ManyToMany(type => ChatRoomEntity, room => room.activeUsers, {cascade: true})
#JoinTable()
activeChatRooms: ChatRoomEntity[];
#OneToMany(type => ChatRoomMessageEntity, msg => msg.fromUser)
chatRoomMessages: ChatRoomMessageEntity[];
}
ChatRoomEntity
#Entity()
export class ChatRoomEntity {
#PrimaryGeneratedColumn()
id: number;
#Column('varchar', {nullable: true})
title: string;
#OneToMany(type => ChatRoomMessageEntity, chatrmsg => chatrmsg.chatRoom)
messages: ChatRoomMessageEntity[];
#ManyToMany(type => UserEntity, user => user.activeChatRooms)
activeUsers: UserEntity[];
}
ChatRoomMessageEntity
#Entity()
export class ChatRoomMessageEntity {
#PrimaryGeneratedColumn()
id: number;
#Column('varchar', {nullable: true})
message: string;
#CreateDateColumn()
creationDate: Date;
#ManyToOne(type => UserEntity, user => user.chatRoomMessages)
fromUser: UserEntity;
#ManyToOne(type => ChatRoomEntity, chatRoom => chatRoom.messages)
chatRoom: ChatRoomEntity;
}

We can load sub-relations by using 'relation.subrelation' within the relations array itself like this:
relations: ['relation1', 'relation2', 'relation2.subrelation1']
So for your case, instead of using join you can simply do something like this:
this.entityManager.findOne(ChatRoomEntity, {
where: {id: roomToJoin.id},
relations: ['activeUsers', 'messages', 'messages.fromUser'],
}).then(roomEntity => {
...
This is specified here: https://github.com/typeorm/typeorm/blob/master/docs/find-options.md#basic-options

Using QueryBuilder
await getRepository(UserEntity)
.createQueryBuilder('user')
.leftJoinAndSelect('user.profile', 'profile')
.leftJoinAndSelect('profile.images', 'images')
.getMany()
Using FindOptions
await getRepository(UserEntity).find({
relations: ['profile', 'profile.images']
})

Update v0.4.0
Typeorm is ditching findOne, you should use findOneBy for the queries without relations, and if you want relations, simply use find.
const users = await userRepository.find({
where: { /* conditions */ },
relations: { /* relations */ }
})
// users[0] if you want a first row

here is the #gradii/fedaco orm how to implement this feature.
use can use multi relations by method with
export class User extends Model {
#HasOneColumn({related: Profile})
profile;
}
export class Profile extends Model {
#HasOneColumn({related: Image})
image;
}
export class Image extends Model {
name;
}
eager load
User.createQuery().with('profile.image').get()
lazy load
const user = await User.createQuery().first();
const image await (await user.profile).image

Related

Problem saving ManyToMany relation in MySQL database via microservices

I have a relationship created between User and Groups, where a group can contain multiple users and a user can be present in more than one group. The relationship was built on the UserEntity and GroupsEntity entities. The problem occurs when the request is made in Postman. The User record is created, but the ids corresponding to the relationship between groups and user are not created in the new table (relationship of the two entities).
Help codes:
UserEntitity.ts
import { AssociateEntity } from 'src/associate/entities/associate.entity';
import { Associate } from 'src/associate/interfaces/associate.interface';
import { GroupsEntity } from 'src/groups/entities/group.entity';
import { Groups } from 'src/groups/interfaces/groups.interface';
import { SubsidiaryEntity } from 'src/subsidiary/entities/subsidiary.entity';
import { Subsidiary } from 'src/subsidiary/interfaces/subsidiary.interface';
import {
Column,
DeleteDateColumn,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
login: string;
#Column()
password: string;
#Column()
email: string;
#Column()
adm: boolean;
#Column()
passwordValidity: string;
#Column()
initials: string;
#Column()
system: string;
#ManyToOne(() => SubsidiaryEntity, (subsidiary) => subsidiary.id)
subsidiary: Subsidiary;
#ManyToMany(() => GroupsEntity, { cascade: true })
#JoinTable()
group: Groups[];
#Column({ default: true })
status: boolean;
#DeleteDateColumn()
deletedAt: Date;
#OneToMany(() => AssociateEntity, (associate) => associate.id)
associate: Associate[];
}
GroupsEntity.ts
import { UserEntity } from 'src/user/entities/user.entity';
import { User } from 'src/user/interfaces/user.interface';
import {
Column,
DeleteDateColumn,
Entity,
ManyToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
#Entity()
export class GroupsEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
description: string;
#Column({ default: true })
status: boolean;
#DeleteDateColumn()
deletedAt: Date;
#ManyToMany(() => UserEntity, (user) => user.group)
user: User;
}
user.service.ts (method create)
async create(user: User) {
try {
const groups: Array<Groups> = await this.groupService.findByIds(
user.groups,
);
//#ts-ignore
user.groups = groups;
const newUser = await this.userRepository.save(user);
return newUser;
} catch (error) {
this.logger.error(`error: ${JSON.stringify(error.message)}`);
throw new RpcException(error.code + error.message);
}
}
The structure of the bank is given by:
Bank entity relationships
The code doesn't report errors, but it doesn't create the relationship with the ids either. I'm using microservices in nestJS, the language is TypeScript and the database used is MySQL.
Join table you have define the join Column and inverseJoinColumn Details. Example
#JoinTable({
name: 'cat_use_cloth',
joinColumn: { name: 'cat_id', referencedColumnName: 'id'},
inverseJoinColumn: { name: 'cloth_id', referencedColumnName: 'id'},
})
Ref: https://medium.com/#rodrigo.tornaciole/nest-js-many-to-many-relationship-using-typeorm-and-crud-ec6ed79274f0

Update a many-to-many relationship with TypeORM, Nest JS

I'm having trouble updating an entity that has many-to-many.
Below are the product entities and categories.
#Entity()
class Products {
#PrimaryGeneratedColumn()
public id: number;
#Column()
public title: string;
#Column()
public content: string;
#ManyToMany((type) => Category, (category) => category.products)
#JoinTable()
#Type(() => Category)
categories: Category[];
}
#Entity()
class Category {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany((type) => Products, (products) => products.categories, {
cascade: true,
})
#Type(() => Products)
products: Products[];
}
Update in my service.
async updateProduct(id: number, product: UpdateProductDto) {
await this.service.update(id, product);
const updatedProduct = await this.service.findOne(id, {
relations: ['categories'],
});
if (updatedProduct) {
return updatedProduct;
}
throw new ProductNotFoundException(id);
}
I do not understand what exactly the problem is, since the addition works, the data in the database is there, I send a change request with data similar to how to create it.
{
"content": "test",
"title": "test",
"categories": [
{
"id": 3
}
]
}
Error message
[ExceptionsHandler] column "productsId" of relation "products" does not exist +23003ms
QueryFailedError: column "productsId" of relation "products" does not exist
at new QueryFailedError (...\node_modules\typeorm\error\QueryFailedError.js:12:28)
at PostgresQueryRunner.<anonymous> (...\node_modules\typeorm\driver\postgres\PostgresQueryRunner.js:248:31)
at step (...\node_modules\typeorm\node_modules\tslib\tslib.js:143:27)
at Object.throw (...\node_modules\typeorm\node_modules\tslib\tslib.js:124:57)
at rejected (...\node_modules\typeorm\node_modules\tslib\tslib.js:115:69)
at processTicksAndRejections (internal/process/task_queues.js:93:5)

NestJs update Many-To-Many relation with join table

I have two entities - Property and Owner. One Property can have a lot of Owners and Owner can have a lot of Properties. For join use property_owner table. How to update this many-to-many relation using NestJS/TypeORM?
#Entity('property')
export class Property extends EntityModel {
#Column({ length: 255, nullable: false })
name: string;
#ManyToMany(type => Owner, { cascade: true })
#JoinTable({
name: 'property_owner',
joinColumn: { name: 'propertyId', referencedColumnName: 'id'},
inverseJoinColumn: { name: 'ownerId', referencedColumnName: 'id'},
})
owners: Owner[];
}
#Entity('owner')
export class Owner extends EntityModel {
#Column({ length: 255, nullable: false })
name: string;
#ManyToMany(type => Property, { cascade: true })
#JoinTable({
name: 'property_owner',
joinColumn: { name: 'ownerId', referencedColumnName: 'id'},
inverseJoinColumn: { name: 'propertyId', referencedColumnName: 'id'},
})
properties: Property[];
}
Below my service's methods for save and update:
public create(req: Request): Promise<Dto> {
const dto: CreateDto = {
...req.body,
owners: this.formatJoinData(req.body.owners) //[1,2,3] => [{id:1},{id:2},{id:3}]
};
const entity = Object.assign(new Entity(), dto);
return this.repo.save(entity);
}
public update(req: Request): Promise<UpdateResult> {
const dto: EditDto = {
...req.body,
owners: this.formatJoinData(req.body.owners) //[1,2,3] => [{id:1},{id:2},{id:3}]
};
const id = req.params.id;
const entity = Object.assign(new Entity(), dto);
return this.repo.update(id, entity);
}
Saving new Property work fine, but when I try update property I get error
[ExceptionsHandler] column "propertyId" of relation "property" does not exist
Owners data in both cases looks like [{id:1},{id:2},{id:3}]. I think problem in save/update methods results. Save method return to us Entity with id and update method return to us UpdateResult which not contain Entity id. But may be we can transform/additionally define this value somewhere...
I found solution. Need to call save method instead update.
In my case update will be looks like
import {plainToClass} from 'class-transformer';
public async update(req: Request): Promise<Dto> {
const found = await this.repo.findOneOrFail(req.params.id, {
relations: ['owners', 'electricMeter'],
});
const dto = {
...found,
...req.body,
owners: this.formatJoinData(req.body.owners) //[1,2,3] => [{id:1},{id:2},{id:3}]
updatedBy: this.getUser(req),
updatedDate: Date.now(),
};
return this.repo.save(plainToClass(Entity, dto));
}
This code can be improved, but think that main idea is clear.
https://typeorm.io/#/many-to-many-relations documentation doesn't say more than use JoinTable decorator, and we don't know what you havr in your request, but it looks like you're passing wrong values. These fields are virtual, at the end with m2m relationship third table is created to handle relationship.

TypeORM bulk create with relations

Is there a way to insert large amount of datas without blowing the JS heap memory ? I have a model which is Email as follow :
#Entity("email")
export class Email extends BaseEntity {
#PrimaryGeneratedColumn()
public id: number;
#ManyToOne((type) => Category, (cat) => cat.category, {nullable: false, cascade: ['insert']})
public category: Category;
#Column({type: "text", name: "email"})
public email: string;
}
and Category :
#Entity("category")
export class Category extends BaseEntity {
#PrimaryGeneratedColumn()
public id: number;
#Column({type: "text", name: "category"})
public category: string;
#OneToMany((type) => Email, (email) => email.category, {nullable: true})
public emails: Email[];
}
First problem I had is when I try to save {email: 'blabal#blalbah.com', category: 'default'} it says that Category must be an ID, but the thing is I want to add emails and create the category if it doesnt exist or asign the ID to the email if it exists. I did the following code :
public async bulkCreate(emails: Email[]): Promise<any> {
try {
const emailRepo = await getRepository(Email);
const categoryRepo = await getRepository(Category);
await Promise.all(emails.map(async (mail) => {
const cat = await categoryRepo.findOne({where: {category: mail.category}});
if (cat) {
// #ts-ignore
mail.category = cat.id;
} else {
const newCat = await categoryRepo.save(Object.assign(new Category(), mail));
// #ts-ignore
mail.category = newCat.id;
}
await emailRepo.save(mail);
}));
} catch (e) {
console.log(e);
throw new Error(e);
}
}
Worked for a few emails, but when I try to add even only 1,000 memory goes up to Like 4Gig and just crash.
What should I do? I'd like to add more than 1,000 emails at once.
I know it's little bit late, but solution for this use Bluebird Promise.map so you can define concurrency. instead executing in one run.

How do i write this query using TypeORM's QueryBuilder?

I ran this code directly on mySQL server and i'm getting the desired results, now how do i replicate this in my code using typeORM's queryBuilder?
SELECT user.id, user.email, user.image, user.phone_number,
user.merchantLocationId, ugg.userGroupsId AS groupId, ug.description AS
groupName, ur.id as roleId, ur.description AS role_name, ugr.priviledges
FROM ipay.user, ipay.user_user_group_user_groups AS ugg,
ipay.user_group_roles AS ugr, ipay.user_groups AS ug,
ipay.user_roles AS ur
WHERE user.id = ugg.userId AND ugg.userGroupsId = ugr.group_id AND
ugr.role_id = ur.id AND user.email = "press#xyz.com";
It's a bit difficult to translate that sort of query into typeorm. The closest example I could give would be something like this
#Injectable()
export class UserService {
constructor(#InjectRepository(User) private readonly userRepository: Repository<User>) {}
async findUserByEmail(email: string): Promise<User | null> {
return await this.userRepository({
relations: ['roles', 'groups'],
where: {
email,
},
});
}
}
Where your UserEntity looks like this
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column({
unique: true,
})
email: string;
#ManyToMany(type => Group, group => group.users)
groups: Group[];
#ManyToOne(type => Role, role => role.users)
role: Role;
}
It's not an exact match but hopefully will give you some insight into how you can achieve your query.