Where condition in nested include in Sequelize - mysql

I have a model with 3 entities, Documents, Employees and Managers. A Document belongs to and Employee and an Employee belongs to a Manager.
My objective is to retrieve all the documents of a manager employees.
My piece of code is working
Document.findAll({
include: [{
model: models.Employee,
required: true,
as: 'employee',
include: [{
model: models.Manager,
required: true,
as: 'manager',
}],
}],
But I'm not really satisfied, I would like to have my where condition outside my include but when I try
where {
'employee.manager.id': id
}
an error is raised.
Is it possible to setup a where condition outside the includes ?
EDIT :
I changed my code to
Document.findAll({
where: {'$employee.manager.id$': id},
include: [{
model: models.Employee,
required: true,
as: 'employee',
include: [{
model: models.Manager,
required: true,
as: 'manager',
where: { id: managerId },
}],
}],
and it's working.
Now, I would like to refine my model. Each document as a type (administrative, evaluation ...) and I would like to retrieve the most recent document for each type for each manager. I used an order by which is working fine and tried to use a group by which is not working
order: [ [ 'updatedAt', 'DESC' ]],
group: ['type'],
I get the following message : column \"Document.id\" must appear in the GROUP BY clause or be used in an aggregate function.
Any idea what I'm doing wrong ?

Yes, you can do that ,
Issue with current one :
where {
'employee.manager.id': id // <--- Here 'employee.manager.id' cosidered as whole column name
}
Solution :
where {
'$employee.manager.id$': id //<--- To make sequlize realize you need to place it b/w $$ ,
//or
sequelize.col('employee.manager.id') : id // <--- You can try this also
}

Related

how to get rows that has at least 1 association row with sequelize

I have 'Ingredient' and 'Log' Tables like this
[Ingredient Table]
id
..
...
[Log Table]
id
Ingredient_id
record_date
..
...
the relationship is Log.belongsTo(Ingredeint)
how can I find all ingredients which have at least 1 row of Log?
I mean when I searching the Ingredients, If there is no related Log on Ingredient, I don't want to include that Ingredient on my search result.
what I did now to accomplish that is
const ingredients = await Ingredient.findAll({
include: {
model: Log
},
group: "id",
attributes: {
include: [
[sequelize.fn("COUNT", sequelize.col("record_date")), "order_count"]
]
}
})
const sortedIngredient = ingredients
.filter(ingredient => ingredient.dataValues.order_count > 0)
But I think there would be a better way.
Thank you for reading this.
If I understand you correctly you want to do a inner join in your include, then you would only return ingredients that have some match in the included model.
Try to change the include to:
include: {
model: Log
required: true, // <-- Add this row
}
More info about require can be found in the docs: https://sequelize.org/master/class/lib/model.js~Model.html#static-method-findAll
Another option that maybe could help you is to add having to filter on a aggregated column, like this:
const ingredients = await Ingredient.findAll({
include: {
model: Log,
},
group: "id",
attributes: {
include: [
[sequelize.fn("COUNT", sequelize.col("record_date")), "order_count"],
],
},
having: sequelize.literal("`order_count` > 0"), // <-- Add this row
});
Does that help?

Sequelize create with associations

I have been trying to define a relationship between 3 tables and then create them all in one create function. For some reason, while creating the 3 models, the linking IDs (foreign keys) are undefined and are not passing on. Here are the associations:
Person.js:
models.person.Lead = models.person.hasMany(models.lead, {
onDelete: "CASCADE",
foreignKey: "person_id"
});
Lead.js:
models.lead.Person = models.lead.belongsTo(models.person, {foreignKey: 'person_id'});
models.lead.Sealant_customer = models.lead.hasOne(models.sealant_customer, {
onDelete: "CASCADE",
foreignKey: 'lead_id'
})
sealantCustomer.js:
models.sealant_customer.Lead = models.sealant_customer.belongsTo(models.lead);
The build function:
let sealantCustomer = models.sealant_customer.build({
address: body.address,
city: body.city,
roof_size: body.roofSize,
last_sealed: body.lastSealed,
existingSealant: body.existingSealant,
leaks_freq: body.leaksFrequency,
floor: body.floor,
elevator: body.elevator,
panels: body.panels,
home_type: body.homeType,
urgency: body.urgency,
next_step: body.nextStep,
more_info: body.moreInfo,
lead: {
site,
url: body.url,
date,
ip: body.ip,
person: {
name: body.name,
email: body.email,
phone: body.phone,
date,
city: body.city ? body.city : undefined,
address: body.address ? body.address : undefined,
}
}
}, {
include: [{
model: models.lead,
association: models.sealant_customer.Lead,
include: [{
model: models.person,
association: models.lead.Person
}]
}]
})
The outputted object is good except for the fact that lead_id and person_id are nulls (Each model has its own ID, but not the associated model's id). I also should note there are no validation errors and the data is good.
The library has a bug in the build function as far as I can tell. Same syntax with create worked perfectly.
In Sequelize v6, the association identifier in the include section is not valid. Otherwise, this build function should properly work.

Pulling Sequelize Info from multiple tables

I'm pretty new to new sequelize but I'm trying to figure out how I can pull sequelize information from multiple tables (Place and Review tables) and render them on the same page. The Review table has a User Id and a Place Id. I've tried raw queries and different variations of the code below to no avail. What sort of syntax should I use in this case?
User.hasMany(Review);
Review.belongsTo(User);
User.hasMany(Place);
Place.belongsTo(User);
Place.hasMany(Review);
Review.belongsTo(Place);
app.get('/place/:category/:id', function(req, res){
var id = req.params.id;
Place.findAll({
where : {id : id},
include: [{
model: [Review]
}]
}).then(function(reviews){
res.render('singular', {reviews});
});
});
From your API route definition, I assume you're trying to display reviews for a place based on place ID.
So, to achieve this, you could model your table associations as
Places.hasMany(Reviews);
Users.hasMany(Reviews);
Review.belongsTo(Places);
Review.belongsTo(Users);
Now, based on this association, you could perform the query like this:
Places.findById(req.params.id, {
include: [{
model: Reviews,
required: false,
include: [{
model: Users,
required: false
}]
}]
}).then(function(place) {
// The rest of your logic here...
});

Populating multiple tables in sails waterline orm

I am working on a sails applications which contains multiple(>2) tables which I need to join with the help of populate method
e.g.
Category.js model
attributes: {
CategoryID:{
type:"integer",
required:true,
primaryKey:true,
autoIncrement:true
},
SubCategories:{ //REFERING TO SUB-CATEGORY TABLE
collection:'SubCategory',
via:'CategoryID'
},
CategoryName:{
type:"string",
required:true,
maxLength:50
}
}
this is SubCategory.js model.
attributes: {
id:{
type:'integer',
required:true,
primaryKey:true,
autoIncrement:true,
maxLength:10,
columnName:'SubCategoryID'
},
CategoryID:{
model:'Category' //REFERING TO CATEGORY TABLE
},
ProductsOfCategory:{ //REFERING TO PRODUCT TABLE
collection:'Product',
via:'SubCategoryID'
},
SubCategory:{
type:"string",
required:true,
maxLength:50
}
}
and Product.js model
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
maxLength: 10,
columnName:'ProductID'
},
SubCategoryID: {
model:'SubCategory'
},
ProductDetails:{
collection:'ProductDetails',
via:'ProductID'
},
ProductName: {
type: "string",
required: true,
maxLength: 50
}
}
and ProductDeatils.js model
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
maxLength: 10,
columnName:'ProductDetailID'
},
ProductID:{
model:'Product'
},
Size:{
type:"string",
required:true,
maxLength:10
},
Color:{
type:"string",
required:true,
maxLength:10
}
}
On Populating, I am able to populate the category and sub-category of each category.
Category.find()
.populate('SubCategories')
.exec(function(err, categories){
if (err) {
console.log(err);
return res.json(err);
}
console.log(categories);
res.json(categories);
})
How to populate the all above table in one go such that after final query we get all the above details in one json.
We get join of all above tables
that is category having all sub-categories, sub-category having all products and all product have product details in one json
You ask a great question. There has been massive interest in getting nested populate feature into sails, literally tens of issue requests and PRs etc.
Take a look at some history here:
[FEATURE REQUEST] Recursively populate #308 - i was late to the party, making the request on October 29th 2014 as you'll see in the history.
As far as I know, most conversations eventually converged here (after a couple of years of Sails users requesting the feature):
Deep populate #1052 (the issue remains open as of writing 14 Jan 2016)
It is unclear from the state of that Issue where we are. The history of both links does suggest alternative workarounds others have used.
My hunch is that recursive populate is not supported out of the box.
What I did when using waterline model associations with SailsJS, was work with a package like async.js - use something like waterfall to explicitly populate the child relationships programmatically. You can combine doing this with overriding the default toJSON() of the models you invoke to add their relationships (which you have programmatically populated) to the JSON response. You could equally choose to use the built-in promises to achieve the same thing.
Found this (dated, 2014) SOF Question which offers more information.
Someone, do please correct me here if I have missed this feature addition in a recent Sails or Waterline version - couldn't find anything in the release notes for either project to say this was supported.

Sequelize include (how to structure query)?

I have a query I'm trying to perform based on a one to many relationship.
As an example there is a model called Users and one called Projects.
Users hasMany Projects
Projects have many types which are stored in a type (enum) column. There are 4 different types that potentially a user may have that I want to load. The catch is I want to include the most recent project record (createdAt column) for all networks that potentially will be there. I have not found a way to structure the query for it to work as an include. I have however found a way to do a raw query which does what I want.
I am looking for a way without having to do a raw query. By doing the raw query I have to map the returned results to users I've returned from the other method, or I have to do a simple include and then trim off all the results that are not the most recent. The latter is fine, but I see this getting slower as a user will have many projects and it will keep growing steadily.
This allow serialize a json for anywhere action about a model. Read it, very well
sequelize-virtual-fields
// define models
var Person = sequelize.define('Person', { name: Sequelize.STRING });
var Task = sequelize.define('Task', {
name: Sequelize.STRING,
nameWithPerson: {
type: Sequelize.VIRTUAL,
get: function() { return this.name + ' (' + this.Person.name + ')' }
attributes: [ 'name' ],
include: [ { model: Person, attributes: [ 'name' ] } ],
order: [ ['name'], [ Person, 'name' ] ]
}
});
// define associations
Task.belongsTo(Person);
Person.hasMany(Task);
// activate virtual fields functionality
sequelize.initVirtualFields();