Mongoose dynamic query - node.js

Query parsed from URL, example :
?year=2014&cat=sonny
Or it can be
?year=2014&id=223&something=high&cat=sonny
I could do
Model.find({year: 2014}).where('cat').equals('sonny')
But what if there a second example? How can I make it dynamic?

You can set the query to a variable and add multiple conditions:
var query = Model.find();
query.where('year').equals('2014');
query.where('cat').equals('sonny');
query.where('id').equals('223');
query.where('something').equals('high');
query.exec(callback);
For dynamic, just pass the query to a for loop and iterate through an array of your filter objects:
var query = Model.find();
var filters = [
{fieldName: "year", value: "2014"},
{fieldName: "cat", value: "sonny"}
...
];
for (var i = 0; i < filters.length; i++) {
query.where(filters[i].fieldName).equals(filters[i].value)
}
query.exec(callback);

Building on the cdbajorin's answer - I suspect many coders are trying to take input from a form and dynamically build a Mongoose filter from the end users input. (or at least that was my scenario).
If you 'name' the html input fields the same as your Mongoose Schema name
<input type='text' name='person.address'>
Then in your code you can use the req.body object
var query = Model.find();
for (var fieldName in req.body)
{
if(req.body.hasOwnProperty(fieldName)) //no inherited properties
{
if(req.body[fieldName]) //get rid of empty fields
{
query.where(fieldName).equals(req.body[fieldName]);
}
}
}
query.exec(function(err,data){console.log('QUERY EXECUTE : ' + err, data, data.length);});

Related

Make a mongoDB query using OR statement and array

I have web interface need to access an URL (shown below) and return only results with requested groups:
http://localhost:3000/device/getgroups/group1,group2,group3,group4
In MongoDB I have entries with the field 'loc_group'.
I want to return the results only where have the field "loc_group" = group1 OR group2 OR group3 OR group4.
My routes are like this:
router.get('/getgroups/:groups', async (req, res) => {
try {
var groups = "req.parms.groups";
#split groups
var arr = groups.split(",");
#iterate in the results on array (arr)
for (var i = 0; i < arr.length; i++) {
arr[i] = ++arr[i];
if (i = arr.lenght) {
var query = "{'loc_group': '" + arr[i] + "' }"
var totals = totals + query;
} else {
var query = "{'loc_group': '" + arr[i] + "' },"
var totals = totals + query;
}
}
# after this for, the totals must looks like
# "{'loc_group': 'group1' }"
# "{'loc_group': 'group2' }"
# "{'loc_group': 'group3' }"
# "{'loc_group': 'group4' }"
#
# then i try to use it on the mongodb query:
const logues = await device.find({ $or: [ totals ], })
res.json(logues)
} catch (err) {
res.status(500).json({ message: err.message })
}
})
but it's not working, what I get when enter the URL is:
{"message":"$or/$and/$nor entries need to be full objects"}
I am a beginner to nodejs and this is my first question here, if need some other information or something please let me know. Thanks.
There are several issues with your code. But the main one is basically ignoring the expectations of API you work with. See, MongoDB expects regular JavaScript objects passed in $or query. What you give it instead is a concatenation of stringified objects, each created by this expression:
var query = "{'loc_group': '" + arr[i] + "' }"
... passed into regular array. Apparently, MongoDB couldn't guess what you wanted, hence $or/$and/$nor entries need to be full objects error.
So, instead of doing JS work on your own, just use native objects to create a query. In fact, you don't even need $or here, as you're looking for the same property's values. Quoting the docs:
When using $or with that are equality checks for the
value of the same field, use the $in operator instead of the $or
operator.
So here's one (simplified) way to do it:
router.get('/getgroups/:groups', async (req, res) => {
const locGroups = req.params.groups.split(',');
const logues = await device.find({ loc_group: { $in: locGroups } });
res.json(logues);
}
What? Yes, that's essentially it. You might consider adding some validation and error handling to this code, and may be some logging, too, but it should be a middleware concern anyway, so the controller stays THAT simple. And that's the beauty - and the reason of existence - of the modern backend-oriented ecosystems: easy things should stay easy.

Mongoose, NodeJS & Express: Sorting by column given by api call

I'm currently writing a small API for a cooking app. I have a Recipe model and would like to implement sorting by columns based on the req Parameter given.
I'd like to sort by whatever is passed in the api call. the select parameter works perfectly fine, I can select the columns to be displayed but when I try to sort anything (let's say by rating) the return does sort but I'm not sure what it does sort by.
The code i'm using:
query = Recipe.find(JSON.parse(queryStr));
if(req.query.select){
const fields = req.query.select.split(',').join(' ');
query = query.select(fields);
}
if(req.query.sort){
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort({ sortBy: 1 });
} else {
query = query.sort({ _id: -1 });
}
The result, when no sorting is set: https://pastebin.com/rPLv8n5s
vs. the result when I pass &sort=rating: https://pastebin.com/7eYwAvQf
also, when sorting my name the result is also mixed up.
You are not using the value of sortBy but the string "sortBy". You will need to create an object that has the rating as an object key.
You need the sorting object to look like this.
{
rating: 1
}
You can use something like this so it will be dynamic.
if(req.query.sort){
const sortByKey = req.query.sort.split(',').join(' ');
const sortByObj = {};
sortByObj[sortByKey] = 1; // <-- using sortBy as the key
query = query.sort(sortByObj);
} else {
query = query.sort({ _id: -1 });
}

