alias was not found. Maybe you forget to join it - javascript

In my nectjs project I'm using TypeORM and I have 2 entities user and post,
and I'm tying to make a relation between them
user.entity.ts
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 50, unique: true })
name: string;
#OneToMany(type => Post, post => post.user)
posts: Post[];
}
post.entity.ts
#Entity()
export class Post {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 30 })
title: string;
#ManyToOne(type => User, user => user.posts)
user: User;
}
So I want to join these tables and get post by it's title for specific user
const PostObject = await createQueryBuilder("post")
.leftJoinAndSelect(
"post.user",
"user",
"post.title = :title",
{ title: "title1" }
)
.where("user.id = :id", { id: id })
.getOne();
but when I run the project and execute this function I get this error:
Error: "post" alias was not found. Maybe you forget to join it?

You have to inject the Repository first:
constructor(
#InjectRepository(Post) private readonly postRepository: Repository<Post>
) {
}
Then you can create the QueryBuilder for the Post entity by passing in the alias:
this.postRepository.createQueryBuilder('post')
// ^^^^^^^^^^^^^^^^
.leftJoinAndSelect(
'post.user',
'user',
'post.title = :title',
{ title: 'title1' },
)
.where('user.id = :id', { id })
.getOne();

Can also use getRepository like below.
const PostObject = await getRepository("post").createQueryBuilder("post")
.leftJoinAndSelect(
"post.user",
"user",
"post.title = :title",
{ title: "title1" }
)
.where("user.id = :id", { id: id })
.getOne();

Related

Get the child type from the parent in typeorm

I have a typeorm entity that uses single table inheritance:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
#ObjectType({ isAbstract: false })
export class OrganisationEntity extends BaseEntity {
#Field()
#Column()
name: string;
#Field(() => [UserEntity])
#OneToMany(() => UserEntity, (user) => user.organisation)
users: UserEntity[];
}
and some child entities:
#ChildEntity()
#ObjectType()
class MedicalOrganisation {
}
#ChildEntity()
#ObjectType()
class SoftwareOrganisation {
}
#ChildEntity()
#ObjectType()
class MedicalOrganisation {
}
I'm wondering how I can get the type and the child properties from the parent organisation so that I can do something like:
const organisation = await OrganisationEntity.findOne()
if(organisation.type === "medicalOrganisation"){
...
}
But it seems I'm not allowed to access the type property through the parent. Does anyone know how this can be done?
I'd prefer not to use instanceof because it requires the child entities and is causing circular dependencies.
You've got two options. Either use .createQueryBuilder and get the item using getRawOne and the returned object would contain a field named OrganisationEntity_type which can be used to do the checks. It's value would either be 'MedicalOrganisation', or 'SoftwareOrganisation' and so on.
const orgRepository: Repository<OrganisationEntity> = getRepository(OrganisationEntity);
let organisation = await orgRepository.createQueryBuilder().getRawOne();
// organisation = {
// OrganisationEntity_name: 'name',
// OrganisationEntity_users: [
// {},
// {}
// ],
// OrganisationEntity_type: 'MedicalOrganisation'
// }
Or, you could add the field type in the OrganisationEntity itself like this:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
#ObjectType({ isAbstract: false })
export class OrganisationEntity extends BaseEntity {
#Field()
#Column()
name: string;
#Field(() => [UserEntity])
#OneToMany(() => UserEntity, (user) => user.organisation)
users: UserEntity[];
#Column
type: string;
}
and do a straightforward:
let organisation = await OrganisationEntity.findOne();
// organisation =
// MedicalOrganisation {
// name: 'name',
// users: [{}, {}],
// type: 'MedicalOrganisation'
// }
and you get the type field in the object itself.

How to add create an entity inside another entity (NestJS)?

