Insert into table via relation id's - postgresql

Below I have the Equipt entity it has three columns createdById, tribeId, userId.
I am trying to save a new row using the id's of each entity, and not the entities themselves:
This doesn't work:
let e = connection.getRepository(Equipt);
const check = await e.save({
userId: 1,
tribeId: 1,
createdById: 1,
})
This works:
let e = connection.getRepository(Equipt);
const check = await e.save({
user: user,
tribe: tribe,
createdBy: adminUser,
})
Entity:
import {ManyToOne, RelationId, JoinColumn, Entity} from "typeorm";
import {User} from './User';
import { Base } from "../base";
import { Tribe } from "./Tribe";
#Entity('Equipts')
export class Equipt extends Base {
#ManyToOne(type => User, { nullable: false })
#JoinColumn()
createdBy: User;
#RelationId((Equipt: Equipt) => Equipt.createdBy)
createdById: number;
#ManyToOne(type => Tribe, { nullable: false })
#JoinColumn()
Tribe: Tribe;
#RelationId((Equipt: Equipt) => Equipt.Tribe)
TribeId: number;
#ManyToOne(type => User, { nullable: false })
#JoinColumn()
user: User;
#RelationId((Equipt: Equipt) => Equipt.user)
userId: number;
}
Is there any way to insert using id's without having the pass the entire entity?

Related

TypeORMError: alias was not found

I'm trying to make API pagination for GET /authors.
I have bidirectional many to many relation between authors and books table.
I found that problem is when using creatingQueryBuilder() in combination with .leftJoinAndSelect() and .skip() I get TypeORMError: ""authors"" alias was not found. Maybe you forgot to join it?. But I'm not sure how to solve it.
My database look like this:
library=# select * from authors;
id | first_name | last_name | birth_date | created_at | updated_at
----+------------+-----------+------------+----------------------------+----------------------------
library=# select * from books;
id | title | isbn | pages | created_at | updated_at
----+------------------+-----------------------+-------+----------------------------+----------------------------
library=# select * from books_authors
books_id | authors_id
----------+------------
(4 rows)
Entities look like this:
import { Exclude } from 'class-transformer';
import { BookEntity } from 'src/book/entities/book.entity';
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeInsert,
ManyToMany,
} from 'typeorm';
#Entity({ name: 'authors' })
export class AuthorEntity {
#Exclude()
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Column({ type: 'date', nullable: true })
birthDate: Date | null;
#Exclude()
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#Exclude()
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#ManyToMany(() => BookEntity, (book) => book.authors)
books: BookEntity[];
#BeforeInsert()
updateTimestamp() {
this.updatedAt = new Date();
}
}
import { Exclude } from 'class-transformer';
import { AuthorEntity } from 'src/author/entities/author.entity';
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeInsert,
ManyToMany,
JoinTable,
} from 'typeorm';
#Entity({ name: 'books' })
export class BookEntity {
#Exclude()
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
#Column()
isbn: string;
#Column()
pages: number;
#Exclude()
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#Exclude()
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#ManyToMany(() => AuthorEntity, (author) => author.books)
#JoinTable({ name: 'books_authors' })
authors: AuthorEntity[];
#BeforeInsert()
updateTimestamp() {
this.updatedAt = new Date();
}
}
Service method looks like this:
async findAll(
pageOptionsDto: PageOptionsDto,
filterAuthorDto: FilterAuthorDto,
): Promise<PageDto<AuthorEntity>> {
const builder = this.dataSource
.getRepository(AuthorEntity)
.createQueryBuilder('authors');
if (filterAuthorDto?.firstName) {
builder.where('"authors"."first_name" LIKE :firstName', {
firstName: `%${filterAuthorDto.firstName}%`,
});
}
if (filterAuthorDto?.lastName) {
builder.andWhere('"authors"."last_name" LIKE :lastName', {
lastName: `%${filterAuthorDto.lastName}%`,
});
}
// This part of code is problematic
builder
.innerJoinAndSelect('authors.books', 'books')
.orderBy('"authors"."created_at"', pageOptionsDto.order)
.skip(pageOptionsDto.skip)
.take(pageOptionsDto.perPage);
const total = await builder.getCount();
const { entities } = await builder.getRawAndEntities();
const pageMetaDto = new PageMetaDto({ total, pageOptionsDto });
return new PageDto(entities, pageMetaDto);
}
Just remove the double quotation inside the string, it's redundant and makes typeorm get confused and couldn't find related defined alias.
...
.orderBy('authors.created_at', pageOptionsDto.order)
...

