Sequelize.s include where ancestor column - join

Sorry for my bad English, but I do not know how to better formulate the title.
Anyway I have the following 3 models
const ModelAlpha = sequelize.define('tbl_A', {
id: {
type: Sequelize.INTEGER.UNSIGNED,
field: 'a_id',
autoIncrement: true,
primaryKey: true,
}
});
const ModelBeta = sequelize.define('tbl_B', {
id: {
type: Sequelize.INTEGER.UNSIGNED,
field: 'b_id',
autoIncrement: true,
primaryKey: true,
},
attributeB: {
type: Sequelize.TINYINT.UNSIGNED,
field: 'b_attribute'
}
});
const ModelCharlie = sequelize.define('tbl_C', {
id: {
type: Sequelize.INTEGER.UNSIGNED,
field: 'c_id',
autoIncrement: true,
primaryKey: true,
},
attributeC: {
type: Sequelize.TINYINT.UNSIGNED,
field: 'c_attribute'
}
});
One ModelAlpha has one ModelBeta.
ModelAlpha has a foreign key column referencing the primary key of ModelBeta.
ModelAlpha can have many ModelCharlie
I am to trying to achieve the following
SELECT C.* FROM tbl_C
JOIN tbl_A ON tbl_A.a_id = C.fkey_a_id
JOIN tbl_B ON tbl_B.b_id = tbl_A.fkey_b_id
WHERE C.c_attribute = B.b_attribute
AND tbl_A.a_id = 123
So, in other words, all Charlies of the Alpha whose id is 123
and whose c_attribute is equal to b_attribute of the Beta
So far I have written something like the following,
but I am stuck as to what I am supposed to add to the where
clause.
ModelAlpha.belongsTo(ModelBeta, {foreignKey: 'fkey_b_id', as: 'beta'});
ModelAlpha.hasMany(ModelCharlie,{foreignKey: 'fkey_a_id', as : 'charlies'});
ModelAlpha.find({
where: {id: '123'},
include: [
{
model: ModelBeta,
as: 'beta',
attributes: [],
},
{
model: ModelCharlie,
as: 'charlies',
attributes:[],
where: {
attributeC: ......
}
}
],
limit: 1
}).then(result => {
//...
});
Has anyone come up with this situation before?
Thank you.

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

NestJs/Swagger(OpenAPI) defining nested objects in query parameters

I'm trying to correctly define OpenAPI spec for the purposes of generating api client from that spec. I've encoutered a problem where we have a complex query object with nested objects and arrays of objects for get a GET route.
Lets take these classes as an example.
class Person {
#ApiProperty()
name!: string
#ApiProperty()
location!: string
}
class CompanyDto {
#ApiProperty()
name!: string
#ApiProperty({
type: [Person],
})
employees!: Person[]
}
And a get request with #Query decorator.
#Get('test')
async testing(#Query() dto: CompanyDto): Promise<void> {
// ...
}
What I'm getting is.
{
get: {
operationId: 'testing',
parameters: [
{
name: 'name',
required: true,
in: 'query',
schema: {
type: 'string',
},
},
{
name: 'name',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'location',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
],
responses: {
'200': {
description: '',
},
},
tags: ['booking'],
},
}
I've also tries to define Query params by adding #ApiQuery decorator and it almost works.
#ApiQuery({
style: 'deepObject',
type: CompanyDto,
})
--
{
get: {
operationId: 'testing',
parameters: [
{
name: 'name',
required: true,
in: 'query',
schema: {
type: 'string',
},
},
{
name: 'name',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'location',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'name',
in: 'query',
required: true,
schema: {
type: 'string',
},
},
{
name: 'employees',
in: 'query',
required: true,
schema: {
type: 'array',
items: {
$ref: '#/components/schemas/Person',
},
},
},
],
responses: {
'200': {
description: '',
},
},
tags: ['booking'],
},
}
However now I'm getting duplicate query definitions mashed in to one. Is there a way to prevent or overwrite #Query definition? Or just a better way to define complex #Query in general?
Ended up creating a custom decorator to extract query without generating OpenAPI Spec.
export const SilentQuery = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest()
if (data) {
return request.query[data]
} else {
return request.query
}
},
)
So now you can use #ApiQuery with deepObject style.
Also if your're using ValidationPipes with class-validator for example. Make sure to set validateCustomDecorators to true
#SilentQuery(new ValidationPipe({ validateCustomDecorators: true }))

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).

How to specify the kendo schema to use in a div

I have the following code in my View using the kendo grid:
<div id="MyGrid"
data-role="grid"
data-editable="true"
data-toolbar='[{ template: kendo.template($("#ToolbarTemplate").html()) }]'
data-columns='[
{ field: "Description" },
{ field: "Value" },
{ command: [{name: "destroy", template: kendo.template($("#DeleteTemplate").html())}], width: 60}
]'
data-bind="source: MyDataSource">
Then in a script section a have:
kendo.bind($("#MyGrid"), MyViewModel)
Everything is working fine. However, now I'm trying to implement a validation to let the user knows that any of the fields inside the Kendo Grid is required. I saw that it can be done in the schema as follow (Kendo doc link):
schema: {
model: {
id: "ProductID",
fields: {
ProductID: { editable: false, nullable: true },
ProductName: { validation: { required: true } },
UnitPrice: { type: "number", validation: { required: true, min: 1} },
Discontinued: { type: "boolean" },
UnitsInStock: { type: "number", validation: { min: 0, required: true } }
}
}
}
Is there a way to do the same in the div tag as the rest of the attributes? Does data-schema attribute exist?
Thanks in advance
You are missing the kendo validator initialization part in your code. Please refer the following links for documentation and demo
Documentation
Kendo Validator demo

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