Sequelize - how to get all Employees for all Locations - mysql

I'll preface this by saying I think my model associations may be incorrect
Basically what i'm trying to do is is return an array of all Employees for a company.
Get all locations that have the same companyId
With those locations, get all Profiles tied to the locationId.
Profiles are linked to Locations through Employees.
Below is my code.
The query:
Location.findAll({
where: { companyId: user.profile.companyId },
include: [
{
model: Employee
}
]
})
This generates the error "employee is not associated to location!".
My models:
Employee.belongsTo(Profile)
Employee.belongsTo(Location)
Profile.belongsTo(Company)
Location.belongsTo(Company)
Profile.belongsToMany(Location, {
through: Employee,
foreignKey: "profileId"
})
Location.belongsToMany(Profile, {
through: Employee,
foreignKey: "locationId"
})
EDIT:
Adding Location.hasMany(Employee) allows me to do the query however it still requires a for loop within another for loop to get the correct data structure needed.
const locations = await models.Location.findAll({
where: { companyId: user.profile.companyId },
include: [{ model: models.Profile }]
})
const response = []
locations.forEach(location => {
location.profiles.forEach(profile => {
response.push({ location, profile })
})
})
return response
The query below returns what exactly as is however its only for a single location. I need to run the same query but for multiple locations.
Employee.findAll({ where: { locationId }, include: [Profile, Location] })

You've specified that Location belongsToMany Locations, but you've failed to specify the other way around. You should specify that a Location hasMany Employees.
Specifying Employee.belongsTo(Location) allows you to include related Locations with Employees. Specifying Location.hasMany(Employee) allows you to include related Employees with Locations.
I was able to recreate your problem and fix it with this line.
Location.hasMany(Employee, {
//...
})

Related

Query model based on value inside one of its columns

I have a sequelize model called staff. One of the staff columns is called locations which holds the id's of all the locations this staff is available at. I want to create a query that uses a Location ID to get all staff active at that location. How can I do that using the ORM?
Assuming I have understood your question correctly and your locations column contains CSV data, then you need to use FIND_IN_SET() -
Staff.findAll({
where: sequelize.where(sequelize.fn('FIND_IN_SET', locationId, sequelize.col('locations')), {[Op.gt]: 0})
})
A better option would be to normalize your data as this query is non-SARGable.
In Sequelize, you can perform a query to filter staff based on their locations using the include and where options in your query.
const staff = await Staff.findAll({
include: [
{
model: Location,
as: 'locations',
where: { id: locationId },
through: { attributes: [] }
}
]
});
const findStaff = await StaffModel.findAll({
where: {
locationId: "your locationId"
}
})

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 multiple foreign key includes only one column

i have made two foreign keys from user table.
db.Subscription.belongsTo(db.User, {foreignKey: 'creatorId'});
db.Subscription.belongsTo(db.User, {foreignKey: 'subscriberId'});
during search query i get subscriberId column included instead of creatorId
Subscription.findAll({
where: {
subscriberId: req.decoded._id
},
include: [
{
model: User,
foreignKey: 'creatorId',
attributes: ['name', 'role', 'uid', 'imageUrl']
}
]
})
can someone please find out what i am doing wrong here.
Try setting a name for the associations so Sequelize has a better idea which association to include. You can do something like this to set the names on the associations...
db.Subscription.belongsTo(db.User, {
as: 'creator',
foreignKey: 'creatorId'
});
db.Subscription.belongsTo(db.User, {
as: 'subscriber',
foreignKey: 'subscriberId'
});
Then you can use those names in the query to get the specific association, as so...
Subscription.findAll({
include: {
model: User,
as: 'creator',
attributes: ['name', 'role', 'uid', 'imageUrl']
},
where: {
subscriberId: req.decoded._identer
}
});
When you have associations to the same table more than once setting a name helps the ORM determine which association to load. For the record, for all of the records that get returned you can access that association by accessing the .creator on the instance.
Good luck! :)

Sequelize : How to map a custom attribute in a pivot table

I've got this pivot table, which represents a many to many relationship with the models Person and Movie.
The thing is I want to get the role when I call the movies that get the persons associated. I tried this but it doesn't show the role :
models.Movie.findAll({
include: [{
model: models.Person,
as: 'persons',
through: {attributes: ["role"]}
}]
}).then(function(movies) {
res.json(movies);
});
Do I have to specify something in the models for the role ?
I finally managed to achieve this by creating a model for the pivot table movie_person with the role attribute as a string.
var MoviePerson = sequelize.define("MoviePerson", {
role: DataTypes.STRING
},
{
tableName: 'movie_person',
underscored: true
});
Then in my Movie model I added this
Movie.belongsToMany(models.Person, {
through: models.MoviePerson,
foreignKey: 'movie_id',
as: 'persons'
});
I had to do something obviously similar to this in my Person model and that's it !
For the purpose of those who will need this, there is a new method called 'magic methods'. I believe you have declared your many-to-many asociation
const movies = Movie.findAll();
const person = Person.findbyPk(personId);
const moviesPerson = movies.getPersons(person);

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();