Why won't this simple TypeORM query return my ManyToOne property with PostgreSQL? - postgresql

In my database, I have one species record and two cat records
Cat Table
id size species_id
1 large 5
1 small 5
Species Table
id
5
#Entity('cat')
export class Cat {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ type: 'text' })
size: string;
#ManyToOne(() => Species)
#JoinColumn({ name: 'species_id', referencedColumnName: 'id' })
speciesId: string;
}
#Entity('species')
export class Species {
#PrimaryGeneratedColumn('uuid')
id: string;
}
const entity = await this.catRepository.findOneOrFail(catId);
the entity only returns {id: catId, size: 'large'} but does not return the speciesId even though there's a species id in the cat record in my database.
I am new to TypeORM and I think one of the things ManyToOne does is to establish FK constraints. Why is the query behavior the way it is and how can I get the species id to return without having to use joins?

In your situation I think you are missing 1 extra column in Cat entity. Column in which you will store species_id, or if you wish to make it accurate, then use singular specie_id.
#Entity('cat')
export class Cat {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ type: 'text' })
size: string;
#Column({type: 'uuid' })
specie_id: string;
#ManyToOne(() => Species)
#JoinColumn({ name: 'specie_id', referencedColumnName: 'id' })
species: Species;
}
More info you can find in typeorm documentation: https://typeorm.io/#/many-to-one-one-to-many-relations

Related

Get related data from another typeorm table (Adminjs integration)?

Is it possible in TypeORM, PostgreSQL, AdminJs with One to Many table relationships to immediately receive data from the associated table, and not a link to this data, as in my example.
#Entity()
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column({ unique: true })
userColum: string;
#OneToMany(() => Car, (car) => car.car)
cars: Car[];
}
Car:----------------------------
#Entity()
export class Car extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => User, (user: User) => user.userColum, { eager: true })
car: Car;
#Column()
name: string;
}
ТI get a link here:
#ManyToOne(() => User, (user: User) => user.userColum, { eager: true })
But I would like to pull up the data itself. This seems to require an additional request.
Or will you have to somehow custom make the request yourself?
If yes, how can this be done?
Next I use AdminJS to display data and act on tables
I'm using AdminJS for the front-end (This requirement is not made by me.
Therefore, I do not have routes, this is done by AdminJS under the hood.
I did not find any mention of my situation in the AdminJS documentation.
At the moment it turns out that instead of the desired data is the value from the userColum column, when I display the Cars table in a column called CarId, I get a reference to userColum. The link is displayed as an index of the Users table - 1, 2, 3 and is clickable.
Here, instead of displaying 1, 2, 3, I want to see the text value “userColum 1”, “userColum 2”, “userColum 3”, which are in the Users column userColum.
Accordingly, when I click on the link, I get the necessary data of the userColum column. And I want the data to be immediately displayed inside the Cars table.
If we remove { eager: true }
Then in the database in the Cars table in the CarId column there will be id from the Users table, but nothing is displayed on the front-end AdminJs.
I believe you have a mistake in your Car entity (and a possible bad practice in User entity)
the mistake - it should be
#ManyToOne(() => User, (user: User) => user.cars, { eager: true })
user: User; // not car: Car;
and bad naming
#OneToMany(() => Car, (car) => car.user) // not car.car
cars: Car[];
Also, it would be helpful to know how you are querying this data. According to the docs
you must specify the relation in FindOptions
Like so:
const userRepository = dataSource.getRepository(User)
const users = await userRepository.find({
relations: {
cars: true,
},
})

typeorm relation in embedded column

I'm working with NestJs, Typeorm and Postgresql.
I'm trying to use ManyToOne relation in embedded entity. I need to load foreign key column in node environment, so added one additional column(createdById column below). This makes problem.
Here is my code.
A.ts
#Entity()
export class A {
#PrimaryGeneratedColumn()
id!: number;
#Column(() => Embed, { prefix: false })
embed!: Embed;
#CreateDateColumn({ name: 'created_at' })
createdAt!: Date;
}
Embed.ts
export class Embed {
#Column()
x!: number;
#Column()
y!: number;
#ManyToOne(() => B)
#JoinColumn({ name: 'created_by_id' })
createdBy?: B;
#Column({ name: 'created_by_id' })
createdById!: number;
}
B.ts
#Entity()
export class B {
#PrimaryGeneratedColumn()
id!: number;
#CreateDateColumn({ name: 'created_at' })
createdAt!: Date;
}
When I run the app with option TYPEORM_SYNCHRONIZE=true and TYPEORM_LOGGING=true, I get error messages like query failed: CREATE TABLE "a" ("id" SERIAL NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "created_by_id" integer NOT NULL, "created_by_id" integer NOT NULL, "x" integer NOT NULL, "y" integer NOT NULL, CONSTRAINT "PK_684f21444e543375e4c2e6f27fe" PRIMARY KEY ("id")), Message: column \"created_by_id\" specified more than once.. Typeorm trying to create created_by_id column twice. (I applied custom NamingStrategy so that column of embedded entity's name to be snake_case)
If I place createdBy and createdById column to A directly, then it makes no error. Is it problem of typeorm version? Or any other solutions?
package version:
"dependencies": {
"#nestjs/typeorm": "7.1.0,
"typeorm": "0.2.31",
}
run with docker container,
node image: 16.14.2-alpine3.15,
postgres image: mdillon/postgis:11-alpine
TypeORM docs say you don't need to decorate embed class with #Entity().
See Name class in https://orkhan.gitbook.io/typeorm/docs/embedded-entities

