Subquery the same table : Sequelize - mysql

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',
}

Related

Check if an user exist in my database with Node and MySQL [duplicate]

I need to check if entry with specific ID exists in the database using Sequelize in Node.js
function isIdUnique (id) {
db.Profile.count({ where: { id: id } })
.then(count => {
if (count != 0) {
return false;
}
return true;
});
}
I call this function in an if statement but the result is always undefined
if(isIdUnique(id)){...}
I don't prefer using count to check for record existence. Suppose you have similarity for hundred in million records why to count them all if you want just to get boolean value, true if exists false if not?
findOne will get the job done at the first value when there's matching.
const isIdUnique = id =>
db.Profile.findOne({ where: { id} })
.then(token => token !== null)
.then(isUnique => isUnique);
Update: see the answer which suggests using findOne() below. I personally prefer; this answer though describes an alternative approach.
You are not returning from the isIdUnique function:
function isIdUnique (id) {
return db.Profile.count({ where: { id: id } })
.then(count => {
if (count != 0) {
return false;
}
return true;
});
}
isIdUnique(id).then(isUnique => {
if (isUnique) {
// ...
}
});
You can count and find.
Project
.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
})
.then(result => {
console.log(result.count);
console.log(result.rows);
});
Doc link, v5 Beta Release
I found the answer by #alecxe to be unreliable in some instances, so I tweaked the logic:
function isIdUnique (id, done) {
db.Profile.count({ where: { id: id } })
.then(count => {
return (count > 0) ? true : false
});
}
As Sequelize is designed around promises anyway, alecxe's answer probably makes most sense, but for the sake of offering an alternative, you can also pass in a callback:
function isIdUnique (id, done) {
db.Profile.count({ where: { id: id } })
.then(count => {
done(count == 0);
});
}
}
isIdUnique(id, function(isUnique) {
if (isUnique) {
// stuff
}
});
Extending #Jalal's answer, if you're very conscious about performance implications while maintaining a simple Sequelize structure and you do not need the row data, I suggest you only request one column from the database. Why waste bandwidth and time asking the database to return all columns when you won't even use them?
const isIdUnique = id =>
db.Profile.findOne({ where: { id }, attributes: ['id'] })
.then(token => token !== null)
.then(isUnique => isUnique);
The attributes field tells Sequelize to only request the id column from the database and not sending the whole row's content.
Again this may seem a bit excessive but at scale and if you have many columns that hold a lot of data, this could make a giant difference in performance.
Try the below solution. I tried it and it works well.
const isIdUnique = async (id, model) => {
return await model.count({ where: { id: id } });
};
const checkExistId = await isIdUnique(idUser, User);
console.log("checkExistId: ", checkExistId);

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!

sequelize between operator not working properly

I am trying to do query to get all rows between id 1 to 4.
I am using mySQL and sequelize.
query
`
main.test = function(req,res,next)
{
var param = req.params;
model.Time.find({
where:{
id:{
$between:[1,4]
}
},limit: 10
}).then(function (time) {
console.log("Time"+time);
});
}
`
and getting the result when execute
Need a way solve it?

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

Sequelize - case-insensitive like

How can I achieve this in Sequelize?
SELECT * FROM table where lower(column) LIKE ('abcd%');
I can't find a way to mix lower function with $like
You should use Sequelize.Op :
Table.findAll({
where: {
name: {
[Sequelize.Op.iLike]: searchQuery
}
}
})
Don't forget to add % before or after your searchQuery, if you want to make a partial query.
See the docs here
I found the solution:
Table.findAll({
attributes: ['createdAt', 'col'],
where: {
$and:[
{
createdAt:{
$between:[minDate, maxDate]
}
},
Sequelize.where(
Sequelize.fn('lower', Sequelize.col('col')),
{
$like: 'abcd%'
}
)
]
}
});
If you're using PostGres, you can use the $iLike operator to search rows (NOT column names like your question asks).
Sequelize Docs
While it doesn't fully address your question, hopefully it will help someone down the road who searches for case-insensitive + Sequelize, which brought me here.
Table.findAll({
where: {
createdAt: {
$between: [minDate, maxDate]
},
someOtherColumn: {
$like: '%mysearchterm%'
}
}
})
I run into similar problem and solved by
const values = ['adcd'].map(x => x.toLowerCase());
const results = await SomeModel.findAll({
attributes: [
...Object.keys(SomeModel.rawAttributes),
[Sequelize.fn('LOWER', Sequelize.col('someColumn')), 'lower'],
],
having: { lower: values, },
});