Sequelise creates weird query - javascript

I have 3 tables city, car, images
I need to get cars of cities for city/parent_id = 21 and images of the cars as well. (using a where clause to filter images on bases of sort_number)
My raw query is this:
SELECT
`car`.`car_id`,
`car`.`chassis_no`,
`images`.`car_image_id` AS `images.car_image_id`,
`images`.`directory` AS `images.directory`,
`images`.`file_name` AS `images.file_name`
FROM `car` AS `car`
INNER JOIN `city` AS `city` ON
`car`.`parent_id` = `city`.`id`
AND `city`.`parent_id` = 21
INNER JOIN `images` AS `images` ON
`car`.`car_id` = `images`.`car_id`
AND `images`.`sort_number` = 1
limit 0, 5;
and the Sequelise generated query is this:
explain SELECT
`car` .*,
`images`.`car_image_id` AS `images.car_image_id`,
`images`.`directory` AS `images.directory`,
`images`.`file_name` AS `images.file_name`
FROM
(
SELECT
`car`.`car_id`,
`car`.`chassis_no`
FROM
`car` AS `car`
INNER JOIN `city` AS `city` ON
`car`.`parent_id` = `city`.`id`
AND `city`.`parent_id` = 21
WHERE
(
SELECT
`car_id`
FROM
`images` AS `images`
WHERE
(`images`.`sort_number` = 1
AND `images`.`car_id` = `car`.`car_id`)
LIMIT 1 ) IS NOT NULL
LIMIT 0,
5) AS `car`
INNER JOIN `images` AS `images` ON
`car`.`car_id` = `images`.`car_id`
AND `images`.`sort_number` = 1;
Doing explain shows the sequelise query is not very efficient and it does looks weird to me, instead of plane joins. The result of the both queries are same though.
There are my relations:
//Relations
city.hasMany(car, { foreignKey: 'parent_id', sourceKey: 'id' })
car.belongsTo(city, {
foreignKey: 'parent_id',
sourceKey: 'id'
})
car.hasMany(image, { foreignKey: 'car_id', sourceKey: 'car_id' })
image.belongsTo(car, { foreignKey: 'car_id', sourceKey: 'car_id' })
The query code as follows:
return this.findAll({
attributes: ['car_id', 'car_model'],
include: [
{
model: db.city,
required: true,
attributes: [],
where: { parent_id: 21 }
},
{
model: db.image,
required: true,
attributes: ['image_id', 'file_name'],
where: { sort_number: 1 }
}
],
offset: 0,
limit: 5
})
Is there any way to make it like the raw query join I have above? What am I missing?
Is Sequelise limmited in generating better queries?
Should I use raw query function to write my joins myself it can't be helped?
Please guide, I'm new to sequelise. sorry for long post.

Related

Filter by month in sequelize from createdAt

I have raw query like this
SELECT trx.WarehouseId, pr.CategoryId, pr.id AS productId, ct.category_name, pr.product_name, pr.description, trx_items.price_per_item AS price, trx_items.quantity,
trx_items.price_per_item * trx_items.quantity AS total, wr.warehouse_name, trx_items.createdAt
FROM transactionitems AS trx_items
JOIN transactions AS trx ON trx.id = trx_items.TransactionId
JOIN products AS pr ON pr.id = trx_items.ProductId
JOIN categories AS ct ON ct.id = pr.CategoryId
JOIN warehouses as wr ON wr.id = trx.WarehouseId
WHERE MONTH(trx_items.createdAt) = 2;
it'll return all data in February, so i want to convert raw query into sequlize but i don't know how to get month in sequlize. i have sequelize code like this
const { createdAt } = req.query
if (createdAt) {
const findDataFilter = await db.Transaction.findAndCountAll({
include: [
{
model: db.Warehouse,
},
{
model: db.TransactionItem,
include: [
{
model: db.Product,
include: [
{
model: db.Category,
},
],
},
],
required: true,
where: sequelize.fn(
"MONTH",
sequelize.col("createdAt")
),
},
],
})
return res.status(200).json({
message: "Get data filtered",
data: findDataFilter.rows,
dataCount: findDataFilter.count,
})
}
it return "Column 'createdAt' in on clause is ambiguous". i'm new in sequelize so idk what method to use. if i write where: {createdAt} this return nothing
i want to filter by month in table TransactionItem but with sequelize.
I think createdAt exists in more tables than transactionitems, that's why the error is "column ambiguos".
You should specify the table before the column like this:
sequelize.fn(
"MONTH",
sequelize.col("TransactionItem.createdAt")
)

