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

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' });

Related

Sequelize multiple joins for fetching data

I am working on a CRUD application with Sequelize and ExpressJS that has the following tables:
Parents
Students
ParentStudents
id
id
id
name
name
idParent
idStudent
I want to query the Parents table and have students key created by a left join between students and ParentStudents on idParent;
I want to get data in the following way:
{
"data":[
{
"name":"nameParent2",
"students":[
{
"name":"Student1"
},
{
"name":"Student2"
}
]
},
{
"name":"nameParent2",
"students":[
{
"name":"Student1"
},
{
"name":"Student2"
}
]
}
]
}
I tried something like this, but is wrong and giving data from ParentStudents:
models.parents.findAll({
include: [{
model: models.parentStudents
}]
}).then(data => {
res.json({
message: "Hello from server!!!",
data: data
});
});
Like this is failing to make the association between Students and ParentStudents. (students is not associated to parentStudents!)
models.parents.findAll({
include: [{
model: models.parentStudents,
include: [{
model: models.students
}]
}]
}).then(data => {
res.json({
message: "Hello from server!!!",
data: data
});
});
My relationships are done like this:
db.parents.hasMany(db.parentStudents);
db.students.hasMany(db.parentStudents);
I also tried Many-to-Many, but still doesn't work:
db.students.belongsToMany(db.parents, { through: db.parentStudents });
db.parents.belongsToMany(db.students, { through: db.parentStudents });
Does someone know how this can be done?
One workaround is:
let parents = await models.parents.findAll();
for (let parent of parents) {
let idParent = parent.dataValues.id;
let students = await models.sequelize.query(`
SELECT * FROM students s
LEFT JOIN parentStudents ps
ON s.id = ps.studentId
WHERE ps.parentId =${idParent};
`);
parent.dataValues['students'] = students;
}

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: []
}
}]
})

How to use having clause on relationship with sequelize node

I have 2 models, business and documents in a 1:n relationship, i want to filter the business in two ways,
business that has documents where every document.due_balance is greater than 0
business that has documents where every document.due_balance is equals to 0
I want to make something like this
select
A.name, B.due_balance, sum(B.due_balance) as total_due_balance
from
business A
inner join documents B ON A.id = B.business_id
group by A.id
having total_due_balance > 0;
select
A.name, B.due_balance, sum(B.due_balance) as total_due_balance
from
business A
inner join documents B ON A.id = B.business_id
group by A.id
having total_due_balance = 0;
these will get me what i want, the problem, is that the previus code was made with sequelize ORM, and i can't change it, something like this
const businesses = await db.business.paginate({
attributes: [
...
],
where: {
... //bunch of where
},
page: parseInt(params.page, 10) || 1,
paginate: parseInt(params.limit, 10) || 10,
});
here is where the problem begins, i don't know how to join the tables and use the having to filter it, i have tried addind this
let toInclude;
if (params.contactability === 'with_balance') {
toInclude = {
include : [
{
attributes: [
[db.Sequelize.fn('sum', db.Sequelize.col('due_balance')), 'total_due_balance'],
],
model: db.document,
as: 'documents',
having: db.Sequelize.where(db.Sequelize.fn('sum', db.Sequelize.col('due_balance')), {
$gt: 0,
}),
},
],
};
} else if(params.contactability === 'without_balance') {
toInclude = {
include : [
{
attributes: [
[db.Sequelize.fn('sum', db.Sequelize.col('due_balance')), 'total_due_balance'],
],
model: db.document,
as: 'documents',
having: db.Sequelize.where(db.Sequelize.fn('sum', db.Sequelize.col('due_balance')), {
$eq: 0,
}),
},
],
};
} else {
toInclude = {};
}
const businesses = await db.business.paginate({
attributes: [
...
],
where: {
...
},
...toInclude,
page: parseInt(params.page, 10) || 1,
paginate: parseInt(params.limit, 10) || 10,
});
but that does not work at all, how can i solve this problem?
I don't think HAVING will work without GROUP.
I would move the having clause outside the include section and use the AS aliases.
So, roughly:
group: ['id'], // and whatever else you need
having : { 'documents.total_balance_due' : {$eq : 0 }}
(Making some guesses vis the aliases)
To filter the date from joined table which uses groupby as well, you can make use of HAVING Property, which is accepted by Sequelize.
So with respect to your question, I am providing the answer.
You can make use of this code:
const Sequelize = require('sequelize');
let searchQuery = {
attributes: {
// include everything from business table and total_due_balance as well
include: [[Sequelize.fn('SUM', Sequelize.col('documents.due_balance')), 'total_due_balance']]
},
include: [
{
model: Documents, // table, which you require from your defined model
as: 'documents', // alias through which it is defined to join in hasMany or belongsTo Associations
required: true, // make inner join
attributes: [] // select nothing from Documents table, if you want to select you can pass column name as a string to array
}
],
group: ['business.id'], // Business is a table
having: ''
};
if (params.contactability === 'with_balance') {
searchQuery.having = Sequelize.literal(`total_due_balance > 0`);
} else if (params.contactability === 'without_balance') {
searchQuery.having = Sequelize.literal(`total_due_balance = 0`);
}
Business // table, which you require from your defined model
.findAll(searchQuery)
.then(result => {
console.log(result);
})
.catch(err => {
console.log(err);
});
Note : Change model name or attributes according to your requirement.
Hope this will help you or somebody else!

