Get years from date columns in two different entities with Typeorm - typeorm

Please, tell me how to get an array of unique values of years from two different entities with columns fromDate and toDate?
#Entity()
export class FirstEntity {
#PrimaryGeneratedColumn()
id!: number;
#Column("date")
fromDate!: Date;
#Column("date")
toDate!: Date;
}
#Entity()
export class SecondEntity {
#PrimaryGeneratedColumn()
id!: number;
#Column("date")
fromDate!: Date;
#Column("date")
toDate!: Date;
I can do for one column:
public async findAllYears(em: EntityManager) {
return em
.createQueryBuilder(FirstEntity, "firstEntity")
.select("extract(YEAR FROM firstEntity.fromDate)", "year")
.distinct()
.getRawMany();
}

Related

TypeORM - Search and populate Many-to-Many relation

I have two entities Tag and Photo:
// tag.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class Tag {
#PrimaryGeneratedColumn()
id!: number;
// Other columns
}
// photo.entity.ts
import { Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, } from 'typeorm';
import { Tag } from './tag.entity';
#Entity()
export class Photo {
#PrimaryGeneratedColumn()
id!: number;
#ManyToMany(type => Tag, { eager: true })
#JoinTable()
tags!: Tag[];
// Other columns
}
I need to filter results from photos table using results from tags table.
const photos = await photosRepository
.createQueryBuilder('photo')
.leftJoinAndSelect('photo.tags', 'tag')
.where('tag.name LIKE :searchQuery', { searchQuery: 'nature%' })
.skip(0)
.take(10)
.getMany();
The query above works fine: photos table records are filtered using tag.name column from tags table.
The issue is that an each photo entity in returned photos array contains only filtered (tag.name LIKE :searchQuery) tags relation entities. What I need is to eager load all tags relation entities for each photo. Is it possible somehow?
For example, with Laravel's Eloquent it's possible achive what I need with:
$photos = Photo::query()
->with('tags')
->whereHas('tags', function (Builder $query) {
$query->where('tags.name', 'like', 'nature%');
})
->skip(0)
->take(10)
->get();
maybe i found the answer
const photos = await photosRepository
.createQueryBuilder('photo')
.leftJoin('photo.tags', 'tag')
.leftJoinAndSelect('photo.tags', 'tagSelect')
.where('tag.name LIKE :searchQuery', { searchQuery: 'nature%' })
.skip(0)
.take(10)
.getMany();
the relative reference: https://github.com/typeorm/typeorm/issues/3731

Update parent entity when updating children on cascade

I am looking for a way to update parents entities when updating children entity using cascade on a OneToMany relationship.
Entities
#Entity()
export class Activity {
#PrimaryGeneratedColumn()
id: number;
#CreateDateColumn()
createAt: Date;
#UpdateDateColumn()
updatedAt: Date;
#OneToMany(
type => ActivityTranslation,
activity_translation => activity_translation.activity,
{
cascade: true
}
)
activity_translations: ActivityTranslation[]
}
#Entity()
export class ActivityTranslation {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#CreateDateColumn()
createAt: Date;
#UpdateDateColumn()
updatedAt: Date;
#ManyToOne(
type => Activity,
activity => activity.activity_translations,
{
onUpdate: 'CASCADE'
}
)
activity: Activity
}
Repository
public async updateActivityTranslation(activity_translation: ActivityTranslationModel): Promise<ActivityTranslationModel> {
try {
delete activity_translation.updatedAt;
return this.repository.save(activity_translation);
} catch {
throw new ServerError("Unable to update activity translation")
}
}
Controller
await this.activityTranslationRepository.updateActivityTranslation(activity_translations[i]);
The activity_translation is updated as expected.
I would like to update the updatedAt column of activity entity when saving activity_translation entity.
I tried everything I found about cascade issues on GitHub and StackOverflow.
Thank you for your help.
I know it is not a solution.... but i was able to do it by forcing the parent entity to change by updating one field. For example, create a field being the number of elements related and update it in the updating method.

How to query a entity based on an related entity property in TypeORM?

I have an Entity Transaction and an Entity Integration
#Entity()
export default class Transaction {
#PrimaryGeneratedColumn()
public id_trans?: number;
#OneToOne(type => Integration, i => i.transaction, { nullable: true })
public integration?: Integration;
}
and
#Entity()
export default class Integration {
#PrimaryGeneratedColumn()
public id_cust?: number;
#OneToOne(type => Transaction, t => t.integration)
#JoinColumn({ referencedColumnName: 'id_trans', name: 'int_id_module' })
public transaction?: Transaction;
}
I tried to query the Transaction by using a Integration property as filter.
const id_api = 10;
const transaction = await repository.find({
where: { integration: { int_id_api: id_api} },
relations: ['integration', 'customer'],
});
but it returns the entire table of Transactions, even if the integration.int_id_api is different from id_api property
What am I doing wrong. What should I do to get this query working ?
You need to add a primary key to all your entities. From the doc:
Each entity must have at least one primary key column. This is a
requirement and you can't avoid it. To make a column a primary key,
you need to use #PrimaryColumn decorator.
Assuming you update the Integration entity like so
#Entity()
export default class Integration {
#PrimaryColumn()
public id_inte: number;
#OneToOne(type => Transaction, t => t.integration)
#JoinColumn({ referencedColumnName: 'id_trans', name: 'int_id_module' })
public transaction?: Transaction;
}
You should be able to find transactions like so
const transaction = await repository.find({
where: { integration: { id_inte: some_id } },
relations: ['integration', 'customer'],
});