Sequelize [Op.and] not working with M:N association

I have two models
Units and Filters which are related with M:N association through unit_filter table
this.belongsToMany(Unit, {
through: "unit_filter",
as: "units",
});
this.belongsToMany(Filter, {
through: 'unit_filter',
as: 'filters',
});
The goal is to fetch units which have more than 1 filter associated with and condition.
let result = await Unit.findAll({
include: [
{
model: Filter,
where: {
id: {
[Op.and] : [2,252,4,80]
}
},
as: 'filters',
},
],
});
The above is only working if there is only one id in the array which does not make any sense.
Seqeulize documenation states
Post.findAll({
where: {
[Op.and]: [{ a: 5 }, { b: 6 }], // (a = 5) AND (b = 6)
}
})
So the code I have should work theoritically. I also tried with
where: {
[Op.and] : [{id:2},{id:252},{id:4},{id:80}]
}
which results in getting all the items from the db. It does not even care about the where condition in this case.
Would be of great help if any one points me in right direction.
You need to use Sequelize.literal with a subquery in where option in order to filter units that have more than 1 filter because simply indicating several ids of filters you will get units that have one of indicated filters (from 1 to N).
let result = await Unit.findAll({
where: Sequelize.where(
Sequelize.literal('(SELECT COUNT(*) FROM unit_filter as uf WHERE uf.unit_id=unit.id AND uf.filter_id in ($filter_ids))'),
Op.gt,
'1'),
bind: {
filter_ids: [2,252,4,80]
}
});

Sequelize - The multi-part identifier XXX could not be bound

I'm trying to run a query with an order option in Sequelize with SQL Server. I've read some examples in SO but I haven't found a solution while running the queries on Sequelize.
let order = [["winner","new_apv","asc"]];
const include = [
{
model: Item_Supplier,
as: "itemSupplier",
attributes: ["id", "supplierOrderId", "cost"],
include: [
{
model: Supplier,
as: "supplier"
}
]
},
{
model: Winner,
as: "winner",
attributes: ["supplierId", "new_apv"],
include: [
{
model: Supplier,
as: "supplier",
attributes: ["supplierName"]
}
]
}
];
await Item.findAll({include,order})
This is the error message
The multi-part identifier "winner.new_apv" could not be bound.
Here is the SQL Server query Sequelize generates:
SELECT [item].*, [itemSupplier].[id] AS
[itemSupplier.id], [itemSupplier].[supplierOrderId] AS
[itemSupplier.supplierOrderId], [itemSupplier].[cost] AS
[itemSupplier.cost], [itemSupplier->supplier].[id] AS
[itemSupplier.supplier.id], [itemSupplier->supplier].[supplierName] AS
[itemSupplier.supplier.supplierName], [itemSupplier->supplier].[duns] AS
[itemSupplier.supplier.duns]
FROM (SELECT [item].[id], [item].[item_price], [item].[common_code], [item].[uom], [item].[usage_per_item], [item].[apv],
[item].[impac_commodity], [item].[mfgname], [item].[mtr_grp_desc], [item].[description], [item].[comments], [item].[renewed_2019],
[item].[currency], [item].[contractId], [item].[mtrId], [item].[allocationId], [winner].[id] AS [winner.id],
[winner].[supplierId]
AS [winner.supplierId], [winner].[new_apv]
AS [winner.new_apv], [winner->supplier].[id]
AS [winner.supplier.id], [winner->supplier].[supplierName]
AS [winner.supplier.supplierName] FROM [items] AS [item]
INNER JOIN [winners] AS [winner]
ON [item].[id] = [winner].[itemId]
AND [winner].[deletedAt] IS NULL
INNER JOIN [suppliers] AS [winner->supplier]
ON [winner].[supplierId] = [winner->supplier].[id]
WHERE ([item].[deletedAt] IS NULL AND ([item].[contractId] = 4 AND [item].[renewed_2019] LIKE N'YES%'))
ORDER BY [item].[id] OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY) AS [item] LEFT OUTER JOIN [item_suppliers] AS [itemSupplier]
ON [item].[id] = [itemSupplier].[itemId] AND ([itemSupplier].[deletedAt] IS NULL) LEFT OUTER JOIN [suppliers]
AS [itemSupplier->supplier] ON [itemSupplier].[supplierId] = [itemSupplier->supplier].[id] ORDER BY [winner].[new_apv] ASC;
Using Sequelize literal and adding the single quotes in the literal itself did the trick.
order = [[Sequelize.literal('"winner.new_apv"'), desc ? "DESC" : "ASC"]];

