Sequelize - Include all children if any one matches - mysql

I have two entities, Post and Tag, I am trying to query for all posts that have any one tag passed to the where clause. In addition, I want to include ALL the tags for the final set of Posts.
The association is defined as so
Post.belongsToMany(
models.tag,
{
through: 'post_tag'
}
);
My query is like so
models.post.findAll({
limit: 20,
offset: 0,
attributes: [
'id',
'name'
],
include: [{
model: models.tag,
attributes: ['name'],
where: {
name: {
[Op.in]: ['tagNameHere']
}
}
}],
where: [{
active: {
[Op.not]: 'False'
}
}],
order: [ ['name', 'ASC'] ]
})
It does work, but the included tags array is ONLY that one specified within the Op.in. I want ALL the tags to be included
Any better way of going about it?

One approach is to make two passes: 1) find posts that have particular tag, 2) find all tags for those posts. You need a third association to make this happen:
models.post.belongsToMany(models.tag, {through: models.postTag, foreignKey: 'post_id'} );
models.tag.belongsToMany (models.post,{through: models.postTag, foreignKey: 'tag_id' });
models.post.hasOne(Post, {
foreignKey: {name: 'id'},
as: 'selfJoin'
});
Now, identify posts that have particular tag (or tags)
models.post.addScope('hasParticularTag',
{
attributes: ['id'],
include: [
{
model: models.tag,
through: models.postTag,
attributes: [],
where: {name: 'TAG-YOU-WANT'} // your parameter here...
}]
});
Finally, list selected posts and all their tasks...
models.post.findAll({
attributes: ['id','name'],
include: [
{ // ALL tags
model: models.tag,
through: models.postTag,
attributes: ['name']
},
{ // SELECTED posts
model: models.post.scope('hasParticularTag'),
required: true,
as: 'selfJoin', // prevents error "post isn't related to post"
attributes: []
}]
})
HTH....

Related

How to limit includes on a belongsToMany association in Sequelize JS?

I currently have the following models in Sequelize:
Product
and
ProductList.belongsToMany(models.Product, { as: 'products', through:
sequelize.models.ProductListProduct, foreignKey: 'productListId'});
and the pivot, ProductListProduct
I am currently trying to get a series of productList to show on my homepage,
and would need to limit the returned products to a certain value (6 in this case):
let productLists = await ProductList.findAll({
where: {
slug: ['recommended', 'new_and_promos']
},
include: [{
model: Product,
as: 'products',
include: ['attributes', 'images'],
where: {
active: true
}
}],
order: [
[
'products', ProductListProduct, 'position', 'ASC'
]
]
})
This is the current fetch, however if I add a limit to the include, it tells me that only hasMany can have {separate: true} ;
To recap, what I'm trying to achieve is to return n ProductList, each with just m Product attached.
Managed to get it working like this, doesn't look ideal but it does the job:
let productLists = await ProductList.findAll({
where: {
slug: ['recommended', 'new_and_promos']
},
include: [{
model: Product,
as: 'products',
include: ['attributes', 'images'],
where: Sequelize.literal('`products->ProductListProduct`.`position` < 8')
}],
order: [
[
'products', ProductListProduct, 'position', 'ASC'
]
]
})

How to fix Sequelize nested includes not working with limit/order/attributes?

I've got some models that have associations with each other, and I need to grab them all in a certain request. I need to use limit, order, and attributes on basically all parts of it, but this is causing the nested includes to freak out and I'm not entirely sure what's wrong with it.
It doesn't really print any errors or anything, the models either just don't get included in the response (i.e they are empty.), or they get included but stuff like order/limit is ignored.
I've already tried using subQuery, separate, etc... None of those worked.
The query in question;
const categories = await models.Category.findAll({
attributes: ['id', 'title', 'description'],
order: [['title', 'ASC']],
include: [
{
model: models.Product,
attributes: ['id', 'title'],
through: { attributes: [] },
include: [
{
model: models.Price,
attributes: ['id', 'amount', 'createdAt'],
order: [['createdAt', 'DESC']],
limit: 1,
},
],
},
],
});
The associations;
models.Category.belongsToMany(models.Product);
models.Product.belongsToMany(models.Category);
models.Product.hasMany(models.Price);
models.Price.belongsTo(models.Product);
I ideally want the query provided above to return;
Category with the order of Ascending based on title.
Product inside of Category with the attributes id and title.
Price inside of Product with the attributes id, amount, and createdAt, the order of Descending based on createdAt, and with a limit of 1.
In order to get the query to sort by Product.Price.createdAt, add [models.Product, models.Price, 'createdAt', 'DESC'] to order. As far as limiting: in order to limit the included model, you need to run it as a separate query, so add separate: true to the include.
Code:
const categories = await models.Category.findAll({
attributes: ['id', 'title', 'description'],
order: [['title', 'ASC'], [models.Product, models.Price, 'createdAt', 'DESC']],
include: [
{
model: models.Product,
attributes: ['id', 'title'],
through: { attributes: [] },
include: [
{
model: models.Price,
attributes: ['id', 'amount', 'createdAt'],
separate: true,
limit: 1,
},
],
},
],
});

