Sequelize | Node.js, search between two numbers - node.js

I'm working on an advanced search with multiple options with Sequelize and Node.js.
One of the options is between two prices.
const search2 = req.body.search2;
const title = req.body.title;
const price = req.body.price;
const prices = req.body.prices;
const prices2 = req.body.prices2;
const address = req.body.address;
Product.findAll({
where: { [Op.and]:
{ price: { [Op.like]: '%' + price + '%' },
category: { [Op.like]: '%' + title + '%' },
description: { [Op.like]: '%' + search2 + '%' },
Renting: { [Op.between]: [prices, prices2] },
address: { [Op.like]: '%' + address + '%' } } }
, order: [['createdAt', 'DESC']], limit, offset
})
const prices and const prices2 are two inputs which the user can write the min and max price.
In my Sequelize model I have column 'Renting' (for renting prices, its INTEGER).
it seems to be that everything is working fine,but the problem is that i get the between prices and prices2 '50' AND '200'. (for example if im searching between 50 and 200).
enter image for example
which is giving me empty results or wrong results for different numbers.
I noticed if i put numbers beside prices and prices2, for example i will write:
Renting: { [Op.between]: [50, 200] },
i will get the right results. 50 AND 200 (without '')
enter image for example
which is good. But it just if I defined the numbers by myself.
Can anyone please help me to find a solution how to get the right result using this two inputs (prices and prices2)?

i think you should convert prices and prices2 to INTEGER before use it in query, and you can update your code
Renting: { [Op.between]: (+prices, +prices2) },
because when you putting '50' , '250' can not match it and return wrong answer.

Just a minor modification to #mohammad javad ahmadi's answer:
Renting: { [Op.between]: [+prices, +prices2] },
Op.between expects array, not tuple.

Related

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.

How to get lastest inserted record _id in mongoose

I am trying to get recently inserted record _id and p_id but i do not know how to get the value.Below given my code.This is not working.How to do it?
DB records:
{
_id:5eba58f0e5def333ad4d8c8d,
p_id:"C1",
product_name:"Name",
product_weight:123
},
{
_id:5eba58f0e5def333ad4d8c8e,
p_id:"C2",
product_name:"Name",
product_weight:123
},
{
_id:5eba58f0e5def333ad4d8c8f,
p_id:"C3",
product_name:"Name",
product_weight:123
}
data.controller.js:
var Product = mongoose.model(collectionName);
let latest_id = Product.findOne().sort({ field: 'asc', _id: -1 }).limit(1);
console.log("_id" + val); //output should be 3
let latest_p_id = Product.findOne().sort({ field: 'asc', p_id: -1 }).limit(1);
console.log("p_id" + val); //output should be C3
MongoDB does not natively support incremental auto generated numbers, so your first case, it's not possible if you don't manage your counter separately. You can count the number of documents, but it won't account for deleted documents.
For the second case, you almost got it:
with async/await
const product = await Product.findOne().sort({ p_id: -1 }).limit(1)
console.log(product.p_id) // this will be your desired output
without async/await
Product.findOne().sort({ p_id: -1 }).limit(1).then((product) => {
console.log(product.p_id) // this will be your desired output
})

match all if the user doesn't specify the field value MongoDB

I am building an API where I have several fields that are optional in my get request. So I want MongoDB to match all values for those optional fields if the user does not specify it. I have come up with this solution:
db.collection(expenses_collection).find(username: username, category: {$regex:"/" + category + "/"}, payment_type: {$regex:"/" + payment_type + "/"}})
Where if category and payment_type are not specified by the user I set them to ".*":
const {category=".*", payment_type=".*"} = req.query;
However, mongodb is still not matching any data. Any help is appreciated. Thanks a lot.
The issue is with your regex string. To match any string value, you have to use this pattern (this matches any string): (.*?)
Consider input documents:
{ _id: 1, name: "John", category: "cat 1", payment_type: "cash" },
{ _id: 2, name: "Jane", category: "cat 2", payment_type: "credit card" }
Usage to match any category field value:
let categoryStr = /(.*?)/
db.exp.find( { category: categoryStr } )
The query returns all documents.
So, in your application for the category value not specified the code can be like this:
if (category is empty or null) { // category not specified by user
categoryStr = /(.*?)/
}
Similarly, for the payment_type field also.
Then query would be:
db.exp.find( {
username: usernameStr,
category: categoryStr,
payment_type: paymentStr
} )
NOTE: The code tests fine with MongoDB NodeJS driver APIs.
Isn't this what exists is made for?
{category: { $exists: true }, payment_type: { $exists: true }}

