How to select specific columns in typeorm querybuilder - sql

I tried to select specific columns by joining tables in typeorm.
When I see following materials there is sample code.
https://orkhan.gitbook.io/typeorm/docs/select-query-builder#joining-relations
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.getOne();
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm";
import {Photo} from "./Photo";
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToMany(type => Photo, photo => photo.user)
photos: Photo[];
}
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
import {User} from "./User";
#Entity()
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#ManyToOne(type => User, user => user.photos)
user: User;
}
for example my desired result is following.where user.name =="Timber"
{
id: user.id
name: user.name
url: photo.url
}
Are there any good way to achieve this ?
Thanks

const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.select(['user.id', 'user.name', 'photo.url']) // added selection
.where("user.name = :name", { name: "Timber" })
.getOne();
By this query, you'll get:
{
id: 1,
name: 'Timber',
photos: [{ url: 'someurl1' }, ..., { url: 'someurlN' }]
}

When you want to select particular columns you have to use getRawOne like below,
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.select(['user.id', 'user.name', 'photo.url'])
.where("user.name = :name", { name: "Timber" })
.getRawOne();

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

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();

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.

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

Undefined is not an object using react native realm

I want to build an application using react native and implement realm. It is meant to have some playlists and songs, and the songs should be able to be added to the playlists.
Playlist:
export class Playlist {
public id: number;
public name: string;
public color: string;
public songs: Song[];
constructor(id: number, name: string, color: string, songs: Song[]) {
this.id = id;
this.name = name;
this.color = color;
this.songs = songs;
}
static schema: Realm.ObjectSchema = {
name: 'Playlist',
primaryKey: 'id',
properties: {
id: 'int',
name: 'string',
color: 'string',
songs: 'Song[]',
},
};
}
Song:
export class Song {
public id: number;
public title: string;
public artist: string;
constructor(id: number, title: string, artist: string) {
this.id = id;
this.title = title;
this.artist = artist;
}
static schema: Realm.ObjectSchema = {
name: 'Song',
primaryKey: 'id',
properties: {
id: 'int',
title: 'string',
artist: 'string',
},
};
}
Realm:
const initData = () => {
const songs = [
new Song(0, 'Avicii', 'Heaven'),
// some songs
];
const playlists = [
new Playlist(0, 'Favorite Songs', 'purple', []),
// some playlists
];
songs.forEach(song => {
Song.insertSong(song);
});
playlists.forEach(playlist => {
Playlist.insertPlaylist(playlist);
});
};
const databaseOptions = {
path: 'playlists.realm',
schema: [Playlist.schema, Song.schema],
};
let realmInstance: Realm | null;
const getRealm = (): Realm => {
if (realmInstance == null) {
realmInstance = new Realm(databaseOptions);
initData();
}
return realmInstance!;
};
export default getRealm;
I always get the error:
TypeError: undefined is not an object (evaluating '_playlist.Playlist.schema')
And I can't figure out why. If you need more code, just tell me.
I am new to react native and JavaScript and TypeScript. I am used to developing Android apps using Java, so maybe I did some dumb mistakes, I don't know.
You are not using your real instance in initData, you need to use realm.write like in the below example. I think my little piece of code should works update me what u got(its better to use Realm async than sync as you want)
const PersonSchema = {
name: 'Person',
properties: {
// The following property definitions are equivalent
cars: {type: 'list', objectType: 'Car'},
vans: 'Car[]'
}
}
let carList = person.cars;
// Add new cars to the list
realm.write(() => {
carList.push({make: 'Honda', model: 'Accord', miles: 100});
carList.push({make: 'Toyota', model: 'Prius', miles: 200});
});
let secondCar = carList[1].model; // access using an array index
In your case
Realm.open({schema: [Song, PlayList]})
.then(realm => {
// ...use the realm instance here
try {
realm.write(() => {
const songs = [
realm.create('Song',{title: 'Avicii', artist: 'Heaven'}),
];
const playlists = [
realm.create('Playlist',{name: 'Favorite Songs', color: 'purple', songs: []}),
// some playlists
];
playlists.forEach(playlist => {
for (const song of songs){
playlist.songs.push(song);
}
});
});
} catch (e) {
console.log("Error on creation");
}
})
.catch(error => {
// Handle the error here if something went wrong
});