Find rows using foreign key in TypeORM - postgresql

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

Related

Typeorm (Nestjs) update ManyToMany relation when using querybuilder violates not-null constraint

I have a simple ManyToMany setup with typeorm. According to the docs of typeORM on how to update/add a ManyToMany relation.
Link to the docs: https://typeorm.io/relational-query-builder
The error I get:
The query it produces, and here is the problem, it does not have 2 parameters and 'usersId' is empty. I think the user has to exist first before connecting them together, but maybe someone can confirm this? Why does it not create the user for me when adding a new user in enterprise?
query: SELECT "Enterprise"."id" AS "Enterprise_id", "Enterprise"."createdAt" AS "Enterprise_createdAt", "Enterprise"."updatedAt" AS "Enterprise_updatedAt", "Enterprise"."name" AS "Enterprise_name", "Enterprise"."erpCustomerCode" AS "Enterprise_erpCustomerCode", "Enterprise"."isActive" AS "Enterprise_isActive", "Enterprise"."street" AS "Enterprise_street", "Enterprise"."houseNumber" AS "Enterprise_houseNumber", "Enterprise"."city" AS "Enterprise_city", "Enterprise"."state" AS "Enterprise_state", "Enterprise"."country" AS "Enterprise_country", "Enterprise"."zip" AS "Enterprise_zip", "Enterprise"."geoLocation" AS "Enterprise_geoLocation", "Enterprise"."unitsSystem" AS "Enterprise_unitsSystem", "Enterprise"."weekNumberingSystem" AS "Enterprise_weekNumberingSystem", "Enterprise"."yearStart" AS "Enterprise_yearStart", "Enterprise"."weekStart" AS "Enterprise_weekStart" FROM "enterprises" "Enterprise" WHERE ("Enterprise"."id" = $1) LIMIT 1 -- PARAMETERS: ["d2f16db8-63be-4b09-a5ea-e04336069df4"]
User {}
query: INSERT INTO "enterprise_users"("enterprisesId", "usersId") VALUES ($1, DEFAULT) -- PARAMETERS: ["d2f16db8-63be-4b09-a5ea-e04336069df4"]
query failed: INSERT INTO "enterprise_users"("enterprisesId", "usersId") VALUES ($1, DEFAULT) -- PARAMETERS: ["d2f16db8-63be-4b09-a5ea-e04336069df4"]
I have the following setup:
enterprise.entity.ts
#Entity('enterprises')
export class Enterprise {
#PrimaryGeneratedColumn('uuid')
id: string;
#CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#UpdateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#Column({ unique: true, length: 50 })
name: string;
...
#ManyToMany(() => User, (users) => users.enterprises, { cascade: true })
#JoinTable({ name: 'enterprise_users' })
users: User[];
}
user.entity.ts
#Entity('users')
export class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
#ManyToMany(() => Enterprise, (enterprise) => enterprise.users)
enterprises: Enterprise[];
}
enterprise.service.ts
#Injectable()
export class EnterpriseService {
constructor(
#InjectRepository(Enterprise)
private enterpriseRepository: Repository<Enterprise>,
) {}
async update(id: string, enterprise: UpdateEnterpriseDto) {
// for testing
const user = new User();
user.name = 'test123';
// triggers error
await this.enterpriseRepository.createQueryBuilder().relation(Enterprise, 'users').of(id).add(user);
return this.enterpriseRepository.save({ id, ...enterprise });
}
}

Value of column in one to one relation using TypeORM + Postgres is null

I'm trying to set up a simple one-to-one relation between an item and item_name. The entity looks as follows:
#Entity('item')
export class ItemEntity {
#PrimaryColumn('integer')
id: number;
#OneToOne(() => ItemNameEntity)
#JoinColumn()
name: ItemNameEntity;
// ... other props
}
item-name.entity
#Entity('item_name')
export class ItemNameEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
en: string;
#Column()
fr: string;
// ... other properties
}
I insert an item using the following payload:
{
id: 26,
name: { en: 'English name', fr: 'French name' },
}
It stores the item as expected, and adds a nameId column. The problem is that it does not insert anything into item_name, and thus the nameId column is null.
What am I missing here?
From this.
You have to set cascade: true on name relation in ItemEntity:
#Entity('item')
export class ItemEntity {
#PrimaryColumn('integer')
id: number;
#OneToOne(() => ItemNameEntity, { cascade: true })
#JoinColumn()
name: ItemNameEntity;
// ... other props
}
Setting cascade: true on ItemEntity tells TypeORM that if a new itemName is "linked" on an item and the item is saved, the new itemName should also be saved to the database.
Example:
const manager = getManager();
const item: ItemEntity = manager.create(ItemEntity, {
id: 26,
name: manager.create(ItemNameEntity, {
en: 'English name',
fr: 'French name'
}),
});
await manager.save(ItemEntity, item);

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

How to map joined table column to an entity's field in TypeORM

There are two entities as follow:
// user.entity.ts
#Entity()
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
#RelationId((user: User) => user.group)
groupId: number;
#Column()
fullName: string;
#Column()
email: string;
#Column()
passwordHash: string;
#ManyToOne(type => Group, group => group.users)
#JoinColumn()
group: Group;
isOwner: boolean;
}
// group.entity.ts
#Entity()
export class Group extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column({ default: false })
isOwner: boolean;
#OneToMany(type => User, user => user.group)
users: User[];
}
I'd like to map the isOwner value of Group to isOwner of User
I tried:
async findOneById(id: number): Promise<User> {
return await User.createQueryBuilder('user')
.leftJoinAndMapOne('user.isOwner', 'user.group', 'group')
.select(['user.id', 'user.fullName', 'user.email', 'group.isOwner'])
.getOne();
}
the result was:
It is possible to achieve that by using #AfterLoad() or with JS or with raw query.
BUT
Is it possible to implement that using the orm on the query level?
Something like that could be as a solution:
findOneById(id: number): Promise<User> {
return User.createQueryBuilder('user')
.leftJoinAndMapOne('user.isOwner', 'user.group', 'group')
.select(['user.id', 'user.fullName', 'user.email', 'group.isOwner AS user.isOwner']) // or probably 'group.isOwner AS user_isOwner'
.getOne();
}
And you could look at this answer, hope it would be helpful

Insert into table via relation id's

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?