About how to use many to one in TypeORM - orm

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.

Related

createQueryBuilder with getOne doesn't return #JoinColumns inside table Typeorm

I have a project written by nestjs and typeorm. In my project I have tables chat and user so I want to get ManyToOne relationships. Here is the chat table
export class Chat extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => User, (user) => user.chatsCreater, { nullable: false })
#JoinColumn({ name: 'creatorId' })
creator: User;
#ManyToOne(() => User, (user) => user.chatsCompanion, { nullable: false })
#JoinColumn({ name: 'companionId' })
companion: User;
}
and chunk from user table
...
#OneToMany(() => Chat, (chat) => chat.creator)
chatsCreater: Chat[];
#OneToMany(() => Chat, (chat) => chat.companion)
chatsCompanion: Chat[];
...
When I query data from chat I expect to get the hole table {id, companionId, creatorI} not only value of chat.id. Here is my query
.createQueryBuilder('chat')
.where('chat.creatorId = :creatorId AND chat.companionId = :companionId', { creatorId, companionId })
.getOne()
and the result {id: 1}
So what I want is to get values of companionId and creatorId too when I query from chat.
If I change getOne() to getRaw() I get the desired output. But in case of more complex queries (with multiple joins) it becomes a mess with getRaw so is there a way to get all columns using getOne ?
I was able to do it by using leftJoinAndSelect
.createQueryBuilder('chat')
.leftJoinAndSelect('chat.creator', 'creator')
.leftJoinAndSelect('chat.companion', 'companion')
.where('chat.creatorId = :creatorId AND chat.companionId = :companionId', { creatorId, companionId })
.getOne();
In case if hole table is not needed it's also possible to use leftJoin without select and later add to query addSelect(['creator.id','companion.id'])

Populaing a query in Typeorm

Can anyone help me to know how to populate a query in typeorm. Like I have this entity
#Entity('users')
export class User extends BaseEntity {
#Column()
userName : string;
#Column()
email : string;
#OneToMany(() => UserFollowing, (userFollowing) => userFollowing.followee)
followers: User[];
#OneToMany(() => UserFollowing, (userFollowing) => userFollowing.follower)
followees: User[];
}
#Entity('user_followings')
export class UserFollowing extends FakeBaseEntity {
#JoinColumn({ name : 'follower_id' })
#ManyToOne(() => User, user => user.followees)
follower : User;
#JoinColumn({ name : 'followee_id' })
#ManyToOne(() => User, user => user.followers)
followee : User;
}
Now to get all the followers and followees of a particular userid
Here are my two approaches : both giving same output
const info = await this.userRepo
.createQueryBuilder('userFollowing')
.select()
.leftJoinAndSelect('userFollowing.followers','followers')
.leftJoinAndSelect('userFollowing.followees', 'followees')
.where('userFollowing.id = :userid', { userid })
.getMany()
return info;
------------------------------------------------------------
const info = this.userRepo.find({
where: {
id : userid,
},
relations: ["followers", "followees"],
})
return info;
output I am recieving : and I want all the info about followers and followees
{
"id": "e8651d4f-3c7b-4f5a-8205-7370b107d98c",
"userName": "something",
"email" : "something#gmail.com",
"followers": [
{
"id": "f54b8574-10ea-4133-85bd-5f8fcda4eeb9",
"createdAt": "2021-08-12T03:58:39.198Z",
"updatedAt": "2021-08-12T03:58:39.198Z"
}
],
"followees": [
{
"id": "eb2cb728-a1c0-4bea-9230-712827c714c7",
"createdAt": "2021-08-12T03:59:32.260Z",
"updatedAt": "2021-08-12T03:59:32.260Z"
}
]
}
If I have understood your question correctly, then what you are looking for is to get the data of followers and followees as well.
You can easily achieve this with the find function.
This is how your query should look:
const info = this.userRepo.find({
where: {
id : userid,
},
relations: ["followers", "followees", "followers.follower", "followees.followee"],
})
As you can see, I have passed two more string values in the relations.
Using this, it will load the sub-relations as well (stated in the TypeORM Find Options).
You can achieve the same using query builder as well by adding more .leftJoinAndSelect method chain.
External Links:
TypeORM Find Options

How to select specific columns in typeorm querybuilder

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

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

GraphQL & Sequelize: Users and followers/following