Use ObjectId from MongoDB with mysql

Is their any possibility to use mongoDB ObjectID system with MySql and typeORM instead of using an incremental ID?
The goal would be to define my entity like this:
#Entity()
export class RolePermission implements IRolePermission {
#ObjectIdColumn() id?: ObjectID;
#Column()
#IsNotEmpty()
roleId: ObjectID;
#Column()
#IsNotEmpty()
permissionId: ObjectID;
}
My entities could therefore have an ID without even being persisted. The ObjectId system would prevent collisions on the unique constraint I'd like to use for this column.
If a system like that can be implemented, is their any performance downside? I remember implementing such a system with PHP, and at the time, I had read this response that made me think that it was ok: Is there a REAL performance difference between INT and VARCHAR primary keys?
It's in fact really simple. You just need to use the ObjectID object from the mongodb package and declare your entities like you would do usually.
First, install mongodb dependencies:
yarn add mongodb
yarn add #types/mongodb
Then, declare your entity. Here an example with a working relationship between a user and an article:
user.entity.ts:
import { Entity, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { Article } from './article.entity';
import { ObjectID } from 'mongodb';
#Entity()
export class User {
constructor() {
this.id = (new ObjectID()).toString();
}
#PrimaryColumn()
id: string;
#Column({ length: 500 })
username: string = null;
#OneToMany(type => Article, article => article.user)
articles: Article[];
}
article.entity.ts:
import { Entity, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { User } from './user.entity';
import { ObjectID } from 'mongodb';
#Entity()
export class Article {
constructor() {
this.id = (new ObjectID()).toString();
}
#PrimaryColumn()
id: string;
#Column({ length: 500 })
title: string = null;
#ManyToOne(type => User, user => user.articles, {nullable: true})
user: User;
}
And use it as you would normally do:
const user = new User();
user.username = 'email#adress.com';
const article = new Article();
article.title = 'Mon titre';
article.user = user;
await this.userRepository.save(user);
await this.articleRepository.save(article);

Is it possible to 'protect' a property and exclude it from select statements

I'd like to protect certain properties on the data-layer level. For example I'd like to protect the password hash I store in the database for a user, so that it doesn't show up in arbitrary select-statements.
This way only when it's explicitly requested in a select property, property2 statement.
I think a more accurate answer would be to set select: false on column options:
#Column({ select: false })
password: string;
And explicitly select the column like this:
const user = await getRepository(User)
.createQueryBuilder()
.addSelect('password')
.getOne()
TypeORM goes well with routing-controllers so you should use it, behind the scenes it uses class-transformer to serialize and deserialize your data. So you can use the #Exclude decorator from that library to prevent certain properties being sent down to the clients.
It also uses the class-validator library to validate the data when specifying it as the type in the controller functions. These are powerful toys. Here is a small example of how you can leverage both:
import { Entity, Column, PrimaryGeneratedColumn, Index, OneToMany } from "typeorm";
import { Exclude, Expose } from "class-transformer";
import { IsNotEmpty, IsEmail, MinLength, MaxLength, Min, Max, IsNumber, IsString } from "class-validator";
#Entity()
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
#IsNotEmpty()
#IsEmail()
#Index({ unique: true })
email: string;
#Exclude()
#Column()
passwordHash: string;
#Column()
#IsNotEmpty()
#IsString()
firstName: string;
#Column()
#IsNotEmpty()
#IsString()
lastName: string;
#Column({ type: 'integer', default: Gender.NotSpecified })
#IsNumber()
#Min(1)
#Max(3)
gender: Gender;
#Expose()
get admin() {
return this.role == Role.Admin;
}
#Expose()
get stylist() {
return this.role == Role.Stylist;
}
}
If you use an another server-side library you can still take advantage of class-transformer and class-validator. You just need to call the validate function manually in your routes, for example for restify you can write:
import {validate } from "class-validator";
import {plainToClass} from "class-transformer";
// ... more code
server.post('/hello', function create(req, res, next) {
let bodyJSON = parseBodyTheWayYouWant(req.body);
let post = plainToClass(bodyJSON);
validate(post)
return next();
});
You can use delete
Exemple Find All users
async findUsers(){
const users:User[] = await userRepository.find();
return users.map(user => {
delete user.password;
delete user.salt;
return user;
}) ;
}
Exemple Find User By Id
async findUserById(id){
const user:User = await userRepository.findOne(id);
delete user.password;
return user;
}
Here is solution - https://github.com/typeorm/typeorm/issues/535
The simplest solution is to exclude field(s) during query.

Resources