How to match the two strings with and without including spaces

For example: In DB I've the string value like "cell phones". If I get the string value like "cellphones" from frontend. How can I compare it with DB string and get the related string values in response.
You can compare so:
let val1 = 'cell phones';
let val2 = 'cellphones';
console.log(val1.replace(/\s/g, '') === val2.replace(/\s/g, '')) // true
//OR
console.log(val1.split(' ').join('') === val2.split(' ').join('')) // true
If you need some aggregation trick then you can try this
db.collection.aggregate([
{ "$project": {
"name": {
"$reduce": {
"input": { "$split": ["$name", " "] },
"initialValue": "",
"in": { "$concat": ["$$value", "$$this"] }
}
}
}},
{ "$match": { "name": "cellphones" }}
])
You can test it
Here
You can first start by stripping out the spaces on both the strings before comparing them, for example:
let a = "cell phone";
let b = "cellphone";
let c = "cell phones"
const stripSpaces = s => s.replace(/\s/g, '');
// compare
console.log(stripSpaces(a) == stripSpaces(b)); // true
console.log(stripSpaces(a) == stripSpaces(c)); // false
Just remove those spaces from the response you are getting after query find then pass the response to require input field. Then match that string with front-end or input string. If both matches load the required content.
Suppose the collection name is Category. Then the sample query will be like this
Category.find().exec((err, categories) => {
var details=[]
var matchCategory=[]
categories.map((category,key)=>{
var obj ={}
obj.name = category.name.toLowerCase().replace(/\s/g, "")
details.push(obj);
})
if(details.length > 0){
var detailsLength=details.length
details.map((category,key)=>{
if(category.name=="cellphones"){ // match your input string
matchCategory.push(category)
}
detailsLength--
})
if(detailsLength==0){
resolve(matchCategory);
}
}
})
This may help you to reach out.
Answers below this question are good, like using where and Regex, but might be at their best if you got a small number of docs that you may want to query from.
If you got many docs, I'd suggest you:
1. Use an extra field like cellphone without any space, if the values of the original field are expected to be short.
2. Try using search engines, like ElasticSearch, or MongoDB's own text search, to find what you need, not only cell phone to cellphone, but mobile phone even smartphone. Actually, when you google something, the suggestions while you're typing are also coming from similar but more complex algorithms.
Given a document like this:
{
"text" : "cell phones"
}
You could use the $where operator like this:
db.collection.find({
$where: function() {
return this.text.replace(' ', '') == "cellphones"
}
});
I wouldn't necessarily recommend this for big collections (performance might not be great). However, even with big collections you could supposedly achieve some pretty ok performance by adding an extra filter on the "text" field to filter out all documents that don't start with the correct first character(s):
db.collection.find({
"text": { $regex: "^" + "cellphones".charAt(0) }, // this will use an index on the "text" field if available
$where: function() {
return this.text.replace(' ', '') == "cellphones"
}
});
Or perhaps even this version with yet another filter in the $where bit that checks the string lengths for a reduced number of string comparisons:
db.collection.find({
"text": { $regex: "^" + "cellphones".charAt(0) }, // this will use an index on the "text" field if available
$where: function() {
return this.text.length >= "cellphones".length // performance shortcut
&& this.text.replace(' ', '') == "cellphones"
}
});
You can first start by stripping out the spaces on both the strings before comparing them. I'm assuming you don't know which one has spaces before hand, so you will run all the values through the stripSpaces function, for example:
let a = "cell phone";
let b = "cellphone";
let c = "cell phones"
const stripSpaces = (s) => s.split(' ').join('');
// compare
console.log(stripSpaces(a) == stripSpaces(b)); // true
console.log(stripSpaces(a) == stripSpaces(c)); // false
try replacing empty string from query string first, and then compare to the field as
db.test.find(function() {
return this.data(function(elm) {
"cell phones".replace(/ /g,"") == elm.name
});
});
May be it solves Your problem
Take Device_Names column contains
"cell phone"
"mobile phone"
"cellphone"
"laptop"
1.) Normal way:
select Device_Name from devices where Device_Name='cellphone' ;
result:
"cellphone"
which is third one
2.)By Remove spaces:
SELECT Device_Name FROM devices WHERE REPLACE(Device_Name, ' ', '')='cellphone'
result:
"cell phone"
"cellphone"
which includes first and third one
You can use of a regex when looking for your value, like :
cellphones|cell phones
Collection.find({
someName: {
$regex: new RegExp('cellphones|cell phones', ''),
},
});

