Possible to WHERE on nested includes in Sequelize? - javascript

I've got a problem that I've been stuck on, to no avail - seemingly similar in nature to Where condition for joined table in Sequelize ORM, except that I'd like to query on a previous join. Perhaps code will explain my problem. Happy to provide any extra info.
Models:
A.hasMany(B);
B.belongsTo(A);
B.hasMany(C);
C.belongsTo(B);
This is what I'd like to be able to achieve with Sequelize:
SELECT *
FROM `A`AS `A`
LEFT OUTER JOIN `B` AS `B` ON `A`.`id` = `B`.`a_id`
LEFT OUTER JOIN `C` AS `B->C` ON `B`.`id` = `B->C`.`b_id`
AND (`B`.`b_columnName` = `B->C`.`c_columnName`);
This is how I imagine this working: (instead it will create a raw query (2 raw queries, for A-B/C) with AND ( `C`.`columnName` = '$B.columnName$')) on the join (second arg is a string). Have tried sequelize.col, sequelize.where(sequelize.col..., etc..)
A.findOne({
where: { id: myId },
include: [{
model: B,
include: [{
model: C,
where: { $C.c_columnName$: $B.b_columnName$ }
}]
}]
});

Use the Op.col query operator to find columns that match other columns in your query. If you are only joining a single table you can pass an object instead of an array to make it more concise.
const Op = Sequelize.Op;
const result = await A.findOne({
include: {
model: B,
include: {
model: C,
where: {
c_columnName: {
[Op.col]: 'B.b_columnName',
},
}
},
},
});

Related

Sequelize: How to select subset of an array column?

I have a table with an array column called tokens
I can query it via npm sequelize with no issues, Sometimes this column may have upto 20k elements in the array which i dont always need. I need just 10 elements from it
In SQL this would be
select tokens[:10] from schema.table
How I do this using sequelize ?
This is what I'm doing now
const whereClause = {
where: { active: true },
attributes: {
exclude: ['tokens'],
include: ['tokens[:10]'],
},
};
table.findAll(whereClause);
This gives the following error
original: error: column "tokens[:10]" does not exist
It is looking for a column named "tokens[:10]" instead of taking a subset.
What am I doing wrong ?
You can use literals when you select attributes in sequelize. You can try something like this,
const whereClause = {
where: { active: true },
attributes: [
[Sequelize.literal(`tokens[:${sub-array-length}]`), 'tokens']
]
};
table.findAll(whereClause);
Note the use of colons to denote the sub-array slicing index

Sequelize exclude orderBy from subQuery

Trying to use sequelize findAndCountAll method, to get items.
I've to use distinct, and offset with limit due to my task.
The problem is, that in associated model i've column with array type, and i need to order parent model by that array length.
My query looks like :
const { rows, count } = await this.repo.findAndCountAll({
where: { someField: someValue },
col: 'someCol',
distinct: true,
include: [
{
model: someNestedModel,
as: 'someNestedModelAssociation',
include: [{ model: someInnerNestedModel, as: 'someInnerNestedAssociation' }]
}
],
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
//#ts-ignore
order: this.getOrderByOptions(sortOrder, orderBy),
limit,
offset
});
getOrderByOptions(sortOrder, orderBy) {
switch (orderBy) {
case Sort_By_Array_Length:
return Sequelize.literal('json_array_length(someModelName.someColumnWithArrayName) ASC');
default:
return [[orderBy, sortOrder]];
}
}
The problem is, that my order by query is used both in subQuery and mainQuery.
And using it into subQuery leads to error, cz there is no such field.
If i use subQuery:false flag, it works, but then i got messed with returning results, due to problems with subQuery:false and offset&limits.
So the question is, is there a way, to exclude orderBy field from subQuery?
P.S. Models have many to many association with through table.

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"]];

Node.js Sequelize ManyToMany relations producing incorrect SQL

I'm having a problem with sequelize ManyToMany relations.
Here are my models...
var db = {
players: sequelize.define('players', {
name: Sequelize.STRING
}),
teams: sequelize.define('teams', {
name: Sequelize.STRING
}),
init: function() {
this.players.hasMany(this.teams, {joinTableName: 'teams_has_players'});
this.teams.hasMany(this.players, {joinTableName: 'teams_has_players'});
this.players.sync();
this.teams.sync();
}
};
Here's the find
db.players.findAll({
where: {team_id: 1},
include: ['teams']
}).success(function(results) {
// print the results
});
The above find will produce the following SQL:
SELECT
players . *,
teams.name AS `teams.name`,
teams.id AS `teams.id`
FROM
players
LEFT OUTER JOIN
teams_has_players ON teams_has_players.player_id = players.id
LEFT OUTER JOIN
teams ON teams.id = teams_has_players.team_id
WHERE
players.team_id = '1';
What appears to be wrong with this is that the WHERE statement should be WHERE teams.team_id = '1'
Where am I going wrong with this?
Thanks in advance
Hmm actually everything looks quite OK-ish. db.players.findAll with where: { team_id: 1 } should create a query with WHERE players.team_id = '1'. That's perfectly expected. Also, you teams won't have a team_id but an id instead. However, there is a good chance that include is broken atm.

Categories

Resources