I have two entities: User and Photo with relationship OneToMany. One user can have many photos.
User:
#Entity('users')
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Exclude()
#Column()
password: string;
#Column({ default: '' })
avatar: string;
#OneToMany(() => Photo, photo => photo.user, { cascade: true })
photos: Photo[];
}
And Photo:
#Entity('photos')
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#Column({default: ""})
description: string;
#ManyToOne(() => User, user => user.photos)
user: User;
}
So, my question is: how can i add new photo to specific user? I tried to do something like this, but it doesn't work as expected.
#Patch(':id/photos')
addPhoto(#Param('id') id: number, #Body() createPhotoDto: CreatePhotoDto){
return this.userService.createImage(id, createPhotoDto);
}
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
const newUser = await this.usersRepository.preload({
id: +id,
photos: [...user.photos, createPhotoDto]
});
return this.usersRepository.save(newUser);
}
What I got from the CreatePhotoDto that you save the image and bind it with the user,
one of the easiest way to do it, First declare you imageRepository in your service then :
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
return await this.imageRepository.save({url:createPhotoDto.url,user:user});
// if you want to return the user with the image added you make another find with the relation exp:
// return await this.usersRepository.findOne(id,{ relations: ['photos'],});
}

automapper-ts returns empty object using typeorm

I'm trying to use automapper-ts to map from dto to entity and vice versa.
The creation of the mappings seems fine to me but when I try to map I always get an object without values.
The config:
automapper
.createMap(CityDTO, City)
.forMember('createdBy', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore())
.forMember('createdAt', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore())
.forMember('updatedBy', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore())
.forMember('updatedAt', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore());
// These are properties present in the entity but not in the DTO
And this is the mapping method:
const fromCityToCityDto = (source: CityDTO, destination?: City): City => {
if (!destination) {
destination = new City();
}
// This produces 'source name' and 'source population'
Object.keys(source).forEach((key) => console.log('source', key));
// This never runs, it seems it doesn't have any key
Object.keys(destination).forEach((key) => console.log('destination', key));
destination = automapper.map(CityDTO, City, source);
return destination;
};
As ORM I'm using typeorm. This is the entity class:
export class EntityBase extends BaseEntity {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({ name: 'CreatedBy', length: 50 })
createdBy: string;
#CreateDateColumn({ name: 'CreatedAt' })
createdAt: Date;
#Column({ name: 'UpdatedBy', nullable: true, length: 50 })
updatedBy: string;
#CreateDateColumn({ name: 'UpdatedAt', nullable: true })
updatedAt: Date;
}
#Entity('Cities')
export class City extends EntityBase {
#Column({ name: 'Name', length: 50 })
name: string;
#Column({ name: 'Population' })
population: number;
}
EDIT: This is the result of console.log:
source id
source name
source population
source createdBy
source createdAt
source updatedBy
source updatedAt
Looks like the destination doesn't have any property.

TypeORM Throwing Duplication Error on Bulk saving instead of ignore or update existing value

As Documented on TypeOrm FrameWork Repository.save should save/insert new values and ignore/update the existing once,
But now I'm facing a problem that it's thrown a duplication error on existing value and stoping the whole inserting! ( I have a unique column called key )
My entity:
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { Source } from '../sources/source.entity';
#Entity({ name: 'articles' })
export class Article {
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
#Column({
nullable: true
})
image: string | null;
#Column({
type: "text",
})
url: string;
#Column({
type: "varchar",
unique: true
})
key: string;
#Column({
type: 'datetime',
nullable: true
})
date: Date | null;
#ManyToOne(type => Source, source => source.articles, {eager: true})
#JoinColumn({name: 'source'})
source: Source;
#Column({
type: `text`,
nullable: true
})
description: string | null
}
My Service:
constructor(
#InjectRepository(Article) private readonly articleRepository: Repository<Article>,
private readonly articlesScraper: BlogScraperService
) {
}
async clonningFromScraper() {
let articles = await this.articlesScraper.articles('1');
articles = articles.map(article => ({ ...article, key: decodeURIComponent(article.url).substring(0, 255) }));
return this.articleRepository
.save(articles);
}
I have ended up solving this by RAW SQL query using the following
return this.articleRepository.query(
"INSERT IGNORE INTO articles ( title, date, url, image, source, description, _key ) VALUES ?", [querableArticles]);

