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
}
]
})
Related
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]
}
});
I am trying to find a way to remove rows from the DB through the parent model (menu) that has many children (foods). I only want to delete certain rows though, not all.
Menu.js
...
Menu.hasMany(models.Food, {
as: 'foods',
foreignKey: 'menuId',
sourceKey: 'id'
});
...
In my controller I have the following to try and delete certain foods off the menu.
...
const result = await menu.destroyFoods({
where: {
name: ['Pasta', 'Pizza']
}
});
...
I have also tried singular destroyFood as well. For both I am getting destoryFood/destoryFoods is not a function. Is there any easy way to do this from the instance of menu? New to sequelize, would love some help. Thanks.
Thanks
You can use menu.removeFoods() and menu.removeFood() - see Special methods/mixins added to instances: Foo.hasMany(Bar) for more information.
You will also need to use the Op.in query operator to specify multiple values for Food.name.
const { Op } = require('sequelize');
const result = await menu.removeFoods({
where: {
name: {
[Op.in]: ['Pasta', 'Pizza'],
}
}
});
This is the equivalent of calling Food.destroy() where the menuId is equal to the menu.id from the earlier result.
const results = await Food.destroy({
where: {
menuId: menu.id,
name: {
[Op.in]: ['Pasta', 'Pizza'],
},
},
});
I am trying to query for music albums that are associated with an exact amount of artists.
I am correctly associating the models using a many-to-many table but my query is failing to do what I'd like it to do.
albumArtists = albumArtists.map((artist) => {
return artist.id;
});
const album = await DB.Album.findOne({
where: {
name: albumName,
},
include: [
{
model: DB.Artist,
through: {
where: {
artistId: {
[Op.and]: albumArtists
}
}
}
},
],
});
I am trying to query for an album with name set to albumName and has associated albumArtists with artistId matching all in albumArtists.
I tried setting my where clause outside of the through object but that didn't seem to work either.
Try to update through to this one:
through: { model: DB.AlbumArtists // write correct model name }
And also yo can simplify where condition:
where: { artistId: albumArtists }
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"]];
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',
},
}
},
},
});