How to make if condition in Nodejs Sequelize model query - node.js

I'm trying to use if condition in Nodejs Sequelize query using models.
I want to select users by filter parameter if there is filter parameter. If there isn't I want to select all users.
I did that in the following way, but I hardcoded this:
var filterData = { id: { $ne: null} };
Is there a proper way to do that using Sequelize? Thanks.
function getData(id, filter='') {
var filterData = { id: { $ne: null} };
if (filter !== '') {
filterData = {
$or: [
{firstName: {$like: '%' + filter + '%'}},
{lastName: {$like: '%' + filter + '%'}}
]
};
}
return models['Profile'].findAll({
where: { profileId: id },
include: [{
model: models.Profile.scope('essentials'),
where: filterData,
as: 'entity'
}]
})
}

I assume you've tried var filterData = {} or var filterData = true and that didn't work? If so, one other option would be to build your include object beforehand, and then add the where clause to it conditionally:
function getData(id, filter='') {
var includeObj = {
model: models.Profile.scope('essentials'),
as: 'entity'
}
if (filter !== '') {
includeObj.where = {
$or: [
{firstName: {$like: '%' + filter + '%'}},
{lastName: {$like: '%' + filter + '%'}}
]
};
}
return models['Profile'].findAll({
where: { profileId: id },
include: [includeObj]
})
}
That avoids the awkward "where ID not equal to null" condition.
Also a small side note (and maybe you already know this and just prefer the more explicit code) but since empty string is falsey in javascript,
if (filter !== '') {
can be replaced with just
if (filter) {

where: {
[Op.and]: [
req.query.gigType && { gigType: req.query.gigType },
],
},
You can just use it like this. This is not the answer to your question. But you can get an idea of how to write this. if there is req.query.gigType variable, giType = req.query.gigType filter applied. If not, the filter doesn't apply.

Related

Sequelize Complex Filter | Node.js

i want to filter, but that depend to the user, for example
Data.findAll({
where: {
name: {
[Op.or]: [
{ [Op.like]: ['%samsung%'] },
{ [Op.like]: ['%iphone%'] },
{ [Op.like]: ['%alcatel%']}
]
}
}
}
If the user selects only Samsung, how do I go about filtering only by Samsung?
Assuming req.query.brands stores either a single search string or an array of strings we can build Op.or conditions on the fly:
const brands = [].concat(req.query.brands)
const brandConditions = brands.map(x => ({
[Op.like]: `%${x}%`
})
const foundItems = await Data.findAll({
where: {
name: {
[Op.or]: brandConditions
}
}
}

best way to execute dynamic mongoDB queries in Nodejs

i have a variable that can either be an array of string or be an empty array.
const userHospitalQuery = [ req.query.hospital || [] ].flat();
when i filter with is as an array of strings, it works fine. Im running into a problem when i try to run my mongo search with the variable not having any values or empty (no Query pass in the url), how do I make mongo skip or ignore it as part of the filters? so only my other 2 filters are applied to the search. i know i cant use if statements inside my query object so im not sure what to do
const userHospitalQuery = [req.query.hospital || []].flat();
const filter = {
"staff.hospital": { $in: userHospitalQuery }, // How do i ignore/skip this if no query is passed in the URL
organisation: { $eq: "Test" },
isDeleted: { $eq: false },
};
const staff = await User.find(filter).sort({ dateAdded: -1 });
Just modify the filter object with a regular if.
const userHospitalQuery = [req.query.hospital || []].flat();
const filter = {
organisation: { $eq: "Test" },
isDeleted: { $eq: false },
};
if (userHospitalQuery.length) {
filter["staff.hospital"] = { $in: userHospitalQuery };
}
const staff = await User.find(filter).sort({ dateAdded: -1 });

NodeJS Mongoose wont accept string variable in $regex

I got one record with admin.name = "FooBar"
And I want to do a findOne on the name but case insensitive.
In Studio3T this works fine...
db.admin.findOne(
{ name: { $regex: /^FoObAr/i } }
)
But in nodeJS I cant seem to get a variable containing the regexp into the find statement...
Let ID = "FoObAr"
return resolve( adminModel.findOne( { "name" : { $regex: "/^" + ID + "$/i"} } ) );
This one results in... (which gives 0 results)
Mongoose: admin.findOne({ name: { '$regex': '/^FoObAr$/i' } }, { projection: {} })
I also tried
Let ID = "FoObAr"
return resolve( adminModel.findOne( { "name" : { $regex: /^ ID $/i } ) );
This one results in... (which gives 0 results)
Mongoose: admin.findOne({ name: { { $regex: /^ ID $/i } }, { projection: {} })
Any ideas?
Please use RegExp function
admin.find({name:new RegExp(['^',ID, '$'].join(''), 'i')}).projection({})
You should pass RegExp object, not string.
adminModel.findOne({ "name" : { $regex: new RegExp(/^FoObAr/, 'i') } })
With variable ID it will look like
adminModel.findOne({ "name" : { $regex: new RegExp(`^${ID}$`, 'i') } })

Why my conditional WHERE inside the OR operator in the Sequelize query is converted to AND?

I'm trying to make a query with Sequelize with a conditional WHERE like explained here (How to perform a search with conditional where parameters using Sequelize).
The relevant part of my code is like this
const Op = Sequelize.Op;
var search = {};
if (typeof req.query.search !== 'undefined'){
search.nome = {[Op.like]: '%' + req.query.search + '%'};
search.username = {[Op.like]: '%' + req.query.search + '%'};
}
model.User.findAll({
where:{
[Op.or]: [
search
]
})
It works, but the generated SQL adds an AND instead of an OR, like this:
SELECT 'id_', 'nome', 'username', 'id' FROM 'User' AS 'User' WHERE (('User'.'nome' LIKE '%test%' AND 'User'.'username' LIKE '%test%'))
Am I doing something wrong that I fail to see?
I've already tried several combinations of this and none works.
There is something wrong in the constructed search filter. [Op.or] should be the spread of search not an array.
Try as below,
model.User.findAll({
where: {
[Op.or]: {
email: {
[Op.like]: 'abcd',
},
username: {
[Op.like]: 'cdf',
},
}
},
logging: console.log,
});
you will get the below,
SELECT "id", "name", "username" FROM "users" AS "User" WHERE "User"."deleted_at" IS NULL AND ("User"."email" LIKE 'abcd' OR "User"."username" LIKE 'cdf');
try to use spread
model.User.findAll({
where: {
[Op.or]: [
{...search}
]
}
})
what about this?
const where = {}
if (typeof req.query.search !== 'undefined'){
where[Op.or] = {
nome : {
[Op.like] : `%${req.query.search}%`
},
username : {
[Op.like] : `%${req.query.search}%`
},
}
}
model.User.findAll({
where
})
Here's some code I'm using:
return Models.job.findAndCountAll({
raw: true,
where: {
isActive: 1,
...((args.statusOfJob > 0) && {
status: args.statusOfJob
})
}
})
In this case, statusOfJob is a numerical value used in a select object, where 0 would show all items.
It's a bit cryptic, but it's also concise and reduces the need for extraneous conditional statements and assignments.

findByIdAndUpdate With Multiple Subdocuments

So I'm working with NodeJS and MongoDB, and I'm making an endpoint that lets clients update their user profiles with several optional data fields. So, one of the update queries can look like this:
{
name: { givenName: 'first' },
about: 'whatever',
auth: { password: 'hashedPW' }
}
The Mongoose API docs state the following info about findByIdAndUpdate: All top level update keys which are not atomic operation names are treated as set operations.
So the top level key, about, works fine to update. However, the nested keys, name and auth are overwritten by the update values, rather than just having the values set.
Now I could go through and manually change each of the fields to be a $set key, but there are a lot of different fields, so to do this would be pretty annoying. Is there an easy way to apply the $set rule to the subdocuments as well? i.e. transform the statement into this, with a Mongoose option or something:
{
$set : { name: { givenName: 'first' } },
$set : { about: 'whatever' },
$set : { auth: { password: 'hashedPW' } }
}
You basically need to tranform your input object into "dot notation" form in order to avoid overwring other possible sub-keys in your update. This is quite simple really:
var obj = {
name: { givenName: 'first' },
about: 'whatever',
auth: { password: 'hashedPW' }
};
var target = {};
function dotNotate(obj,prefix) {
prefix = (typeof(prefix) === 'undefined') ? "" : prefix;
Object.keys(obj).forEach(function(key) {
if ( typeof(obj[key]) === "object" ) {
dotNotate(obj[key],key + ".")
} else {
target[prefix + key] = obj[key];
}
});
}
dotNotate(obj);
Now the target object looks like this:
{
"name.givenName" : "first",
"about" : "whatever",
"auth.password" : "hashedPW"
}
So the update block of your statement is merely written as:
{ "$set": target }
For reference, the dotNotate() function can be a bit more refined and self contained. Also including shorter default assignments as valid input would generally be considered be "truthy". Also the "prefix" should have been pre-pended on each call to make this work at arbitrary depth:
function dotNotate(obj,target,prefix) {
target = target || {},
prefix = prefix || "";
Object.keys(obj).forEach(function(key) {
if ( typeof(obj[key]) === "object" ) {
dotNotate(obj[key],target,prefix + key + ".");
} else {
return target[prefix + key] = obj[key];
}
});
return target;
}
Then you can use either inline:
var update = { "$set": dotNotate(obj) };
Or pass in a defined object like this if you prefer:
var update = { "$set": {} };
dotNotate(obj,update["$set"]);
With the same results.
Also fine for arrays and nested depth:
{
"things" : [
{
"a" : 1,
"b" : 2
},
{
"a" : 3,
"b" : 4
}
],
"bool" : false
}
With output:
{
"things.0.a" : 1,
"things.0.b" : 2,
"things.1.a" : 3,
"things.1.b" : 4,
"bool" : false
}

Resources