Typeorm bulk insert - typeorm

I have the following Group entity
export class Group {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar', unique: true })
name: string;
}
I am trying to implement a bulk insert where only groups with new names would get inserted, while existing one would just get returned without any operation performed on them.
I managed to get this working as follows:
await this
.createQueryBuilder()
.insert()
.into(Group)
.values(groups)
.orUpdate({
conflict_target: ['name'],
overwrite: ['name'],
})
.execute();
But it can not work.why?

When I set unique key in db.It is work.

Related

Return ONLY selected fields within a TypeORM find request

I'm struggling in returning only selected fields in my TypeORM find request.
Assuming the following request
const data = await AppDataSource.manager.find(User, {
select: {
id: true,
hash: true,
firstname: true,
lastname: false,
},
take: 10, // Just here to shrink dataset
});
The script works pretty well excepted that it return every field of my model, with default value initialized.
[
User {
prefix: 'usr',
hash: 'usr_835b0ad2-XXXXXX',
email: undefined,
accountValidated: false,
role: 'free',
myKeyOne: true,
myKeyTwo: false,
gender: 'unspecified',
lastConnexion: 2023-01-19T10:11:02.733Z,
pendingDeletion: false,
deletionDate: undefined,
firstname: 'Clément',
lastname: undefined,
password: undefined,
facebookId: undefined,
googleId: undefined,
id: 158
},
...
]
Of course, it's not usable as it, because I have extensive relations, and thus the payload would be extremely heavy.
Are you aware of a method / a way to remove all unnecessary fields ?
i.e. I'm expecting
[
User {
id: 124,
hash: 'urs_XXXX',
firstname: 'Clément',
},
...
]
In older versions of typeorm I think you need to select with an array of strings, try:
select: ["id", "hash", "firstname"],
See this older version of the docs: https://github.com/typeorm/typeorm/blob/bc60dd559ba42af083ddea17f01205c78c83c7e0/docs/find-options.md
After hours of researches I've finally found out why it behaved like this.
TypeORM relies on class definitions and typescript so...
if you have typescript default values OR if you have rewrite your constructor, all the "default" properties are injected.
Assuming a User model
❌ You should not do
#Entity({ name: 'users' })
class User {
#Column()
firstname?: string;
#Column({ nullable: true })
lastname?: string;
#Column({ unique: true, nullable: false })
email!: string;
#Column({ name: 'account_validated', nullable: false})
accountValidated?: boolean = false
//Your other fields...
}
✅ You should do
#Entity({ name: 'users' })
class User {
#Column()
firstname?: string;
#Column({ nullable: true })
lastname?: string;
#Column({ unique: true, nullable: false })
email!: string;
// Use default argument of the decorator
#Column({ name: 'account_validated', nullable: false, default: false})
accountValidated?: boolean
//Your other fields...
}
And if you need in some way to init a default, then create a public static method which return the Entity instead of using the constructor.
#Entity({ name: 'users' })
class User {
//...fields
public static init(...params): User {
let _user = new User()
//...populate your object
return _user
}
}

Sequelize JOIN with function in conditoin

I googled a bit, looked into documentation but no sign for functions in join conditions
I wanna do (pls ignore the obvious issue with the db design => name_department instead of id_department.. this is just an example)
SELECT *
FROM emp e
JOIN department d
ON e.name_department = lower(d.name_department)
How do I define such relation in the Sequelize model ???
const { Sequelize } = require('sequelize')
const { db } = require('./../../db/sequelize')
const dbConfig = require('./../../config/db.config')
const Department = require('./department.model')
const Employee = db.define('emp', {
id: {
type: Sequelize.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING(255)
},
name_department: {
type: Sequelize.STRING(255)
}
}, {
schema: dbConfig.backendSchema,
freezeTableName: true,
timestamps: false,
})
Employee.hasOne(Department, {as: 'dpt', foreignKey: 'name_department', sourceKey: '????????'})
module.exports = Employee
How do I implement the function there? I simply tried -> lower(name_department) but it doesn't work ... is there any way to use functions?
It's impossible with Sequelize to indicate something other than a real field from a table/view.
One possible solution is to add a field with calculated function result and join two models using this field (Obviously it would be totally up to you to keep this field up-to-date).