Sequelize select only chosen attributes

I am using MySQL database, when I am doing:
models.modelA.findAll({
attributes: [
['modelA.id','id']
],
raw: true,
include:
[
{
model: models.modelB,
required: true
}
]
}).then(function (tenants) {
});
Nevertheless that I've selected only id, Sequelize is retrieving all attributes, from related table as well so I'm getting {id, ... All attributes here}.
How I can prevent this? Sometimes I want to select only 2/3 columns and Sequelize is always selecting all of them what is not efficient.
You can do something like the following
models.modelA.findAll({
attributes: [
'id'
],
raw: true,
include:
[
{
model: models.modelB,
attributes: ['fieldName1', 'fieldName2'], // Add column names here inside attributes array.
required: true
}
]
}).then(function (tenants) {
});
You can try sending empty array as attributes to exclude them:
models.modelA.findAll({
attributes: [
['modelA.id','id']
],
raw: true,
include:
[
{
model: models.modelB,
attributes: [],
required: true
}
]
}).then(function (tenants) {
});

Exclude primary key attributes from a sequelize query

I have a sequelize query from multiple tables inner joined together. I need to group them by on a nested include model but the sequelize query throws the primary key every time, even if I mention the attributes as: attributes:[].
However attributes:[] is working for nested include models.
You can exclude any attributes by passing an exclude array into the attributes option:
MyModel.findAll({
attributes: {exclude: ['some_field']}
});
For included models use attributes: ['prop_name']
Remember include/exclude will not affect nested tables use through: { attributes:[]}
Model.addScope('scope_name', {
attributes: ['id', 'name'],
include: [
{
model: models.role,
as: 'roles',
attributes: ['name'],
through: {
attributes: []
}
}
]
More details can be found here: https://github.com/sequelize/sequelize/issues/4074#issuecomment-153054311
I want to add that you can explicitly list the attributes you want and that they work on nested inner joins as follows:
const my_model = await MyModel.findById(id, {
include: [
{
model: AnotherModel,
attributes: [ 'displayName', 'email' ] // only these attributes returned
},
{ model: YetAnotherModel,
include: [{
model: AnotherModel,
attributes: [ 'id', 'displayName', 'email' ]
}]
}
]
})
Your returned Object should look like:
{
// ...MyModel attributes
,
AnotherModel: {
displayName: '...',
email: '...',
},
YetAnotherModel: {
// ...YetAnotherModel's attributes
,
AnotherModel: {
id: '...',
displayName: '...',
email: '...',
}
}
}

Sequelize Query - Finding records based on many-to-many table and parent table

Given the following sequelize models:
var User = db.define('user', {
name: Sequelize.STRING
});
var Group = db.define('group', {
name: Sequelize.STRING,
public : { type: Sequelize.BOOLEAN, defaultValue: true }
});
Group.belongsToMany(User, { as: 'specialUsers', through: 'user_groups', foreignKey: 'group_id' });
User.belongsToMany(Group, { through: 'user_groups', foreignKey: 'user_id' });
How would I go about finding the Groups for a through the Groups model where the Groups returned should be those where the user has a record in the many to many table -- or -- the group is a public group?
I've tried something like this:
return Group.findAll({
attributes: ['name', 'public'],
include: [{
model: User,
as: 'specialUsers',
where: {
$or : [
{name: 'Neill'},
Sequelize.literal('"group"."public" = true')
]
}
}]
});
return Group.findAll({
attributes: ['name', 'public'],
include: [{
model: User,
as: 'specialUsers',
}],
where: {
$or : {
'$users.name$": 'Neill',
public: true
}
}
});
Should work if you are on a fairly recent version. Note that I moved the where out of the include