Find records where its #OneToMany relation is not empty - nestjs

I'd like to select all Users where its photos property is not empty. In other words, "select users where photos is not empty".
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[]
}
Is this possible in typeorm, or is SQL the only way to go? Thank you.

You can use QueryBuilder and leftJoinAndSelect. The query would be as simple as:
const users = await userRepository.createQueryBuilder('user')
.leftJoinAndSelect('user.photos', 'photos')
.where('photos.id IS NOT NULL')
.getRawMany();
Edit: If you want the output to contain an array of photos for each user, just replace getRawMany with getMany.
const users = await userRepository.createQueryBuilder('user')
.leftJoinAndSelect('user.photos', 'photos')
.where('photos.id IS NOT NULL')
.getMany();

Related

Cannot query across one-to-many for property in NestJS Postgresql

That's the updateEntity.ts
import { IsNotEmpty } from 'class-validator'
import { BaseEntity, Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { Company } from './company.entity'
#Entity('countries')
export class Country extends BaseEntity {
#PrimaryGeneratedColumn('uuid')
id: string
#IsNotEmpty()
#Column({ unique: true })
name: string
#ManyToOne(() => Company, (company) => company.locations, { nullable: true })
#JoinColumn({ name: 'company_id' })
countryId: Company[]
}
CompanyEntity.ts with the location field
#OneToMany(() => Country, (country) => country.countryId, { eager: true })
locations: Array<Country>
and here is the function where I want to update the properties
async update(id: number, updateCompanyDto: UpdateCompanyDto) {
const newLocations = updateCompanyDto.locations.map((location) => Country.create(location))
updateCompanyDto.locations = newLocations
const status = await Company.update(id, updateCompanyDto)
if (status.affected <= 0) {
throw new HttpException('This company does not exist', HttpStatus.NOT_FOUND)
}
return status
}
First time working with OneToMany and ManyToMany, and if I have the request body like this
"locations": [{"name":"Paris"}]
I'm getting an error "Cannot query across one-to-many for property locations"
I just want to be able to update the companies
It's okay because if you need to update,create or delete data from Location entity,you need to query using that entity not from joined entity

How to implement many-to-many query in typeorm

I'm working with typeORM with nestJS.
I have bi-directional many-to-many relationship between 2 tables: A and B (means a entity of A can be assign to many entities of B, and vice versa)
A.entity.ts:
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany, JoinTable } from 'typeorm';
import { B } from './B.entity';
#Entity('A')
export class A {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany(() => B,
(b)=>(b.AObjects))
BObjects: B[];
}
B.entity.ts:
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany, JoinTable } from 'typeorm';
import { A} from './A.entity';
#Entity('B')
export class B{
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany(() => A,
(a) => a.BObjects,
{eager:true})
#JoinTable({
name: 'AB',
inverseJoinColumn : { name: 'Aid', referencedColumnName: 'id'},
joinColumn: { name: 'Bid', referencedColumnName: 'id'},
})
AObjects: A[];
}
In the module service I want to implement a function, which receives a given id of B entity, and retreive all A's objects which refers to B's id
I want to write a typeORM query which implements the following sql query, for a given_B_id (which will be supplied as a parameter):
SELECT A.*
from A, AB
where AB.Bid = given_B_id and A.id = AB.Aid
Will appreciate your help
I finally find a workaround solution.
But still will appreciate your feedback and advices about the best way to implement many-to-many request with constraint with TypeORM.
My workaround based on queryRunner of DataSource
in the service constructor: add private member :
#Injectable()
export class AService {
constructor(
#InjectRepository(A)
private workerRepository: Repository<A>,
private dataSource: DataSource
){}
// .....
}
and in the "#GET" route handler using
async getByB(given_B_id: number): Promise<A[]> {
let AObjects :Promise<A[]>
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
try {
AObjects = await queryRunner.query(
` SELECT A.*
FROM A, AB
WHERE AB.Bid = ${given_B_id}
AND A.id = AB.Aid`
)
} catch (ex) {
throw ex;
} finally {
await queryRunner.release();
}
return(AObjects)
};
You can use find method as below
const res = this.BRepository.find({
where: { id: given_B_id },
relations: ['A']
});
Or use queryBuilder:
const res = this.BRepository
.createQueryBuilder('b')
.where('id = :b_id', { b_id: given_B_id })
.leftJoinAndSelect('B.A', 'A')
.getMany();
For more information check out the official TypeORM Docs.

How to exclude a column from typeorm entity and could be optional to get the column using Find method

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
password: string;
}
i dont want password here because i want to return to client:
const user = await User.find({where:{name:"test"}})
when i want to modify password i need password:
const user = await User.findOne({where:{name:"test"}})
user.password="password";
await user.save()
is there any solution with Find,FindAndCount or even FindOne methods?
How should i do?
You could add a select option in your #Column decorator inside the entity like this:
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column({select: false})
password: string;
}
This way you will not get the password field from the model if you do find. You'll have to explicitly do addSelect using QueryBuilder.
Reference: https://typeorm.io/#/select-query-builder/hidden-columns
If you don't want to apply select: false to column in entity, then other option is to selectively type columns in find method only which you need
this.ManagementUnitRepository.find({
select: ["id", "name"]
});

