BookShelf orm MySQL how to select column1-column2 as alias - mysql

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

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 can I query in Bookshelf/Knex on a BelongsToMany join table?

I am running Bookshelf over MySQL in a Node application.
I have a model called Document and another called Tag, which are joined through a belongsToMany relationship via a table called "map_tag_document".
Document:
'use strict';
const bookshelf = require('../bootstrap/bookshelf_instance').bookshelf;
const Tag = require('./tag').model;
const Document = bookshelf.Model.extend({
tableName: 'document',
tags() {
return this.belongsToMany(Tag, 'map_tag_document', 'document_id', 'tag_id')
}
},
{
jsonColumns: ['data']
}
);
module.exports.model = Document;
Tag:
'use strict';
const bookshelf = require('../bootstrap/bookshelf_instance').bookshelf;
const Tag = bookshelf.Model.extend({
tableName: 'tag'
});
module.exports.model = Tag;
Tags have a "name" column.
How do I query documents based on a search string appearing in the names of the tags associated with them?
Currently, I'm querying like this:
await new Document()
.query((qb) => {
if (searchString)
qb.whereRaw(`(data->'$.description' LIKE "%${searchString}%" OR name LIKE "%${searchString}%")`)
})
.fetch({
withRelated: ['tags']
});
What's the proper syntax for querying on the joined table?
Figured it out-can do joins on the Knex querybuilder object, like this:
const documents = await new Document()
.query((qb) => {
qb.join('map_tag_document', 'document.id', '=', 'map_tag_document.document_id')
qb.join('tag', 'tag.id', '=', 'map_tag_document.tag_id')
if (searchString)
qb.andWhereRaw(`(document.data->'$.description' LIKE "%${searchString}%" OR document.name LIKE "%${searchString}%" OR tag.name LIKE "%${searchString}%")`)
})
.fetch({
withRelated: ['tags']
});

Using a raw SQL query with Sequelize ORM and literal

Using the Sequelize ORM I am trying to update the field level_id where this field has a foreign key to the field Level in another table called level_tbl.
select * from level_tbl;
+----------+----------+
| level_id | Level |
+----------+----------+
| 1 | Higher |
| 2 | Ordinary |
+----------+----------+
My update task looks like this, and as you can see I am trying to get a raw sql query to work as a literal with Sequelize.
//Update task
router.put("/task/:id", (req, res) => {
if (!req.body) {
res.status(400)
res.json({
error: "Bad Data....!"
})
} else {
Task.update({
Level: req.body.Level,
Level_id: [sequelize.literal("SELECT level_id FROM level_tbl WHERE Level = 'Ordinary'")],
Year: req.body.Year,
Question: req.body.Question,
Answer: req.body.Answer,
Topic: req.body.Topic,
Sub_topic: req.body.Sub_topic,
Question_type: req.body.Question_type,
Marks: req.body.Marks,
Question_number: req.body.Question_number,
Part: req.body.Part,
Sub_part: req.body.Sub_part
}, {
where: {
id: req.params.id
}
})
.then(() => {
res.send("Task Updated")
})
.error(err => res.send(err))
}
})
What would be the correct syntax for this line?
Level_id: [sequelize.literal("SELECT level_id FROM level_tbl WHERE Level = 'Ordinary'")],
The issue is that I already have imported a model and have access to the global Sequelize instance. Therefore example in the documentation don't apply this way, i.e.,
order: sequelize.literal('max(age) DESC')
From https://sequelize.org/master/manual/querying.html
and also,
https://github.com/sequelize/sequelize/issues/9410#issuecomment-387141567
My Task.js where the model is defined is as follows,
const Sequelize = require("sequelize")
const db = require("../database/db.js")
module.exports = db.sequelize.define(
"physics_tbls", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
Level: {
type: Sequelize.STRING
},
Level_id: {
type: Sequelize.INTEGER
},
Year: {
type: Sequelize.INTEGER
},
.........
}, {
timestamps: false
}
)
I am using a MEVN stack -> MySQL, Express.js, Vue.js and Node.js
Any help would be greatly appreciated,
Thanks,
I needed to require Sequelize again in tasks.js, the file the defines the express routes. It wasn't enough just to require Task.js although Task.js does itself require sequelize.
const Sequelize = require('sequelize')
var express = require("express")
var router = express.Router()
const Task = require("../model/Task")
Also brackets needed around the query and inside the double quotes,
Level_id: Sequelize.literal("(SELECT level_id FROM level_tbl WHERE Level = 'Higher')"),
i'm using sequelize 6.3 and raw query on where is no longer supported, i'm using this syntax :
where: sequelize.where(sequelize.col("table.column"), "=", "yourvalue")
and it worked

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

Subquery the same table : Sequelize

I have got a scenario where I would want the below query executed using sequelize.
select * from master where catg_level = 1 and obj_id in (select obj_id from master where catg_level = 2) order by position;
I've the below code written in sequelize.
Master.all({
where: {catg_level: '1'},
order: 'position ASC',
include: [{
model: Master,
as: 'sub-menu',
where: {catg_level: '2'}
}]
})
.then(function(a){
try {
console.log(JSON.stringify(a));
} catch (e) {
console.log(e);
}
});
The SQL generated this
The condition catg_level = 2 is added to the main query instead of being added as a subquery. I understand this is the actual functioning. But is there a workaround to get this done? Please advise.
Thanks in advance.
You can use sequelize.literal:
{
where: {
catg_level: '1',
obj_id:{
in:[sequelize.literal('(select obj_id from master where catg_level = 2)')]
}
},
order: 'position ASC',
}