Return ONLY selected fields within a TypeORM find request - typeorm

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
}
}

Related

TypeORM QueryBuilder add parameter that is in string

I want to add query parameter to my queryBuilder but it returns error invalid input syntax for type json. What is the correct way to send this parameter?
My query:
const foundedUserContacts = await this.userBookContactRepository
.createQueryBuilder('userBookContacts')
.select(['userBookContacts.contacts'])
.andWhere(`contacts #> '[{ "phoneNumber": :phoneNumber }]'`, {
phoneNumber: user.phoneNumber,
})
.getMany();
This works ok:
const foundedUserContacts = await this.userBookContactRepository
.createQueryBuilder('userBookContacts')
.select(['userBookContacts.contacts'])
.andWhere(`contacts #> '[{ "phoneNumber": "${user.phoneNumber}" }]'`)
.getMany();
My schema is:
#Entity({ name: 'userBookContacts' })
#Unique('userUuid_userPhone', ['userUuid', 'phoneNumber'])
export class UserBookContact {
#PrimaryGeneratedColumn()
id: number;
#Column()
userUuid: string;
#Index()
#Column()
phoneNumber: string;
#Column({
type: 'jsonb',
nullable: true,
default: '[]',
})
contacts: UserContact[];
}

TypeORM relation through third table won't work

Following the TypeORM tutorial (this part - https://typeorm.io/#/many-to-many-relations/many-to-many-relations-with-custom-properties) i created the models in my app:
#Entity({ name: 'organizations'})
export class OrganizationEntity extends AbstractEntity {
#Column()
name: string;
#Column({ nullable: true })
description: string;
#Column({ nullable: true })
logo: string;
#ManyToOne(() => UserEntity, { nullable: true })
mainContactUser: UserEntity;
#OneToOne(() => LicenseEntity, license => license.organization)
license: LicenseEntity;
#OneToMany(() => UserRoleEntity, userRole => userRole.user)
users!: UserRoleEntity[]
}
#Entity({ name: 'users' })
export class UserEntity extends AbstractEntityWithDates {
#Column({ nullable: false })
firstName: string;
#Column({ nullable: false})
lastName: string;
#Column({ nullable: true })
jobTitle: string;
#Column({ unique: true })
email: string;
#Column({ nullable: true })
phone: string;
#Column({ nullable: true })
avatar: string;
#Column({ nullable: true })
oAuthIdentityId: string;
#OneToMany(() => UserRoleEntity, userRole => userRole.user)
userRoles: UserRoleEntity[]
}
and finally:
#Entity({ name: 'user_roles' })
export class UserRoleEntity extends AbstractEntity {
#ManyToOne(() => UserEntity, user => user.userRoles)
user: UserEntity;
#ManyToOne(() => OrganizationEntity, organization => organization.users, { nullable: true })
organization: OrganizationEntity;
#ManyToOne(() => ProjectEntity, { nullable: true })
project: ProjectEntity;
#Column({
type: "enum",
enum: RoleType,
nullable: true
})
role: string;
}
Then in a service i'm performing a query where i want to fetch all organizations with at least related user roles:
const found = await this.organizationRepository
.createQueryBuilder("organization")
.leftJoinAndSelect("organization.users", "users")
.getMany();
No errors but the query generated by TypeORM is as follows:
SELECT "organization"."id" AS "organization_id", "organization"."name" AS "organization_name", "organization"."description" AS "organization_description", "organization"."logo" AS "organization_logo", "organization"."main_contact_user_id" AS "organization_main_contact_user_id", "users"."id" AS "users_id", "users"."role" AS "users_role", "users"."user_id" AS "users_user_id", "users"."organization_id" AS "users_organization_id", "users"."project_id" AS "users_project_id"
FROM "organizations" "organization"
LEFT JOIN "user_roles" "users" ON "users"."user_id"="organization"."id"
This part:
...ON "users"."user_id"="organization"."id"
is obviously incorrect but i can't see why?
Any help would be appreciated.
Your OrganizationEntity has an error in the OneToMany config; You set userRole.user, but it should be userRole.organization, which is the field in that table that links to this field.
#OneToMany(() => UserRoleEntity, userRole => userRole.organization)
users!: UserRoleEntity[]
Also, you should add primary keys to your tables. A safe approach by default is to use a Generated column. You can add it to your 3 tables. For the many-to-many table you could create a compound key, or simply an index with user_id, and company_id, but with tradeoffs - so just use an id on every table to get started.
#PrimaryGeneratedColumn()
id: string;

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 :)"