Data is not written to the database when using #AfterInsert(),#AfterUpdate()

And when using #BeforeInsert(), #BeforeUpdate() are recorded, but I don't need this behavior.
There are Entitys: Driver, Truck, Trip.
Trip has a relationship with the Truck and Driver tables.
Trip has a name_trip field, which should be generated and updated when the associated Driver, Truck data changes.
I'm trying to use Trip
#AfterInsert(), // #AfterUpdate()
Driver:
#Entity()
export class Driver extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
DriverName: string;
}
Truck:
#Entity()
export class Truck extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
AutoData: string;
}
Trip:
#Entity()
export class Trip extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#OneToOne(() => Truck, (truck) => truck.id, { nullable: true, eager: true })
#JoinColumn()
truck: Truck;
#OneToOne(() => Driver, (drive) => drive.DriverName, { nullable: true, eager: true }) // eager: true }
#JoinColumn()
driver: Driver;
#Column({ unique: true, nullable: true })
name_trip: string;
#AfterInsert()
// #BeforeInsert()
#BeforeUpdate()
// #AfterUpdate()
updateNameTrip() {
try {
let driverName = '';
let autoData = '';
console.log('this.driver', this.driver);
if (!this.driver) {
console.log('DriverName = Null');
driverName = Driver not found';
} else {
driverName = this.driver.DriverName;
}
if (!this.truck) {
console.log('AutoData = Null');
autoData = Auto not found';
} else {
autoData = this.truck.AutoData;
}
const name_trip = `${uuidv4()}: ID Trip: ${this.id}, driver:
${driverName}, autoData: ${autoData}`;
this.name_trip = name_trip;
console.log('name_trip', name_trip);
} catch (e) {
console.log('Entity Trip Error:', e);
}
}
}

Find rows using foreign key in TypeORM

I have an OneToMany relation from User to Message.
When I insert register a user with a message, it adds the user to the User table, and the message to the Message table with a userId pointing to the user's id.
This is done automatically using the following setup.
User entity:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
email: string;
#JoinTable()
#OneToMany((type) => Message, (message) => message.user, {
cascade: true,
})
messages: Message[];
}
Message entity:
#Entity()
export class Message {
#PrimaryGeneratedColumn()
id: number;
#Column()
text: string;
#ManyToOne((type) => User, (user) => user.messages, { eager: true })
user: User[];
}
If I want to find all messages from a user via userId with:
const existing_msgs = await this.messageRepository.find({
where: { userId: user.id },
});
It tells me that it cannot find the userId column, which is understandable as I did not specifically include userId to the Message entity.
But how would we query it in this case?
As mentionned in This post, you have to add the decorator #JoinColumn after your manytoone. This will join the column and you will be able to perform the query you want with :
const existing_msgs = await this.messageRepository.find({
where: { user: { id: user.id }},
});
Note that this will work only on primary column like id.
And your message table will be :
#Entity()
export class Message {
#PrimaryGeneratedColumn()
id: number;
#Column()
text: string;
#ManyToOne((type) => User, (user) => user.messages, { eager: true })
#JoinColumn() // <-- Add this
user: User[];
}
I was able to do it with the following querybuilder.
const msg_arr = await this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.messages', 'messages')
.where('user.id = :userId', { userId: user.id })
.andWhere('messages.text LIKE :text', { text: message })
.select('messages.text')
.execute();

Error retrieving data from DB using typeorm and type-graphql