How to search data in mongodb with dynamic fields using mongoose?

I've a node.js api in which user sends the required fields as an array to be fetched from the mongodb database. I need to find the data of that fields using Find query. I've written forEach statement to loop through that array and got the array elements. But when I try to get the results by inserting the array elements in the query, it doesn't giving the required results. Could any one please help me in resolving the issue by seeing the code below?
templateLevelGraphData: async function(tid,payload){
let err, templateData, respData = [], test, currentValue;
[err,templateData] = await to(Template.findById(tid));
var templateId = templateData.templateId;
payload.variables.forEach(async data=>{
console.log(data); //data has the array elements like variables=["humidity"]
[err, currentValue] = await to(mongoose.connection.db.collection(templateId).find({},{data:1}).sort({"entryDayTime":-1}).limit(1).toArray());
console.log(currentValue);
});
return "success";
}
The expected output is,
[ { humidity: 36 } ]
But I'm getting only _id like,
[ { _id: 5dce3a2df89ab63ee4d95495 } ]
I think data is not applying in the query. But I'm printing the data in the console where it's giving the correct results by displaying the array elements like, humidity. What I need to do to make it work?
When you are passing {data: 1} you are passing an array where is expecting name of column.
You have to create an object where the keys are going to be the elements of the array and set them to 1.
const projection = data.reduce((a,b) => (a[b]=1, a), {});
[...] .find({}, projection) [...]
Actually I got the solution.
for(let i=0;i<payload.variables.length;i++){
var test = '{"'+ payload.variables[i] +'":1,"_id":0}';
var query = JSON.parse(test);
[err, currentValue] = await to(mongoose.connection.db.collection(templateId).find({"deviceId":deviceId},query).sort({"entryDayTime":-1}).limit(1).toArray());
console.log(currentValue); //It's giving the solution
}

Mongoose find not match ids from array which passed

I'm stuck in mongoose query. I've an array of ids as input, I want to search that array of ids in a database for matching elements. It returns perfect result after using $in in find query.
Now, I want that Ids from an array which is not found in the database. what is the best way to do it?
Please try to comment it.
Template.find({
_ids : [
"as6d87as67da7s8d87a87", // available in database
"as6dasd8sa9d8a9a9s8d7", // not-available in database
"6756asd5as6dsadghasd3", // available in database
]
}, function(err, result){
// Need result as "as6dasd8sa9d8a9a9s8d7", which is not matched. or in object.
// Can we do with `aggregate` query?
});
I think this makes what you want
var _ids = [
"as6d87as67da7s8d87a87",
"as6dasd8sa9d8a9a9s8d7",
"6756asd5as6dsadghasd3"
];
Template.find({
_ids : _ids
}, function(err, result){
var filteredResult = _ids.filter(currentId => !result.some(item => item._id.toString() == currentId));
});
Answer by David will work. The idea here is to compare _ids array with the _ids in the result and return the missing ones. Adding more code just for understanding purposes:
Push _id of result into a new array
Compare _ids and the new array to return missing elements
var resIDs = new Array();
for (var i = 0; i < result.length; i++) {
resIDs.push(result[i]._id.toString());
}
var resultFiltered =
_ids.filter(function (v) {
return !resIDs.includes(v.toString());
})

how to use in operator in dynamo db

I have a user table with a field username. I need to write something equivalent to this in dynamo db: Select * from user where username in('a','b','c');
Adding more from code prosepective i have usernames in an array say var arr=['a','b','c'];
I so far tried this which is giving me zero result
this.dynamo.client.scanAsync({
TableName: this.dynamo.table('users'),
FilterExpression: 'username IN (:list)',
ExpressionAttributeValues: {
':list': arr.toString()
}
}).then((response) => {
console.log(response);
return {
userFriends: result.Item.friends
};
});
When I pass one element in array it give me result searching passed single element in user table but its not working with more than one element in array.
The individual users should be given as comma separated String variables. JavaScript array is equivalent to List in AWS DynamoDB data type. The DynamoDB can't compare the String data type in database with List attribute (i.e. Array in JavaScript).
var params = {
TableName : "Users",
FilterExpression : "username IN (:user1, :user2)",
ExpressionAttributeValues : {
":user1" : "john",
":user2" : "mike"
}
};
Construct the object from array for FilterExpression:-
Please refer the below code for forming the object dynamically based on Array value.
var titleValues = ["The Big New Movie 2012", "The Big New Movie"];
var titleObject = {};
var index = 0;
titleValues.forEach(function(value) {
index++;
var titleKey = ":titlevalue"+index;
titleObject[titleKey.toString()] = value;
});
var params = {
TableName : "Movies",
FilterExpression : "title IN ("+Object.keys(titleObject).toString()+ ")",
ExpressionAttributeValues : titleObject
};
Note:-
I don't think IN clause with 1000s of usernames is a good idea in terms of performance.

Resources