I'm attempting to set up my User GraphQL model to have followers and following attributes to query on. However I'm having trouble setting up the relationship in Sequelize. I'm trying to use a Follower model as a Join Table and setup a BelongsToMany association, but haven't been able to get it working. Can anyone suggest what to do or point out what I'm doing wrong?
I've come up with a temporary solution by manually querying, which you can see in my User.model.ts, but I believe there is a better way to do it using proper configuration.
I'm using typescript wrappers around GraphQL and Sequelize, TypeGraphQL and sequelize-typescript respectively, as well as PostgreSQL.
User.model.ts
// VENDOR
import { ObjectType, Field, ID } from 'type-graphql';
import { Model, Table, Column, PrimaryKey, Unique, IsUUID, HasMany, DefaultScope, AllowNull, DataType, BelongsToMany } from 'sequelize-typescript';
// APP
import Post from '../post/post.types';
import Follower from '../follower/follower.types';
/** User model for GraphQL & Database */
#Table({ timestamps: false, tableName: 'users' }) // tell sequelize to treat class as table model
#DefaultScope(() => ({ include: [{ model: Post.scope(), as: 'posts' }] })) // tell sequelize to include posts in its default queries
#ObjectType() // tell GraphQL to treat class as GraphQL model
export default class User extends Model<User>{
#PrimaryKey
#Unique
#AllowNull(false)
#IsUUID(4)
#Column(DataType.UUID)
#Field(() => ID)
id: string;
#Unique
#AllowNull(false)
#Column
#Field()
ci_username: string;
#AllowNull(false)
#Column
#Field()
username: string;
#AllowNull(false)
#Column
#Field()
first_name: string;
#Column
#Field()
last_name: string;
#Column
#Field({ nullable: true })
profile_picture?: string;
// #BelongsToMany(() => User, { otherKey: 'user_id', as: 'followers', through: () => Follower })
// #Field(() => [User])
// followers: User[];
// MY TEMPORARY SOLUTION USING MANUAL QUERYING
#Field(() => [User])
get followers(): Promise<User[]> {
return Follower.findAll({ where: { user_id: this.id } })
.then(records => records.map(record => record.follower_id))
.then((follower_ids: string[]) => {
return User.findAll({ where: { id: follower_ids }});
})
}
// DOES NOT WORK, BUT I BELIEVE COULD POTENTIALLY LEAD TO BETTER SOLUTION
#BelongsToMany(() => User, { otherKey: 'follower_id', as: 'following', through: () => Follower })
#Field(() => [User])
following: User[];
#HasMany(() => Post)
#Field(() => [Post])
posts: Post[];
}
Follower.model.ts
// VENDOR
import { Model, Table, Column, PrimaryKey, Unique, IsUUID, AllowNull, DataType, Index, ForeignKey, AutoIncrement } from 'sequelize-typescript';
// APP
import User from '../user/user.types';
/** Follower model for Database */
#Table({ timestamps: false, tableName: 'followers' }) // tell sequelize to treat class as table model
export default class Follower extends Model<Follower>{
#PrimaryKey
#AutoIncrement
#Unique
#AllowNull(false)
#Column
id: number;
#AllowNull(false)
#IsUUID(4)
#Index
#ForeignKey(() => User)
#Column(DataType.UUID)
user_id: string;
#AllowNull(false)
#IsUUID(4)
#Index
#ForeignKey(() => User)
#Column(DataType.UUID)
follower_id: string;
}
GraphQL Query
{
users: allUsers {
id
username
first_name
last_name
following {
username
id
}
}
}
GraphQL Response / Error
{
"errors": [
{
"message": "Cannot return null for non-nullable field User.following.",
"locations": [
{
"line": 7,
"column": 5
}
],
"path": [
"users",
0,
"following"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Cannot return null for non-nullable field User.following.",
" at completeValue (/Users/jsainz237/Projects/trueview/trueview-api/node_modules/graphql/execution/execute.js:560:13)",
" at /Users/jsainz237/Projects/trueview/trueview-api/node_modules/graphql/execution/execute.js:492:16"
]
}
}
}
],
"data": null
}
Any help is appreciated.
You need to write a #FieldResolver manually that will resolve the relation and return proper data.
Another solution is to rely on ORM capabilities and lazy relations - when the returned base entity contains a promise as a field, so when .then() is called, it automatically fetches the relation for the database.