I'm using type-graphql in conjunction with typeorm, apollo-server-express and postgreSQL. I have a User and a Customer entity in a 1:n relationship, meaning one user can have multiple customers.
I can create users and customers just fine, but when attempting to retrieve the user associated to a customer using Apollo Server playground, I get an error message stating "Cannot return null for non-nullable field Customer.user."
When I check the database, the associated user id on the customer table is definitely not null (see attached image).
query {
customers {
customerId
customerName
user {
userId
}
}
}
Does anyone know what I'm doing wrong?
User.ts
import { Field, ID, ObjectType } from "type-graphql";
import { BaseEntity, Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Customer } from "./Customer";
#ObjectType()
#Entity("users")
export class User extends BaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn("uuid")
userId: string;
#Field()
#Column({ unique: true })
email: string;
#Column({ nullable: false })
password: string;
#Field(() => Customer)
#OneToMany(() => Customer, customer => customer.user)
customers: Customer[]
}
Customer.ts
import { Field, ID, ObjectType } from "type-graphql";
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { User } from "./User";
#ObjectType()
#Entity("customers")
export class Customer extends BaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn("uuid")
customerId: string;
#Field()
#Column()
customerName: string;
#Field(() => User)
#ManyToOne(() => User, user => user.customers)
user: User;
}
CustomerResolver.ts
export class CustomerResolver {
#Query(() => [Customer])
async customers():Promise<Customer[]> {
try {
return await Customer.find();
} catch (error) {
console.log(error);
return error;
}
}
....
Setup / Version
Node: v14.17.0
"apollo-server-express": "^2.24.0",
"type-graphql": "^1.1.1",
"typeorm": "0.2.32"
postgreSQL: 13.2
In your resolver change the find operation like below:
return Customer.find({
relations: ["user"]
});
You should write a #FieldResolver which will fetch customers based on root user data.
https://typegraphql.com/docs/resolvers.html#field-resolvers

Pass empty string to column datatype is enum postgres got error

I got an error when I pass an empty string to completed but when I pass empty string to delivery_name is work fine
const queryBuilder = this.deliveryRepository
.createQueryBuilder('delivery')
.select([
'delivery.delivery_id AS delivery_id',
'delivery.price AS price',
'delivery.delivery_date AS invoice_date',
'delivery.invoice_id AS invoice_id',
'delivery.completed AS completed',
'delivery.deliveryman_id AS deliveryman_id',
'deliveryman.delivery_name AS delivery_name',
'deliveryman.vehicle AS vehicle',
'deliveryman.phone AS phone',
]).orWhere('LOWER(deliveryman.delivery_name) LIKE LOWER(:delivery_name)', {
delivery_name: `%${option.filter}%`,
}).orWhere('delivery.completed = :completed', {
completed: `%${option.filter}%`,
})
DeliveryEntity
import { DeliveryManEntity, InvoiceEntity } from '#entity';
import { DeliveryStatus } from '../../../constant/delivery/delivery-status';
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { ShareEntity } from '#share-entity';
#Entity({ name: 'delivery' })
export class DeliveryEntity extends ShareEntity {
#PrimaryGeneratedColumn()
delivery_id: number;
#Column({ nullable: true, default: 0 })
price: number;
#Column({ type: 'date', default: () => 'NOW()' })
delivery_date: Date;
#OneToOne(() => InvoiceEntity, (entity) => entity.delivery)
#JoinColumn({ name: 'invoice_id', referencedColumnName: 'invoice_id' })
invoice: InvoiceEntity;
#Column()
invoice_id: number;
#Column({
enum: DeliveryStatus,
type: 'enum',
default: DeliveryStatus.PENDING,
enumName: 'delivery_status',
name: 'completed',
})
completed: DeliveryStatus;
#ManyToOne(() => DeliveryManEntity, (entity) => entity.delivery)
#JoinColumn({
name: 'deliveryman_id',
referencedColumnName: 'deliveryman_id',
})
deliveryman: DeliveryManEntity;
#Column()
deliveryman_id: number;
}
Delivery Status
export enum DeliveryStatus {
PENDING = 'PENDING',
COMPLETED = 'COMPLETED',
}
I want to pass an empty string to completed or I can pass PENDING OR COMPLETED
any solution how to query with empty string to completed field
I'm using typeorm + Nestjs +Postgresql
Based on your comment, what you should be doing is something like below:
const queryBuilder = this.deliveryRepository
.createQueryBuilder('delivery')
.select([
'delivery.delivery_id AS delivery_id',
'delivery.price AS price',
'delivery.delivery_date AS invoice_date',
'delivery.invoice_id AS invoice_id',
'delivery.completed AS completed',
'delivery.deliveryman_id AS deliveryman_id',
'deliveryman.delivery_name AS delivery_name',
'deliveryman.vehicle AS vehicle',
'deliveryman.phone AS phone',
]).orWhere('LOWER(deliveryman.delivery_name) LIKE LOWER(:delivery_name)', {
delivery_name: `%${option.filter}%`,
});
const filter = `%${option.filter}%`;
if (filter === "") {
queryBuilder.orWhere('delivery.completed IN (...:completed)', {
completed: [DeliveryStatus.COMPLETED, DeliveryStatus.PENDING],
});
} else {
queryBuilder.orWhere('delivery.completed = :completed', {
completed: filter,
});
}