Nestjs how to properly map 2 tables with a middle table

I'm using Postgres as database have the next tables:
Students
id: Integer (PK)
name: Text
Subject
id: Integer (PK)
name: Text
student_assignation
student_id: Integer (PK)
subject_id: Integer (PK)
Those tables haven't an Auto-generated PK.
So, my entities are:
Student.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToMany, PrimaryColumn } from 'typeorm';
import { student_assignation } from './student_assignation.entity';
import { Subject } from './subject.entity';
#Entity()
export class Student {
#Column('number')
#PrimaryColumn()
id: number;
#Column('text')
name: string;
#OneToMany(type => student_assignation, x => x.subject_id)
//student_assignations: student_assignation[];
}
Well, here is my question:
I'm trying to get all the subjects assigned to a user.
In SQL terms, I would define it like:
SELECT
u.id, u.name, r.id, r.name
FROM
student u INNER JOIN student_assignation ra
ON u.id = ra.student_id
INNER JOIN subject r
ON r.id = ra.subject_id
WHERE
u.id = 1
But at the moment to convert and use it in nestjs, I have this relation:
#OneToMany(type => student_assignation, x => x.subject_id)
#ManyToOne(type => subject, x => x.id)
But, is not retrieving any information.
You need many-to-many relation. Take a look on TypeOrm implementation here
You have to implement your entities in many-to-many relation:
#Entity()
export class Student {
#Column('number')
#PrimaryColumn()
id: number;
#Column('text')
name: string;
#ManyToMany(type => Subject)
#JoinTable({ name: 'student_assignation' })
subjects: Subject[];
}
#Entity()
export class Subject {
#PrimaryColumn()
id: number;
#Column()
name: string;
#ManyToMany(type => Student)
students: Student[];
}
To retrieve a user with all subjects:
const user = await User.findOne(USER_ID, { relations: ['subjects'] })
console.log(user.subjects);

Typescript Sequelize, M:M relation, query only returns single entry

I'm using NestJs framework with Sequelize Typescript for Node where I'm trying to create a many to many relation between a user and a webpage where many users can have the same site in their favorites.
Now my problem is that when I make the query it limits the result to a single entry, while querying the database directly with the exact same query returns all expected entries.
This is my NestJs favorites entity where I define the favorite table:
// favorite.entity.ts
import { Table, Column, Model, PrimaryKey, ForeignKey, BelongsTo, NotNull } from "sequelize-typescript";
import { IDefineOptions } from "sequelize-typescript/lib/interfaces/IDefineOptions";
import { UserEntity } from "../users/user.entity";
import { SiteEntity } from "../sites/site.entity";
const tableOptions: IDefineOptions = {
timestamp: true,
tableName: "favorites",
schema: process.env.DB_SCHEMA,
} as IDefineOptions;
#Table(tableOptions)
export class FavoriteEntity extends Model<FavoriteEntity> {
#BelongsTo(() => UserEntity)
user: UserEntity;
#ForeignKey(() => UserEntity)
#PrimaryKey
#NotNull
#Column
userId: number;
#BelongsTo(() => SiteEntity)
site: SiteEntity;
#ForeignKey(() => SiteEntity)
#PrimaryKey
#NotNull
#Column
siteId: number;
}
And my service where I make the Sequelize query:
// favorite.service.ts
import { Inject, Injectable } from "#nestjs/common";
import { Model } from "sequelize-typescript";
import { IFavoriteService } from "./interfaces";
import { FavoriteEntity } from "./favorite.entity";
#Injectable()
export class FavoriteService implements IFavoriteService {
constructor(
#Inject("FavoriteRepository") private readonly favoriteRepository: typeof Model,
#Inject("SequelizeInstance") private readonly sequelizeInstance,
) {}
public async findByUserId(userId: number): Promise<FavoriteEntity | null> {
return await FavoriteEntity.scope().find<FavoriteEntity>({
logging: console.log,
where: { userId },
});
}
}
The logged out SQL statement is:
Executing (default): SELECT "userId", "siteId" FROM "public"."favorites" AS "FavoriteEntity" WHERE "FavoriteEntity"."userId" = '1';
And results in a single entry as result (there are many rows in the db)...
{
"userId": 1,
"siteId": 1650
}
Have I made some mistake in my entity or might it be in my sequelize query?
Thanks!
what is your result if you use findAll instead of find.but i am agree the query looks nice
mottosson are you sure the same sql query returns many rows? Because it will be odd.

Resources