Mongoose get a random element except one

I have a collection of articles in mongodb. I choose an article that i want to render, and I want two other articles chosen randomly. I want to pick two articles in my collection that are not the same, and are not the article I have chosen before.
Been on this problem for hours, search for a solution but only found how to pick an element randomly, but not except one...
Here is what I have now :
article.find({}, function(err, articles{
var articleChosen = articles.filter(selectArticleUrl, articleUrl)[0];
article.find({})
.lean()
.distinct("_id")
.exec(function(err, arrayIds){
var articleChosenIndex = arrayIds.indexOf(articleChosen._id);
arrayIds.splice(articleChosenIndex, 1);
chooseRdmArticle(arrayIds, function(articleRdm1Id){
var articleRmd1 = articles.filter(selectArticleId, articleRdm1Id)[0];
var articleRdm1Index = arrayIds.indexOf(articleRdm1Id);
arrayIds.splice(articleRdm1Index, 1);
chooseRdmArticle(arrayIds, function(articleRdm2Id){
var articleRmd2 = articles.filter(selectArticleId, articleRdm2Id)[0];
// do stuff with articleChosen, articleRmd1 and articleRmd2
})
})
})
})
where the function which choose rdm article is :
function chooseRdmArticle(articles, callback){
var min = Math.ceil(0);
var max = Math.floor(articles.length);
var rdm = Math.floor(Math.random() * (max - min)) + min;
callback(articles[rdm])
}
and the function which select the article from its url is :
function selectArticleUrl(element){
return element.url == this
}
My idea was to work on the array containing all the ObjectId (arrayIds here), to choose two Ids randomly after removing the articleChosen id. But I understood that arrayIds.indexOf(articleRdm1Id); couldn't work because ObjectIds are not strings ... Is there a method to find the index of the Id I want? Or any better idea ?
Thanks a lot !
Run two queries where the first fetches the chosen document and the other uses the aggregation framework to run a pipeline with the $sample operator to return 2 random documents from the collection except the chosen one.
The following query uses Mongoose's built-in Promises to demonstrate this:
let chosenArticle = article.find({ "url": articleUrl }).exec();
let randomArticles = article.aggregate([
{ "$match": { "url": { "$ne": articleUrl } } },
{ "$sample": { "size": 2 } }
]).exec();
Promise.all([chosenArticle, randomArticles]).then(articles => {
console.log(articles);
});
There is the mongodb command $sample, which is gonna read documents in a random way.
Example from the documentation :
db.users.aggregate( [ { $sample: { size: 3 } } ] )
I had the same problem and this works for me
const suggestedArticles = await Article.find({
articleId: { $ne: req.params.articleId },
}).limit(2);

Resources