BookShelf orm MySQL how to select column1-column2 as alias

In a raw MySQL query, I have something like this:
Select total_sales - over_head_costs As net_sales from departments;
How can I realize the same thing with BookShelf /knex query? Ideally not using knex.raw.
My attempt involves following:
let Department = bookshelf.Model.extend({
tableName: 'departments',
idAttribute: 'department_id',
},{
getDepartments: function(){
return this.fetchAll({columns: ['department_id', 'department_name', 'over_head_costs', 'total_sales - over_head_costs AS net_sales']})
.then(models=>models.toJSON());
},
});
Bookshelf does not have this feature but it brings a plugin for that: Virtuals
. No need to install anything, you just load it right after loading Bookshelf using bookshelf.plugin('virtuals').
Your model should then look like:
const Department = bookshelf.Model.extend({
tableName: 'departments',
idAttribute: 'department_id',
virtuals: {
net_sales: function() {
return this.get('total_sales') - this.get('over_head_costs');
}
}
},{
getDepartments: function(){
return this.fetchAll({columns: ['department_id', 'department_name', 'over_head_costs', 'net_sales']})
.then(models=>models.toJSON());
},
});

Sails / WaterLine ORM / Has Many Through: Insert data into join table

I'm newbie with Sails/WaterLine ORM
I'm following http://sailsjs.org/documentation/concepts/models-and-orm/associations/through-associations
One question.
How way to insert data into a join table ?
For example: User m - m Pet
User model
module.exports = {
attributes: {
name: {
type: 'string'
},
pets:{
collection: 'pet',
via: 'owner',
through: 'petuser'
}
}
Pet model
module.exports = {
attributes: {
name: {
type: 'string'
},
color: {
type: 'string'
},
owners:{
collection: 'user',
via: 'pet',
through: 'petuser'
}
}
PetUser model (join table)
module.exports = {
attributes: {
owner:{
model:'user'
},
pet: {
model: 'pet'
}
}
}
Pet data is available (some record with ID1, ID2, ID3...)
I want to add new one user with some pets
PetUser ( id , id_of_user, id_of_pet)
1, U1, P1
2, U1, P2
{
"name" : "John",
"pets" : [2,3]
}
UserController
module.exports = {
addUserWithPets: function(req, res) {
User.create(req.body).exec(function(err, user) {
if(err){
throw err;
}else {
/*pets.forEach(function(pet, index){
user.pets.add(pet);
})
user.save(function(err) {});*/
user.pets.add(data);
user.save(function(err) {});
}
return res.ok({
data: user
});
})
}
};
Thanks!
I think this hasn't been implemented yet in sails.
Refer to this question: through associations in sails.js on SO.
Here is what waterline docs say:
Many-to-Many through associations behave the same way as many-to-many associations with the exception of the join table being automatically created for you. This allows you to attach additional attributes onto the relationship inside of the join table.
Coming Soon