How can I query/update only Junction table in nestjs using TypeOrm?

I have two tables with #ManyToMany relation between them:
#Entity()
export class Category {
#PrimaryGeneratedColumn()
id: number;
#ManyToMany(() => Question, question => question.categories)
questions: Question[];
}
#Entity()
export class Question {
#PrimaryGeneratedColumn()
id: number;
#ManyToMany(() => Category, category => category.questions)
#JoinTable({
name: "question_categories",
joinColumn: {
name: "question",
referencedColumnName: "id"
},
inverseJoinColumn: {
name: "category",
referencedColumnName: "id"
})
categories: Category[];
}
As we know we should have 3 tables for such relation and one them would be junction table question_categories. So my question is - can I somehow query that junction table to update? Or how and what should I do to be able to update junction table rows?
Here is what I did. I added a separate entity for question_categories and set #ManyToOne relation to the tables, also did the opposite in the main tables - categories and questions. But when I did that I'm getting error when trying to create new relation.

TypeORM Query entity with jsonb column by json property

I have an entity which holds a json object called "stats". Stats has multiple properties which Id like to order my findAll method by.
I tried everything but I can not get the query to work using my attempts.
This is how I am trying to find all Collection entities ordered by the stats.one_day_volume property
The error I am getting with my last approach shown below is
"missing FROM-clause entry for table "stats"
const result = await getRepository(Collection)
.createQueryBuilder('collection')
.orderBy('collection.stats.one_day_volume', "DESC")
.take(take)
.skip(skip)
.getMany();
This is the entity class
#Entity()
export class Collection {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column({nullable:true})
external_link: string;
#Column({nullable:true})
description: string;
#Column()
slug: string;
#Column({nullable:true})
image_url: string;
#Column({nullable:true})
banner_image_url: string;
#Column()
dev_seller_fee_basis_points: string;
#Column()
safelist_request_status: string;
#Column({nullable:true})
payout_address: string;
#Column('jsonb')
primary_asset_contracts: AssetContract[];
#Column("simple-json")
traits: object;
#Column("jsonb", {array:true, nullable:true})
payment_tokens: PaymentToken[];
#Column("simple-array", {nullable:true})
editors: string[];
#Column("jsonb")
stats: CollectionStats;
#Column({ type: 'timestamptz' })
created_date: Date;
}
I was also looking for a way to orderBy on jsonb column
But there's an issue when you try to getMany with it.
So my workaround was to have it this way (use getRawMany instead of getMany):
const result = await getRepository(Collection)
.createQueryBuilder('collection')
.orderBy("collection.stats->>'one_day_volume'", "DESC")
.take(take)
.skip(skip)
.getRawMany();
So apparently with getRawMany it's working well.
To get it as an entity you can try create it from repo like so:
const repo = await getRepository(Collection);
const resultAsEntities = repo.create(result);
But looks like there are sometimes this won't work as expected due to relations and other objects so you may try achieve it this way:
const resultIds = result.map(item => item.id);
const resultAsEntities = await getRepository(Collection).findByIds(resultIds);

TypeORM - postgresSQL / saving data in the DB

