Can't use variables as key, value for mongoose .find() - node.js

I'm using mongoose 5.11.4 and trying to find documents. On the official docs they say
MyModel.find({ name: /john/i })
I wanna know how to use variables for "name" & "john" and get the exact thing done. this is an API that I'm working on. filter (name) and the value (john) gonna decide by the frontend user. We should search for a given field using the value. Any suggestions?
let filter = req.params.filter
let value = req.params.value
MyModel.find({ filter : /value/i })
doesn't work

You can use the RegExp object to convert the value to a regular expression.
let regex = new RegExp(`${value}`,'options');
Now you can use it on mongoose query.
model.find({name:regex});

this worked : )
let filter = req.params.filter
let value = req.params.value
const filterParam = {}
filterParam[filter] = { $regex: .*${value}.* }
const suppliers = await Supplier.find(filterParam)

Related

How do I match multiple fields to only part of a query string in a mongoose query?

Let's say I have a collection of books that contains multiple documents that looks like this:
{
"author":"Gud Author",
"title":"Gud Book"
}
{
"author":"Lazy Writer",
"title":"Lesson about lazyness"
}
{
"author":"Mysterious Enigma",
"title":"Ways of the unknown"
}
In the request I'm getting one query parameter (string) called "filter". I want to use this parameter to get only the books whose author or title matches at least a part of the filter string.
This is my code so far:
bookRouter.get("/books", (req,res) => {
const {filter} = req.query
let query = {}
if (filter) query.$or = [{author: {$regex: filter}}, {title: {$regex: filter}}]
Book.find(query)
.then((books) => res.json(books))
.catch((err) => res.json(err))
})
If the filter parameter is shorter than the values in my db everything works ok.
"" as filter would return everything,
"Gud" would only return the first object
"Mysterious" would only return the third object"
However when my query is " Mysterious Enigma Ways" I'm getting back an empty array. Also a combination of both title and author that is only a partial match with both db values like "Mysterious Ways" won't work.
How do i modify my query to also return documents that only match with a part of the query string?
I've tried turning the string into an array (separating words) using .join(" ") to compare matches to the single elements of the array but I couldn't figure out how to combine the operators for this query.
PS: Yes I will convert both query string and values to lowercase for case-insensitive comparison. Just wanted to keep this short.
Instead of $regex, try using plain Javascript regex, by splitting the input words and checking if any of the words are present using $in,
let splitWords = filter.split(" ");
let regexWords = splitWords.map(e =>{return new RegExp(e,i)}
if (filter) query.$or = [{author: {$in: regexWords}}, {title: {{$in: regexWords}}]

Implementing mongodb pagination along with match query?

I have user document as this
users = [
{
_id:'',
name:'jay',
email:'jay#gmail.com',
role: 'actor',
status: true // isActive
},
{
_id:'',
name:'ram',
email:'ram123#gmail.com',
role: 'electrician',
status: false // isActive
},
...... so on
]
I want to apply pagination and also some filters to retrieve data
filter = {
role: 'actor',
order: -1 //descending sort,
sortOn: 'name' // apply sort on name field
search: 'ja', // match the string starting with 'ja',
status: true,
size:25,
page: 1 // means documents from 1-25, page2 means 26-50
}
How can this be achieved?
I am using mongoose as well.
Using your filter object you can do something like this:
Use these steps to ensure a good pagination:
Sort by any value (to ensure not get random positions)
Skip by the number of pages
Limit by the number of elements into page
So, the query will be something like (not tested but you can see the idea):
const elementsPerPage = filter.size
const nSkip = elementsPerPage * filter.page
const sort = {[filter.sortOn]:filter.order}
YourModel.find({/*yourquery*/})
.limit(elementsPerPage)
.skip(nSkip)
.sort(sort)
Also, you can use filter values into your query, something like:
YourModel.find({
role: filter.role,
status:filter.status,
name:{ $regex: filter.search}
})
This query is like this example.
Also, is not defined what calues do you want to use, the condition etc, so, with this, you can use if/else to add or not values into query.
For example:
var query = {}
if(filter.search){
query.name = {$regex: filter.search}
}
So all together can be:
const elementsPerPage = filter.size
const nSkip = elementsPerPage * filter.page
const sort = {[filter.sortOn]:filter.order}
var query = {}
if(filter.search){
query.name = {$regex: filter.search}
}
if(filter.role){
query.role = filter.role
}
if(filter.status){
query.status = filter.status
}
YourModel.find(query)
.limit(elementsPerPage)
.skip(nSkip)
.sort(sort)
Note that this has not been tested, but as I've said before you can see the idea with this example.

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 pass fieldname dynamically in mogodb

I want to pass field name dynamically in Mondgodb while query the collection. e.g.
emp.ply = function(res,res) {
Employee.find({area: 'Plymouth'}).exec(function(err,PLY) {
res.render("../index", {resultset: PLY})
here I'm doing query on field name area which is hard code. Instead of i want to pass fieldname dynamically. How we can achieve it in nodeJS
So, conceptually you just need to build the query object before sending it to the .find() function. Perhaps something like this:
const query = {}
const fieldname = 'area'
const fieldvalue = 'Plymouth'
query[fieldname] = fieldvalue
Employee.find(query).exec((err, ply) => {
// do whatever
});
You could conceptually extract both fieldname and fielvalue from req.body or req.query depending on what you're doing.
If your code supports ES6 or (any latest browser except IE), we can do this:
emp.ply = function(res,res) {
let searchField = 'area'; // this can be anything dynamically
Employee.find({[searchField]: 'Plymouth'}).exec(function(err,PLY) {
res.render("../index", {resultset: PLY})
})
}
You can refer to MDN docs about Computed property names

SQL Server : lower camelcase for each result

I am creating an API with SQL Server as the database. My tables and columns are using Pascal case (CountryId, IsDeleted, etc) that cannot be changed.
So when I do this:
const mssql = require('mssql');
var sqlstr =
'select * from Country where CountryId = #countryId';
var db = await koaApp.getDb();
let result = await db.request()
.input('countryId', mssql.Int, countryId)
.query(sqlstr);
My resulting object is
{
CountryId: 1,
CountryName: "Germany"
}
But I want it to be
{
countryId: 1,
countryName: "Germany"
}
I know there is a "row" event, but I wanted something more performant (since I may be returning several rows from the query, above is just an example).
Any suggestions?
PS: I want to avoid the FOR JSON syntax
Posting this as an actual answer, as it proved helpful to the OP:
if it's viable, you may try simply specifying the columns in the query as such:
select
CountryID countryId,
CountryName countryName
from
Country
where
CountryId = #countryId
Typically it's not best practice to use select * within queries anyways because of performance.
A simple explanation, putting a space and a new name (or perhaps better practice, within square brackets after each column name, such as CountryName [countryName] - this allows for characters such as spaces to be included within the new names) is aliasing the name with a new name of your choosing when returned from SQL.
I'd suggest using the lodash utility library to convert the column names, there is a _.camelCase function for this:
CamelCase documentation
_.camelCase('Foo Bar');
// => 'fooBar'
_.camelCase('--foo-bar--');
// => 'fooBar'
_.camelCase('__FOO_BAR__');
// => 'fooBar'
You can enumerate the result keys using Object.entries then do a reduce, e.g.
let result = {
CountryId: 1,
CountryName: "Germany"
};
let resultCamelCase = Object.entries(result).reduce((obj,[key,value]) => {
obj[_.camelCase(key)] = value;
return obj;
}, {});
console.log(resultCamelCase);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Resources