How to save relation in #ManyToMany in typeORM

There are 2 entities named Article and Classification. And the relation of them is #ManyToMany.
Here's my question: How to save the relation?
My code as below:
#Entity()
export class Article {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#CreateDateColumn()
createTime: Date;
#UpdateDateColumn()
updateTime: Date;
#Column({
type: 'text',
})
content: string;
#Column({
default: 0,
})
likeAmount: number;
#Column({
default: 0,
})
commentAmount: number;
}
#Entity()
export class Classification {
#PrimaryGeneratedColumn()
id: number;
#CreateDateColumn()
createTime: Date;
#UpdateDateColumn()
updateTime: Date;
#Column()
name: string;
#ManyToMany(type => Article)
#JoinTable()
articles: Article[];
}
I can save the Article and Classification successful. But I'm not sure how to save the relation of them.
I have tried to save the relation via below code:
async create(dto: ArticleClassificationDto): Promise<any> {
const article = this.repository.save(dto);
article.then(value => {
console.log(value);//console the object article
value.classification.forEach(item => {
const classification = new Classification();
classification.id = item.id;
classification.articles = [];
classification.articles.push(value);
this.classificationService.save(classification);
})
});
console.log(article);
return null;
}
And the post data strcture like that
{
"name":"artile name",
"content":"article content",
"classification":[{
"id":4
},{
"id":3
}]
}
At the beginning, it works.
But when I post the data again, the old record was replaced rather create another record.
What should I do next?
Just look below code please.
async create(dto: ArticleClassificationDto): Promise<any> {
this.repository.save(dto).then(article => {
article.classification.forEach(item => {
this.ClassificationRepository.findOne(
{
// the privous method is get all the articles from databse and push into this array
// relations: ['articles'],
where: { id: item }// now I change the data strcture, just contains id instead of {id}
}
).then(classification => {
// console.log(article);
console.log(classification);
// cmd will show ' UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'push' of undefined' withous below line code. But if I init the array manually,the old record will be replaced again.
// classification.articles = [];
classification.articles.push(article);
this.ClassificationRepository.save(classification);
});
})
})
return null;
}
How to save relations?
Let's assume you have an array of articles and you want to create a relation to a classification entity. You just assign the array to the property articles and save the entity; typeorm will automatically create the relation.
classification.articles = [article1, article2];
await this.classificationRepository.save(classification);
For this to work, the article entities have to be saved already. If you want typeorm to automatically save the article entities, you can set cascade to true.
#ManyToMany(type => Article, article => article.classifications, { cascade: true })
Your example
async create(dto: ArticleClassificationDto): Promise<any> {
let article = await this.repository.create(dto);
article = await this.repository.save(article);
const classifications = await this.classificationRepository.findByIds(article.classification, {relations: ['articles']});
for (const classification of classifications) {
classification.articles.push(article);
}
return this.classificationRepository.save(classifications);
}
in my case i have user and role, 1st you have to initialize your manytomany in your entities :
in user entity :
#ManyToMany((type) => Role, {
cascade: true,
})
#JoinTable({
name: "users_roles",
joinColumn: { name: "userId", referencedColumnName: "id" },
inverseJoinColumn: { name: "roleId" }
})
roles: Role[];
in role entity :
//Many-to-many relation with user
#ManyToMany((type) => User, (user) => user.roles)
users: User[];
in my service i create a new entity from my data then i added role data to my new entity object :
let entity = await this.userRepository.create(data);
let entity2 = {
...entity,
roles: data.selectedRoles,
};
const user = await this.userRepository.save(entity2);
this is the exemple in typeorm website :
const category1 = new Category();
category1.name = "animals";
await connection.manager.save(category1);
const category2 = new Category();
category2.name = "zoo";
await connection.manager.save(category2);
const question = new Question();
question.title = "dogs";
question.text = "who let the dogs out?";
question.categories = [category1, category2];
await connection.manager.save(question);

Categories