So, i'm new into this typeORM thing, and actually also new into postgresSQL DB, and there's something i couldn't undertand about typeORM and making relations between tables.
My Question: So, i have two entities, User and Post. When you create a post, we store the user ( creator of the post ) in the DB using #JoinColumn, and when i go to users table, i can see the name of that field (username), but, inside User entity, we have an array of Posts, but, that field doesn't appear in the postgres DB, so, when i create a relation, #ManyToOne and #OneToMany, what data stores in the DB and which don't ? Besides that, when i fetch stuff, i can fetch the array, but, does that array is store in the DB or what ? I'm kinda confused with this, so, now let me show you the code
User entity
import {
Entity as TOEntity,
Column,
Index,
BeforeInsert,
OneToMany
} from "typeorm";
import bcrypt from "bcrypt";
import { IsEmail, Length } from "class-validator";
import { Exclude } from "class-transformer";
import Entity from "./Entity";
import Post from "./Post";
#TOEntity("users")
export default class User extends Entity {
constructor(user: Partial<User>) {
super();
Object.assign(this, user);
}
#Index()
#IsEmail(undefined, { message: "Must be a valid email address" })
#Length(5, 255, { message: "Email is empty" })
#Column({ unique: true })
email: string;
#Index()
#Length(3, 200, { message: "Must be at leat 3 characters long" })
#Column({ unique: true })
username: string;
#Exclude()
#Length(6, 200, { message: "Must be at leat 3 characters long" })
#Column()
password: string;
#OneToMany(() => Post, post => post.user)
posts: Post[];
#BeforeInsert()
async hashedPassword() {
this.password = await bcrypt.hash(this.password, 6);
}
}
Post entity
import {
Entity as TOEntity,
Column,
Index,
BeforeInsert,
ManyToOne,
JoinColumn,
OneToMany
} from "typeorm";
import Entity from "./Entity";
import User from "./User";
import { makeid, slugify } from "../util/helpers";
import Sub from "./Sub";
import Comment from "./Comment";
#TOEntity("posts")
export default class Post extends Entity {
constructor(post: Partial<Post>) {
super();
Object.assign(this, post);
}
#Index()
#Column()
identifier: string; // 7 Character Id
#Column()
title: string;
#Index()
#Column()
slug: string;
#Column({ nullable: true, type: "text" })
body: string;
#Column()
subName: string;
#ManyToOne(() => User, user => user.posts)
#JoinColumn({ name: "username", referencedColumnName: "username" })
user: User;
#ManyToOne(() => Sub, sub => sub.posts)
#JoinColumn({ name: "subName", referencedColumnName: "name" })
sub: Sub;
#OneToMany(() => Comment, comment => comment.post)
comments: Comment[];
#BeforeInsert()
makeIdAndSlug() {
this.identifier = makeid(7);
this.slug = slugify(this.title);
}
}
How the User entity looks as a table in the DB
So, as you can see, there's no field with name posts ( which is weird, because as i already said, if i can fetch that, where is that data if i can't see it in the DB )
Now, let me show you Post entity
What i want to understand: So, we have the relationship between tables, know, i tried to search stuff in order to understand that, but i couldn't find anything, so, if you can help me with this mess, i would really aprecciate that, so, thanks for your time !
Let's take this section and try to understand piece by piece:
#ManyToOne(() => User, user => user.posts)
#JoinColumn({ name: "username", referencedColumnName: "username" })
user: User;
1. #ManyToOne(() => User, user => user.posts):
#ManyToOne: This annotation tells typeORM that Post entity is going to have a many to one relationship. From the postgres DB point of view, this means that posts table is going to have a new column (foreign key) which points to a record in some other table.
() => User: This is called definition of the target relationship. This helps typeORM to understand that the target of the relationship is User entity. For postgres DB, this means the foreign key in posts table is going to reference a row in users database
user => user.posts: This is called the inverse relationship. This tells typeORM that the related property for the relationship in User entity is posts. From the postgres DB point of view, this has no meaning. As long as it has the foreign key reference, it can keep the relationship between the two tables.
2. #JoinColumn({ name: "username", referencedColumnName: "username" }):
#JoinColumn: In this scenario, this annotation helps typeORM to understand the name of the foreign key column in posts table and the name of the referenced column in users table
name: "username": This is the name of the column in posts table which is going to uniquely identify a record in users table
referencedColumnName: "username": This is the name of the column in users table which is going to be referenced by the foreign key username in posts table.
inside User entity, we have an array of Posts, but, that field doesn't appear in the postgres DB
The array of Posts is there for the typeORM to return you an array of linked posts. It is not needed by postgres DB to contain the relationship.
when i create a relation, #ManyToOne and #OneToMany, what data stores in the DB and which don't
Whatever property you decorated using #Column will be there in the table as it is. And for the relationships, only the foreign key will be saved. As an example, when you save a Post entity, it will save only the relevant columns in that entity + username foreign key.
when i fetch stuff, i can fetch the array, but, does that array is store in the DB or what ?
When you query User entity, typeorm uses the annotations to join users table with posts table and return you the posts with the user you searched. But in database, it saves users and posts data in their respective tables and uses username foreign key to keep the relationship between them.
I hope this helps you to understand what happens. Cheers 🍻 !!!