sequelize dynamic query params - node.js

I'm sending query params as JSON format in req.query.p from my front-end MVC framework , the point is that this could be a dynamic key and value, for example:
req.query.p = {nombre : 'juan'}
or
req.query.p = {pais : 'chile'}
So I need the key, and the value to put them in the where statement, something like this
exports.select = function(req, res){
console.log('=> GET | Obtener peliculas'.bold.get);
db.Pelicula
.findAndCountAll({
limit : req.query.limit,
offset : req.query.offset,
where : req.query.p ? [req.query.p.KEY + " = ?", req.query.p.VAL] : null
})
.success(function(resp){
console.log(JSON.stringify(resp.rows, null, 4).bold.get);
res.json({peliculas : resp.rows, meta : { total : resp.count}});
});
}

The where parameter can be an object, so you can just pass where: req.query.p

Usually I put the entire object, so if it comes empty, it will work normally as if there is no conditional WHERE.
You don't need to add {} in the where, because the object that comes from req.query already has it.
const filter = req.query;
example= await ModelExample.findAndCountAll({
where:
filter
})

With ES6 and with usage of the dynamic properties I'll do it like this
const { Op } = require("sequelize");
const from = new Date()
// const to = new Date().setMinutes(40)
const to = null
let where = {
timestamp: {
[Op.or]: {}
}
}
if (from) {
where.timestamp[Op.or][Op.gte] = new Date(from)
}
if (to) {
where.timestamp[Op.or][Op.lte] = new Date(to)
}
console.log(where);
Model.find({ where })

Related

how to ignore null or blank value while writing to JSON using NodeJS

I'm writing below content to JSON file, I would like to ignore the fields which has value null or blank - in this case I want to ignore productPrice and productRating fields while writing to JSON file, I'm not much familiar with NodeJS - can someone please help how can I achieve this in NodeJS?
Please find my code below:
const fs = require('fs');
const params = {
productID: 'prd323434',
productName: 'Google',
productDesc: 'Larum ipsum',
productPrice: null,
productRating: '',
productReview: 'Lorum ipsum'
};
var data = {
productID: params.productID,
productName: params.productName,
productDesc: params.productDesc,
productPrice: params.productPrice,
productRating: params.productRating,
productReview: params.productReview
};
let jsonContent = JSON.stringify(data);
fs.writeFileSync('test.json', jsonContent);
console.log(jsonContent)
You look like you are just filtering your object for "falsy" values.
So take params, use Object.entries on it to get something like this:
[
[productID, 'prd323434'],
[productName, 'Google'],
...
]
Use the filter method on the result, destructure each param with ([k,v]). Then only return it if v is "truthy".
const data = Object.fromEntries(Object.entries(params).filter(([k,v]) => v))
Or perhaps in a more readable way:
const entries = Object.entries(params)
const filtered = entries.filter(([k,v]) => {
if (v === null || v === "") {
return false
} else {
return true
}
}))
const data = Object.fromEntries(filtered)

how to use orderByChild with startAfter in Realtime Database using Node.js

