TypeORM Query entity with jsonb column by json property - postgresql

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

Related

Why won't this simple TypeORM query return my ManyToOne property with 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

How to disable automatic downcasing in NestJS' typeORM query to my postgres DB?

I am using TypeORM to create and manage my DataBase using TypeORM Entities.
I have a very peculiar relation as such
#Entity()
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
login: string;
#Column()
nickname: string;
#Column()
wins: number;
#Column()
looses: number;
#Column()
current_status: string;
#ManyToMany(() => User)
#JoinTable()
friends: User[];
#ManyToMany(() => MatchHistory)
#JoinTable()
match_histories: MatchHistory[];
}
Where User has a ManyToMany relationship with itself. Because of this, typical tools do not work correctly (I haven't found a way to access a User's friends with TypeORM's tools).
So I am doing a good old SQL request as such:
const res = await this.manager.query("SELECT * FROM user WHERE user.id IN (SELECT F.userId_2 AS Friends FROM user_friends_user F WHERE F.userId_1=?);", [user.id]);
This can be translated as "get all the users who's ID is in the friend list of user user.
The problem is that I have noticed that all my requests seem to be downcased. When doing this I face the column f.userid_2 does not exist.
I do not know if it is TypeORM or Postgres which downcases my requests, all I want is it to stop doing so. Thank you very much !
This is something Postgres does by default. If you need to use uppercase values, you need to pass a string literal to Postgres. In your case, that would look like
const res = await this.manager.query(
'SELECT * FROM user WHERE user.id IN (SELECT "F"."userId_2" AS "Friends" FROM user_friends_user "F" WHERE "F"."userId_1"=?);',
[user.id]
);

Many-to-many with custom column

I'm fairly new to Postgres (and Typeorm), and having a really hard time figuring out how to handle the following scenario:
I have a table called blog and a table called category . Blogs can belong to many categories, and categories can have many blogs. So I believe I need a "many to many" relationship here.
My join table needs to have a column called is_primary, so since I need an additional custom column, I believe I need to create my own entity for this, instead of using the auto-generated one. I'm calling this table blogCategory. I'm getting tripped up here on how to handle the save when I need to set the is_primary flag to true.
Here is my schema:
// blog.entity.ts omitting some fields for brevity
#Column()
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
#OneToMany(() => BlogCategory, blogCategory => blogCategory.blog)
blogCategories: BlogCategory[];
// category.entity.ts
#Column()
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToMany(() => BlogCategory, blogCategory => blogCategory.category)
blogCategories: BlogCategory[];
// blog-category.entity.ts
#PrimaryGeneratedColumn()
id: number;
#Column()
is_primary: boolean;
#Column()
categoryId: number;
#Column()
blogId: number;
#ManyToOne( () => Category, category => category.blogCategories )
category: Category
#ManyToOne( () => Blog, blog => blog.blogCategories )
blog: Blog
I think that is all correct, but please correct me if I'm wrong. But the part I'm getting tripped up on now is how I actually handle the blog insert. I'm not sure how I handle adding the is_primary flag when necessary. Do I actually insert the blogCategory item manually? In which case I'd need to save the blog first, so that I'd have access to its id for the blogCategory. I was kind of under the impression that item would be created automatically.
Thanks in advance for anyone who is able to offer some insight!
You can achieve automatic insertion using {cascade: true} in your entity relation. Check this post How to save relation in #ManyToMany in typeORM
Other way you can do it using transaction.
First save Blog, get the blog id then add item into BlogCategory
See https://typeorm.io/#/transactions
For an example see this pseudo code,
await getManager().transaction(async transactionalEntityManager => {
const blog = await transactionalEntityManager.save(blogObject);
blogCategory.blogId = blog.id;
blogCategory.CategoryId = categoryId; // get categoryId from request body
await transactionalEntityManager.save(blogCategory);
});

Cannot set values on uuid or enum fields in Postgres using TypeORM

I've been stuck on this for a good hour, and unfortunately can't find anything relevant in the docs.
I'm unable to set values on fields that use types like enum or uuid. For instance:
export enum PhotoStatus {
WORKING = 'WORKING',
DONE = 'DONE'
}
export class Photo {
#PrimaryGeneratedColumn()
id: number:
#Column()
size: string;
#Column({
type: 'enum',
enum: PhotoStatus,
default: PhotoStatus.WORKING,
})
status: PhotoStatus;
}
But when I try to create a new record, like so:
const photo = new Photo();
photo.size = 'huge';
photo.status = PhotoStatus.DONE;
await connection.manager.save(photo);
It throws an error:
BadRequestException: ERROR: column "status" is of type content_status_enum but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
This happens for uuid columns too.

replace ObjectId field with custom string for ObjectIdColumn in TypeORM/MongoDB

I have a NestJs REST API and use TypeORM with MongoDB. I want to create a entity called project. I started with a basic entity and I just read that I should use ObjectIdColumn instead of PrimaryColumn for MongoDB.
#Entity()
export class Project extends BaseEntity {
// The technical project name
#ObjectIdColumn({ generated: false })
public id: ObjectID;
// The display name
#Column({ unique: true })
public name: string;
// The project successor
#Column({ nullable: true })
public successorId: ObjectID;
// Configuration stuff for that project
#Column()
public configuration: object;
}
I would like to know if it's possible to replace that object id column with a primary column of type string. The id field is based on a special pattern, e.g. the name field
my awesome project
would result into
my-awesome-project
for the id field. Sure I made use of generated: false but I have to pass in a custom string instead of an ObjectID. Currently this is not possible because the docs say the ObjectID
Can be a 24 byte hex string, 12 byte binary string or a Number. http://mongodb.github.io/node-mongodb-native/2.1/api/ObjectID.html
So what needs to get done to use a custom string as an ID field? The only thing I can think of is creating a second field e.g. theRealId and treat it like the ID field and ignore the autogenerated ObjectId...
From what I've learnt, here is what you can do
#Entity()
export class UserEntity
{
#ObjectIdColumn()
_id: string;
#PrimaryColumn()
id: string;
// The display name
#Column({ unique: true })
public name: string;
#Column({ nullable: true })
public successorId: ObjectID;
#Column()
public configuration: object;
}
MongoDB will use _id as an internal id, that you do not expose through your program (and api, then)
You will work with the id, "normally', and it will be your primary key, generating automatically and so on
Source : personal learning, and Udemy course : NestJS Zero to Hero - Modern TypeScript Back-end Development