Sequelize join table without selecting

I'd like to select IDs from a join table in a many-to-many relation in Sequelize, and restrict results based on one of the endpoints. For example:
ModelA:
- id
- deleted_at
- is_expired
ModelB:
- id
- deleted_at
- is_active
ModelAToModelB:
- a_id
- b_id
I'd like to do something like
SELECT id FROM ModelAToModelB ab INNER JOIN ModelB b ON ab.b_id = b.id WHERE ab.id = someA_ID AND b.deleted_at IS NULL;
I'm currently do something like
const ModelB = require("./modelb")
const ModelAToModelB = require("./modelatob");
ModelAToModelB.findAll({
where: { id: someA_ID },
include: [
{
model: ModelB,
where: { deleted_at: null }
}
]
})
That seems to work, except then I also get all the data from ModelB as well, when all I want is ModelAToB.id.
Is there any way to scrap ModelB's data? Or maybe use something from ModelA to get just the association IDs?
const ModelB = require("./modelb")
const ModelAToModelB = require("./modelatob");
ModelAToModelB.findAll({
where: { id: someA_ID },
include: [
{
model: ModelB.scope(null), //Prevent the default scope from adding attributes
where: { deleted_at: null },
required: true, //Force an inner join
attributes: [] //Join without selecting any attributes
}
]
})

Custom or override join in Sequelize.js

I need to create a custom join condition using Sequelize.js with MSSQL. Specifically, I need to join TableB based on a COALESCE value from columns in TableA and TableB and end up with a join condition like this:
LEFT OUTER JOIN [TableB]
ON [TableB].[ColumnB] = COALESCE (
[TableC].[ColumnC],
[TableA].[ColumnA]
)
I'd settle for an OR clause in my join:
LEFT OUTER JOIN [TableB]
ON [TableB].[ColumnB] = [TableA].[ColumnA]
OR [TableB].[ColumnB] = [TableC].[ColumnC]
I read that you can achieve behaviour like this by including required: false in your scope definition. As you can see, I've plastered my scope with it attempting to get this to work.
The best I could get is this (note the AND clause):
LEFT OUTER JOIN [TableB]
ON [TableB].[ColumnB] = [TableA].[ColumnA]
AND [TableB].[ColumnB] = COALESCE (
[TableC].[ColumnC],
[TableA].[ColumnA]
)
If I were using MySQL, I think I could simply use the COALESCE value from the SELECT in the JOIN and be good to go but in my prior research read that it was required to recalculate the value.
I've included a stripped down model definition for TableA:
export default (sequelize, DataTypes) => sequelize.define('TableA',
{
// Attributes omitted..
},
{
classMethods: {
associate ({
TableB,
TableC
}) {
this.belongsTo(TableB, {
foreignKey: 'ColumnA',
targetKey: 'ColumnB'
});
this.belongsTo(TableC, {
foreignKey: 'ColumnA',
targetKey: 'ColumnC'
});
},
attachScope ({
TableB,
TableC
}) {
this.addScope('defaultScope', {
attributes: [
...Object.keys(this.attributes),
[
sequelize.fn(
'COALESCE',
sequelize.col('[TableC].[ColumnC]'),
sequelize.col('[TableA].[ColumnA]')
),
'ColumnA'
]
],
include: [
{
model: TableB,
where: {
ColumnB: sequelize.fn(
'COALESCE',
sequelize.col('[TableC].[ColumnC]'),
sequelize.col('[TableA].[ColumnA]')
)
},
required: false
},
{
model: TableC,
required: false
}
],
required: false
}, { override: true });
}
}
}
);
Any assistance would be greatly appreciated, and if any additional information is required please let me know.
Note: I'm working with a legacy database and unfortunately cannot change the data structure.
Hi I had the same issue and finally I solved using a SQL pure query.
Example:
const query = `SELECT s.*, us.UserId \
FROM UserSections AS us \
RIGHT JOIN Sections AS s ON (s.id = us.SectionId) \
WHERE s.parentId = ${sectionId}`;
return db.query(query, { type: db.QueryTypes.SELECT });
The problem is that this function will return a IMyType instead of IMyTypeInstance
Can it works for you??

Categories

Resources