I am trying to sort orders in descending and start after on particular key but its not working
nextAfter : -Mk4-n5BnVpwhum62n2g or any Key / _id
db record:
{
'-Mk4-n5BnVpwhum62n2g': {
_id: '-Mk4-n5BnVpwhum62n2g',
createdAt: -1632171667626,
name: 'abc'
},
'-Mk40Ko9DbSeMdjIpY4': {
_id: '-Mk40Ko9DbSeMdjIpY4',
createdAt: -1632171809831,
name: 'new '
}
}
trying query :
query = dbRef.orderByChild('createdAt').startAfter(nextAfter).limitToFirst(limit);
The startAfter() method accepts two parameters - the first is the relevant orderBy value and the second is the optional key of the last entry (for when multiple entries have the same value for the orderBy criteria). So to correctly paginate the reference, you need to pass the previous entry's createdAt value and its key.
const baseQuery = dbRef
.orderByChild('createdAt')
.limitToFirst(limit);
let pageCount = 0, lastChildOnPage = undefined;
const children = [];
while (true) {
const pageQuery = pageCount === 0
? baseQuery
: baseQuery
.startAfter(lastChildOnPage.createdAt, lastChildOnPage.key);
const pageSnapshot = await pageQuery.once('value');
pageSnapshot.forEach((childSnapshot) => {
children.push({ key: childSnapshot.key, ...childSnapshot.val() });
})
const newLastChildOnPage = children[children.length-1];
if (lastChildOnPage !== newLastChildOnPage) {
lastChildOnPage = newLastChildOnPage;
pageCount++;
} else {
break; // no more data
}
}
console.log(`Grabbed ${pageCount} page(s) of data, retrieving ${children.length} children`);

Find value inside a collection in mongoose from nested json

