Populate data without association id in Sequelize - mysql

I have 4 tables, user, profile , speciality , user_specialities. I am using sequelize. My user model is-->
const User = sequelize.define('user', {
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
IsDeleted: {
type:Sequelize.BOOLEAN,
},
status: {
type:Sequelize.BOOLEAN,
},
});
My profiles model-->
const Profile= sequelize.define('profile', {
profile_id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
first_name: {
type:Sequelize.STRING,
},
last_name: {
type:Sequelize.STRING,
},
},
{
indexes: [
{
unique: true,
fields: ['user_id']
}
]
});
Chef_Info.belongsTo(User, {foreignKey: 'user_id'});
Speciality table-->
const Profile= sequelize.define('speciality', {
speciality_id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
title: {
type:Sequelize.STRING,
},
}
user_specialities hold the many to many relation between user and speciality. I have used belongsToMany association like this -
Speciality.belongsToMany(User, {through: 'user_speciality',foreignKey: 'speciality_id', otherKey: 'user_id'});
User.belongsToMany(Speciality, {through: 'user_speciality',foreignKey: 'user_id', otherKey: 'speciality_id'});
Now, I want to get data where I have user_id, first_name, last_name also the specialities. For example-->
first_name | last_name | specialities
Khabib | Nurmagamedov | Athlete, Wrestler
Tony | Ferguson | Athlete, Striker
I tried in MySql, where query should be-->
SELECT profiles.first_name,profiles.last_name,GROUP_CONCAT(specialities.title)
FROM (
users
LEFT JOIN profiles
ON users.user_id = profiles.user_id
LEFT JOIN user_specialities
ON users.user_id = user_specialities.user_id
LEFT JOIN specialities
ON chef_specialities.speciality_id = specialities.speciality_id
)
WHERE(
AND users.IsDeleted = false
And users.status = true
)
GROUP BY users.user_id;
But I am struggling to convert this to sequelize. I have come this far-->
UserSpecialities.findAll({
attributes:[sequelize.fn('GROUP_CONCAT', sequelize.col('title')), 'speciality_id'],
include: [
{
model: User,
attributes: [],
where:{
IsRemoved: false,
status: true,
},
},
{ model: Speciality,
attributes: [],
},
],
group:['user_id']
Its provides the specialities userwise-->
[
{
"speciality_id": "Athlete, Wrestler",
},
{
"speciality_id": "Athlete, Striker",
}
]
But I am failing to populate the profile data here.If I try to include profile, it shows no association error, or I try to add a hasManyin user model it throws error again. What should I do in this situation?
Thanks In Advance.

Because Profile is associated with Users but not UserSpecialties it would make more sense to accomplish this with two queries.
Grab your user specialties like you did above, then take those userId's and grab their associated profiles (should be something like User.getProfile depending on how your associations are set up).
Right now you have your UserSpecialties attributes set up to only return the specialty_id and title, if you add in userId to those attributes then you can use those userId's to query your profile table. A then promise works well for something like this so something like:
UserSpecialties.findAll({...}).then((userSpecialties) => {
User.getProfile({where: {userId: userSpecialties.userId}, attributes: {'first_name', 'last_name'})
});
Depending on what you are specifically trying to do with the information you will probably have to tweak it a bit, but the general idea of adding in userId as an attribute and then querying your User table for profiles using the userId from the UserSpecialties query result should work fine for the data you want.
Though looking at your models, it might make sense to just have profile and user be one table, where User has first_name, last_name, userId, and status. Since Profile doesn't have a lot of other stuff to it, and then if you need specific things for the userProfile you can use scopes on your user model.

Related

How do I join two tables using Sequelize?

I have 3 tables: Books Authors and AuthorBooks. The last one to solve many-to-many. So far i managed to get the tables to work and display as i expected to. Now i want to display a table that shows data from both tables: Books and Authors. BooksAuthors stores only the IDs of the other tables.
I want something like this to work with sequelize:
SELECT ab.idA, ab.idB, a.firstName, a.lastName, b.title, b.description
from authorbooks ab
left join authors a on ab.idA=a.idA
left join books b on ab.idB=b.idB;
This is how i tried to define the relationship. I think I'm missing something here.
db.models.Books = require("../models/books")(sequelize);
db.models.Authors = require("../models/authors")(sequelize);
db.models.AuthorBooks = require("../models/authorbooks")(sequelize);
db.models.Authors.belongsToMany(db.models.Books,{
through: db.models.AuthorBooks,
foreignKey:{ name: 'idA',
allowNull:true
}
})
db.models.Books.belongsToMany(db.models.Authors,{
through: db.models.AuthorBooks,
foreignKey:{ name: 'idB',
allowNull: true
}
});
module.exports = db;
This is how i defined the tables. BOOKS:
module.exports = (sequelize) => {
class Books extends Sequelize.Model {}
Books = sequelize.define('Books', {
idB:{
type: Sequelize.INTEGER,
allowNull: false,
primaryKey : true
},
title: {
type: Sequelize.STRING
},
description:{
type: Sequelize.STRING(6000)
},
});
return Books;
};
AUTHORS:
module.exports = (sequelize) => {
class Authors extends Sequelize.Model {}
Authors = sequelize.define('Authors', {
idA:{
type: Sequelize.INTEGER,
allowNull: false,
primaryKey : true
},
firstName: {
type: Sequelize.STRING
},
lastName:{
type: Sequelize.STRING
},
});
return Authors;
};
AUTHORBOOKS:
module.exports = (sequelize) => {
class AuthorBooks extends Sequelize.Model {}
AuthorBooks.init({
id: {
type: Sequelize.INTEGER,
primaryKey:true,
autoIncrement: true
}
},{sequelize});
return AuthorBooks;
}
I think you can use the "include" option to achieve this function.
For example, you can use the code like
Book.findAll({
include:[{model:Author}]
})
This option will add an "Author" field including the author information to the result.
What you need is a through options in the query, it combines all the authors linked to a particular book, empty attributes will avoid adding any attribute from mapping table which in your case is 'AuthorBooks'.
Book.findAll({
include:[{
model:Author,
through: {
attributes: []
}
}]
})

SequelizeEagerLoadingError while include: [{model: , as: ' ' }]

I want to run this function. I want that the included model: SurveyResult getting an alias.
But i get this error: SequelizeEagerLoadingError: SurveyResult is associated to User using an alias. You've included an alias (Geburtsdatum), but it does not match the alias defined in your association.
const mediImport = await User.findAll({
where: { Id: 1 },
// Select forename as Vorname, name as Nachname
attributes: [['forename', 'Vorname'], ['name', 'Nachname']],
include: [{
model: SurveyResult,
as: 'Geburtsdatum'
}]
})
I know that it is a Problem with my associates, but i cant find the problem
Here are my models.
Model: User
User.associate = function (models) {
User.hasOne(models.Admin)
User.hasOne(models.UserStatus)
User.hasOne(models.SurveyResult, {
})
Model SurveyResult
SurveyResult.associate = function (models) {
SurveyResult.hasOne(models.Survey)
User.hasOne(models.SurveyResult, {})
You need to define the alias on the association level also , like this :
User.hasOne(models.SurveyResult,{ as : 'Geburtsdatum' });

Join table on Sequelize CLI

I am creating an application, and I would like to have more information
about creating a join table.
I have a table media:
var Media = sequelize.define('Media', {
m_title: DataTypes.STRING(50),
m_duration: DataTypes.INTEGER,
m_year: DataTypes.DATEONLY,
m_country: DataTypes.STRING(50),
m_season: DataTypes.INTEGER,
m_order: DataTypes.INTEGER,
m_fk_id_type_media: DataTypes.INTEGER
},
And Celebrity:
var Celebrity = sequelize.define('Celebrity', {
c_lastname: DataTypes.STRING(50),
c_firstname: DataTypes.STRING(50),
c_birth_date: DataTypes.DATEONLY,
c_nationality: DataTypes.STRING(50)
},
Do I have to create the model myself (media_has_celebrities)? Because I tried to add
Media.associate = function (models) {
models.Media.belongsToMany(models.Celebrity, {through: 'MediaHasCelebrity'});
};
and
Celebrity.associate = function (models) {
models.Celebrity.belongsToMany(models.Media, {through: 'MediaHasCelebrity'});
};
but it does not create the model of join as indicated in the documentation?
Do you have to define foreign keys before?
The goal would be to do something like Media.addCelebrity ...
Sorry for my english !
Thank you

Sails js sort by populated field

I need to sort data from a MySQL database on related table row.
Suppose we have two models:
ModelOne:
module.exports = {
tableName: 'modelOne',
attributes: {
// some atributes.........
modelTwo{
model: 'ModelTwo',
columnName: 'model_two_id'
}
}
}
ModelTwo:
module.exports = {
tableName: 'modelTwo',
attributes: {
// some atributes.........
model_two_id: {
type: 'integer',
autoIncrement: true,
primaryKey: true
},
name: 'string'
}
}
I would like to do something like:
ModelOne
.find(find_criteria)
.populateAll()
.paginate({page: page, limit: limit})
.sort('modelTwo.name')
.then(...)
Is there possibility to do this or I need to write an SQL query without using Waterline functions?
This is how I do it...
ModelOne
.find(find_criteria)
.populate('modelTwo', {sort: 'name ASC'})
.paginate({page: page, limit: limit})
.then(...)
As you can see, you can pass {sort} object to populate method.
No. Deep-population-sorting is on future list https://github.com/balderdashy/waterline/issues/266

Populating one to many relationship in sails js without using primary key

Every example code of one to many in sails/waterline documentation assumes the primary key is the association between two models (I think).
http://sailsjs.org/documentation/concepts/models-and-orm/associations/one-to-many
However i have models that have a referral column that references some other values similar to
User
{
id (primary): <int>
email: <string>
}
recordings
{
id (primary): <int>
email: <that email from user>
}
atm im trying
userModel.js
{
attributes: {
email: {
type: 'string',
size: 255,
unique: true
},
},
queries: {
columnName: 'email',
model: 'recordings'
}
.....
}
}
recordingsModel.js
{
attributes: {
email: {
type: 'string',
size: 255,
},
},
queries: {
model: 'user'
}
.....
}
}
and in the Controller
sails.models.user
.find()
.populate('queries')
.exec(function (err, results) {
});
But i get the error
: ER_BAD_FIELD_ERROR: Unknown column '__queries.queries' in 'field list'
Does anyone have a good tutorial for one to many relationships in waterline because the documentation on there site is pretty bad so i feel im just not understanding how to design the models.
From what I can gather, what you want is to be able to set up your userModel and recordingsModel models such that, by giving a recording a certain value for email, it will automatically be associated with any users that share that email. This is not something that the Waterline (the Sails ORM) supports.
Instead, your best option is to set the models up as indicated in the documentation:
// userModel.js
{
attributes: {
email: {
type: 'string',
size: 255,
unique: true
},
},
recordings: {
collection: 'recordingsModel',
via: 'user'
}
.....
}
}
// recordingsModel.js
{
attributes: {
email: {
type: 'string',
size: 255,
},
},
user: {
model: 'userModel'
}
}
}
My guess is that you're trying to avoid having to look up a user ID in order to associate a new recording with it. You can do this if you make email the primary key of the userModel; then when you create a new recording, you can set its user to an email address and voila: the two are linked.