Loopback POST array of entry?

I want to insert 10 entries with one query against 10 queries.
I read that it's possible to do it by sending an array like this :
But I get this error:
Do I need to set something? I don't know what to do at all.
Repo with a sample : https://github.com/mathias22osterhagen22/loopback-array-post-sample
Edit:
people-model.ts:
import {Entity, model, property} from '#loopback/repository';
#model()
export class People extends Entity {
#property({
type: 'number',
id: true,
generated: true,
})
id?: number;
#property({
type: 'string',
required: true,
})
name: string;
constructor(data?: Partial<People>) {
super(data);
}
}
export interface PeopleRelations {
// describe navigational properties here
}
export type PeopleWithRelations = People & PeopleRelations;
The problem with your code was :
"name": "ValidationError", "message": "The People instance is not
valid. Details: 0 is not defined in the model (value: undefined);
1 is not defined in the model (value: undefined); name can't be
blank (value: undefined).",
Here in above as in your #requestBody schema, you are applying to insert a single object property, where as in your body are sending the array of [people] object.
As you can see in your people.model.ts you have declared property name to be required, so system finds for the property "name", which obviously not available in the given array of object as primary node.
As you are passing index array, so its obvious error that you don't have any property named 0 or 1, so it throws error.
The below is the code hat you should apply to get insert the multiple, items of the type.
#post('/peoples', {
responses: {
'200': {
description: 'People model instance',
content: {
'application/json': {
schema: getModelSchemaRef(People)
}
},
},
},
})
async create(
#requestBody({
content: {
'application/json': {
schema: {
type: 'array',
items: getModelSchemaRef(People, {
title: 'NewPeople',
exclude: ['id'],
}),
}
},
},
})
people: [Omit<People, 'id'>]
): Promise<{}> {
people.forEach(item => this.peopleRepository.create(item))
return people;
}
You can also use this below
Promise<People[]> {
return await this.peopleRepository.createAll(people)
}
You can pass the array of your people model by modifying the request body.If you need more help you can leave comment.
I think you have a clear solution now. "Happy Loopbacking :)"

How to allow circular relation with nullable: false?

I want to store who is the "creator" and "updater" of every record as a reference to the "User" table in my database. This applies to every table, including the "User" table itself. This is because it can happen that a record is created either by the user that is registering or by some other already existing user.
On the "User" table I have a DB trigger that fills the "creator" and "updater" fields to the same value as the newly generated "id" by default, hence I would like to use a NOT NULL constraint on my "creator" and "updater" column. Unfortunately, this results in the following error: "Circular relations detected: User -> User. To resolve this issue you need to set nullable: false somewhere in this dependency structure."
Is there a way for me to have a circular relation with a not null constraint using TypeORM?
I was actually able to make it work. I'm going to share my solution in case anyone is looking for an answer.
Before I was trying to auto generate the "created_by" column in the following way:
#ManyToOne(type => User, { onUpdate: 'CASCADE', onDelete: 'CASCADE', nullable: false })
#JoinColumn({ name: 'created_by' })
creator: User
All I had to do to make it work was to remove the nullable: false from there and move it to a new #Column field so that in total I would have:
#Column({ nullable: false })
created_by: number
#ManyToOne(type => User, { onUpdate: 'CASCADE', onDelete: 'CASCADE' })
#JoinColumn({ name: 'created_by' })
creator: User
I had a similar issue. I have a scenario where I have foreign key in the same table.
If I explain my scenario a bit more. I was working on storing conditional statements in an application.
condition#1 a > 4
condition#2 a + b < 0
Now I wanted to give user the ability to put a logical operator in b/w condition#1 and condition#2 . Let say user selected OR operator in b/w.
That would look like
(condition#1 OR condition#2)
Now comes the interesting part where I felt the need to have FK in the same. What if user wants to add another condition with OR operator before or after #1 and #2.
condition#3 OR (condition#1 OR condition#2)
So for storing these conditions I created relationships.
I stored 2 records for this scenario
1st for storing (condition#1 OR condition#2) and 2nd record for storing
condition#3 OR (condition#1 OR condition#2)
Now come to the point how I fixed it.
Previously it was something like :
export class ConditionLogicalOperator {
#PrimaryGeneratedColumn({
type: "int",
name: "ConditionLogicalOperatorID"
})
ConditionLogicalOperatorID: number;
#Column("int", {
nullable: false,
name: "LogicalOperatorID"
})
LogicalOperatorID: number;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators)
#JoinColumn({ name: 'ConditionID1' })
conditionLogicalOperator1: ConditionLogicalOperator | null;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators2)
#JoinColumn({ name: 'ConditionID2' })
conditionLogicalOperator2: ConditionLogicalOperator | null;
....
....
}
The trick was to add an empty object { } to Relationship. Please notice { } at the end of #ManyToOne line in each property. It worked for me.
export class ConditionLogicalOperator {
#PrimaryGeneratedColumn({
type: "int",
name: "ConditionLogicalOperatorID"
})
ConditionLogicalOperatorID: number;
#Column("int", {
nullable: false,
name: "LogicalOperatorID"
})
LogicalOperatorID: number;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators, { })
#JoinColumn({ name: 'ConditionID1' })
conditionLogicalOperator1: ConditionLogicalOperator | null;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators2, { })
#JoinColumn({ name: 'ConditionID2' })
conditionLogicalOperator2: ConditionLogicalOperator | null;
....
....
}