Suppose I have data stored in collection Teacher as:
{
"name":"john",
"age":45,
"class_access":{
1234:"head",
1235:"head
},
"_id" : ObjectId("12312312"),
}
{
"name":"henry",
"age":55,
"class_access":{
1234:"head",
},
"_id" : ObjectId("sdf9991"),
}
{
"name":"travis",
"age":35,
"class_access":{
2341:"head",
},
"_id" : ObjectId("sas21"),
}
I want to find all the information of all the teachers belonging to class 1234.
For this I tried:
const TeacherDetails = await Teacher.find({ class_access: {1234:"head"} })
But it doesn't return anything.
So how can I access nested json to get all the details?
If any one needs any further information do let me know.
As per solution provided by Mr. Arif
const TeacherDetails = await Teacher.find({ "class_access.1234": "head" });
Support class value is not constant say I'm getting it from variable
const className = 1234
Now if I try, fetching className, it gives syntax error, I tried following syntax error for all of them
const TeacherDetails = await Teacher.find({ class_access.className: "head" });
const TeacherDetails = await Teacher.find({ class_access[className]: "head" });
const TeacherDetails = await Teacher.find({ 'class_access.+'${className}': "head" });
So how can we do it dynamically?
Since class_access represent object which may have multiple keys, you should try like
You can use [variableName] as key like following
method 1: Using string template
const className = 1234
const TeacherDetails = await Teacher.find({ [`class_access.${className }`]: "head" });
method 2: Using string concatenation
const className = 1234
const classAccess = 'class_access.' + className;
const TeacherDetails = await Teacher.find({ [classAccess] : "head" });

How to use MongoDB $ne on nested object property

I have a node API which connects to a mongoDB through mongoose. I am creating an advanced results middleware that enabled selecting, filtering, sorting, pagination etc. based on a Brad Traversy course Node.js API Masterclass With Express & MongoDB. This is all good.
I am adapting the code from the course to be able to use the $ne (not equal) operator and I want to be able to get a model that is not equal to a nested property (user id) of the model. I am using this for an explore feature to see a list of things, but I don't want to show the user their own things. I am having trouble figuring out how to access the id property.
********************* UPDATE *********************
It seems all the documentation I've read recommends writing const injected like this:
const injected = {
'user._id': { "$ne": req.user.id }
};
but for some reason it is not working. I can query top level properties that are just a plain string value like this:
const injected = {
access: { "$ne": "public" }
};
but not a property on an object. Does anyone know why? Is it because the property I want to query is an id? I've also tried:
const injected = {
'user._id': { "$ne": mongoose.Types.ObjectId(req.user.id) }
};
which also does not work...
So the model looks like this:
{
name: 'Awesome post',
access: 'public',
user: {
_id: '2425635463456241345', // property I want to access
}
}
then the actual advanced results middleware looks like this and it's the 'injected' object where I am trying to access id. In the course brad uses this syntax to use lte (/?averageCost[lte]=10000) but I do not get any results with my ne. Can anyone help me here?
const advancedResults = (model, populate) => async (req, res, next) => {
let query;
const injected = {
access: 'public',
'user._id[ne]': req.user.id, // I don't think user._id[ne] is correct
};
}
// Copy req.query
const reqQuery = { ...req.query, ...injected };
console.log('injected: ', injected);
// Fields to exclude
const removeFields = ['select', 'sort', 'page', 'limit'];
// Loop over removeFields and delete them from reqQuery
removeFields.forEach(param => delete reqQuery[param]);
// Create query string
let queryStr = JSON.stringify(reqQuery);
// Create operators ($gt, $gte, etc)
queryStr = queryStr.replace(/\b(gt|gte|lt|lte|in|ne)\b/g, match => `$${match}`);
// Finding resource and remove version
query = model.find(JSON.parse(queryStr)).select('-__v');
// Select Fields
if (req.query.select) {
const fields = req.query.select.split(',').join(' ');
query = query.select(fields);
}
// Sort
if (req.query.sort) {
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort(sortBy);
} else {
query = query.sort('-createdAt');
}
// Pagination
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 25;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const total = await model.countDocuments(JSON.parse(queryStr));
query = query.skip(startIndex).limit(limit);
if (populate) {
query = query.populate(populate);
}
// Executing query
const results = await query;
// Pagination result
const pagination = {};
if (endIndex < total) {
pagination.next = {
page: page + 1,
limit,
};
}
if (startIndex > 0) {
pagination.prev = {
page: page - 1,
limit,
};
}
res.advancedResults = {
success: true,
count: results.length,
pagination,
data: results,
};
next();
};
module.exports = advancedResults;
Answering your question about how to use $ne:
The use of $ne is as follows:
"field":{
"$ne": yourValue
}
Into your query should be like:
"user._id": {
"$ne": req.user.id
}
Example here
$ne operator will return all document where the field value don't match with the given value.
As you have done, to acces the nested field is necessary use the dot notation.
Also, to ensure it works, if your schema defines _id as ObjectId maybe is necessary parse req.user.id to ObjectId.
But if in your schema is a string then should works.
So try (not tested at all):
const injected = {
'user._id': { "$ne": req.user.id }
};

Multiple insertion with addition data with pg-promise

I have a large dataset that I want to insert into a postgres db, I can achieve this using pg-promise like this
function batchUpload (req, res, next) {
var data = req.body.data;
var cs = pgp.helpers.ColumnSet(['firstname', 'lastname', 'email'], { table: 'customer' });
var query = pgp.helpers.insert(data, cs);
db.none(query)
.then(data => {
// success;
})
.catch(error => {
// error;
return next(error);
});
}
The dataset is an array of objects like this:
[
{
firstname : 'Lola',
lastname : 'Solo',
email: 'mail#solo.com',
},
{
firstname : 'hello',
lastname : 'world',
email: 'mail#example.com',
},
{
firstname : 'mami',
lastname : 'water',
email: 'mami#example.com',
}
]
The challenge is I have a column added_at which isn't included in the dataset and cannot be null. How do I add a timestamp for each record insertion to the query.
As per the ColumnConfig syntax:
const col = {
name: 'added_at',
def: () => new Date() // default to the current Date/Time
};
const cs = pgp.helpers.ColumnSet(['firstname', 'lastname', 'email', col], { table: 'customer' });
Alternatively, you can define it in a number of other ways, as ColumnConfig is very flexible.
Example:
const col = {
name: 'added_at',
mod: ':raw', // use raw-text modifier, to inject the string directly
def: 'now()' // use now() for the column
};
or you can use property init to set the value dynamically:
const col = {
name: 'added_at',
mod: ':raw', // use raw-text modifier, to inject the string directly
init: () => {
return 'now()';
}
};
See the ColumnConfig syntax for details.
P.S. I'm the author of pg-promise.

Resources