TypeORM: Insert an entity which has many to many relation with another entity (based on existing records)

I have 2 following entities with Many To Many relationship
User entity (relationship owned by the User entity)
import { Entity, Column, PrimaryGeneratedColumn, UpdateDateColumn, ManyToMany, JoinTable, CreateDateColumn } from 'typeorm';
import { Role } from './role.schema';
#Entity('Users')
export class User {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({
name: 'Email',
length: 100,
unique: true
})
email: string;
#Column({
name: 'FirstName',
length: 30
})
firstName: string;
#Column({
name: 'LastName',
length: 30
})
lastName: string;
#ManyToMany(type => Role, { eager: true })
#JoinTable({
name: 'UserRoles',
joinColumns: [
{ name: 'UserId' }
],
inverseJoinColumns: [
{ name: 'RoleId' }
]
})
roles: Role[];
}
Role entity (with two existing roles: Admin and User)
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany } from 'typeorm';
import { User } from './user.schema';
#Entity("Roles")
export class Role {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({
name: 'Name',
length: 50,
unique: true
})
name: string;
#ManyToMany(type => User, user => user.roles)
users: User[];
}
the design of application is such that Role name is sent in request in the form of an array e.g. ['Admin', 'User']
Now while inserting a User,
currently I first retrieve Role object from database based on the role name array received in request (to assign a desired role),
then assign it to the User object (roles property) and
then finally call save method on User object to insert the record in User table.
Snippet:
createConnection(connectionConfig as ConnectionOptions).then(async connect => {
let userRepo = connect.getRepository(User);
let roleRepo = connect.getRepository(Role);
let roles = ['Admin', 'User'];
let user = userRepo.create();
return roleRepo.createQueryBuilder('role').where('role.name IN (:roleNames)', { roleNames: roles }).getMany().then((roles) => {
user.email = 'test1#test.test';
user.firstName = 'TestFName';
user.lastName = 'TestLName';
user.roles = roles;
return userRepo.save(user)
})
}).catch(error => {
console.log(error);
});
This results in many database calls. It would be great if some one can enlighten me with smarter and more elegant way ( using fluent QueryBuilder to achieve above result)

embedForm saving problem - Symfony

I have schema like:
Schema:
article:
id: ~
title: { type: VARCHAR, size: '255', required: true }
created_at: { type: TIMESTAMP, required: true }
updated_at: { type: TIMESTAMP, required: true }
article_data:
id: ~
article_data: { type: BLOB, required: true }
article_filename: { type: VARCHAR, size: '255'}
article_id: { type: INTEGER, required: true, foreignTable: article, foreignReference: id, onDelete: cascade }
So, in my article admin module, I'd like to display the article_data widget, which is a file upload.
Everything is fine. But when saving the uploaded file to the server, the article_id field is null.
Is there a way i could get the id of the article and save it as the article_id of the article_data table?
Thanks
EDIT:
I think I need to override the saveEmbeddedForm() method, but I am not sure what I'd need to do.
Could someone help with some code for a saveEmbeddedForm()?
Thanks
I don't known Propel, but in Doctrine you could do something like this:
class ArticleForm extends BaseForm
{
public function configure()
{
parent::configure();
$form = new sfForm();
$datas = $this->getObject()->getDatas();
foreach ($datas as $index => $data)
$form->embedForm($index, new ArticleDataForm($data));
$this->embedForm('dataForms', $form);
}
public function saveEmbeddedForm($con = null, $forms = null)
{
$dataForms = $this->getEmbeddedForm('dataForms')->getEmbeddedForms();
foreach ($dataForms as $dataForm)
$dataForm->getObject()->setArticle($this->getObject());
parent::saveEmbeddedForm($con, $forms);
}
}

Resources