typeorm - how to add extra field to "many to many" table?

for example:
3 tables
user
user_business_lines_business_line
business_line
those created by typeorm with the declaration in User
#ManyToMany(type => BusinessLine)
#JoinTable()
businessLines: BusinessLine[]
then, how to add columns fields like
#CreateDateColumn({ type: 'timestamp' })
createdAt: Date
#UpdateDateColumn({ type: 'timestamp' })
updatedAt: Date
to user_business_lines_business_line
It is not possible to add custom column in the auto created many-to-many bridge table. So create another table and give one-to-many and many-to-one relationship between them.
for example:
Three tables
User -> Table 1
BusinessLine -> Table 2
UserBusinessLine -> Bridge Table between User table and BusinessLine table
UserBusinessLine table will contain the foreign key of both parent tables and also we can add custom columns into it.
In User Table
#OneToMany(() => UserBusinessLine, (userBusinessLine) => userBusinessLine.user)
public userBusinessLines: UserBusinessLine[];
In BusinessLine Table
#OneToMany(() => UserBusinessLine, (userBusinessLine) => userBusinessLine.businessLine)
public userBusinessLines: UserBusinessLine[];
In UserBusinessLine Table
#ManyToOne(() => User, (user) => user.userBusinessLines)
public user: User;
#ManyToOne(() => BusinessLine, (businessLine) => businessLine.userBusinessLines)
public businessLine: BusinessLine;
// Custom Colums
#CreateDateColumn({ type: 'timestamp' })
createdAt: Date;
#UpdateDateColumn({ type: 'timestamp' })
updatedAt: Date;
So now the custom table has the foreign keys of User table and BusinessLine table. Also the CreateddateColumn and UpdatedDateColumn
You can specify custom-join table for ManyToMany relation
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number
#Column()
name: string
#ManyToMany(type => BusinessLine, businessLine => businessLine.users)
#JoinTable({
name: 'user_business_line',
joinColumn: {
name: 'userId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'businessLineId',
referencedColumnName: 'id',
},
})
businessLines: BusinessLine[]
}
#Entity('user_business_line')
export class UserBusinessLine {
#CreateDateColumn({ type: 'timestamp' })
createdAt: Date
#UpdateDateColumn({ type: 'timestamp' })
updatedAt: Date
#Column()
#IsNotEmpty()
#PrimaryColumn()
userId: number;
#Column()
#IsNotEmpty()
#PrimaryColumn()
businessLineId: number;
}
#Entity()
export class BusinessLine {
#PrimaryGeneratedColumn()
id: number
#Column()
name: string
#ManyToMany(type => User, user => user.businessLines)
users: User[]
}
If it is just for a createdAt timestamp for example, you could just add an extra column to your user_business_lines_business_line table using migrations like this (I am using Postgres):
ALTER TABLE "user_business_lines_business_line" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT LOCALTIMESTAMP

Resources