How to get all results that contain a certain string (explained more in post) - node.js

So, let's say I put "h" in a post form and I have 5 things in my data base:
1st Entry - Name: Hello
2nd Entry - Name: Height
3rd, 4th, 5th entries do not have a word that has "h" in them
How would I make it return all of the ones that include the string provided (like Hello and Height)
Here is my current setup:
app.post('/search/full', async (req, res) =>{
const URLs = await shortURL.find({
"short": {
"$regex": req.body.fullURLSearch,
"$options": "i"
}
})
res.render('searchFull', {
fullURLsearch: URLs
})
})

If you want case insensitive query, you can do this (if you want case sensitive query, just remove "$options": "i"):
db.collection.find({
"field": {
"$regex": "YourQueryString",
"$options": "i"
}
})
Here is the working snippet: https://mongoplayground.net/p/F6oSaf8P7lb

You don't really give much to go on, but based on your question I can assume something like this might work:
db.collection.find({field:/H/}})
naturally replace the H with a variable as required

Related

Cypress index position to came before a specific string

I need to check a string on a Thead to come before a specific string.
User need to be before Primium like in the e.g
Here is the code I am using
it('test', () => {
cy.visit('url')
cy.wait('#geturl')
.its('response.statusCode')
.should('be.oneOf', [200, 201])
cy.url().should('include', 'url')
cy.get('h1').should('contain', 'url List')
cy.get('thead').find(`th:contains('User')`).invoke('index').as('userIndex')
cy.get('thead')
.find(`th:contains('Premium')`)
.invoke('index')
.as('premiumUndex')
cy.get('#userIndex').then((userIndex) => {
cy.get('#premiumUndex').then((premiumUndex) => {
expect(userIndex).to.be.lessThan(premiumUndex)
})
})
})
It is working, but I need to check the User column is before Premium column and after Agent column Is there a better way to do it?
Check three column headers using .slice() to get just the columns you want.
Then map elements to the text. Use .trim() if there is whitespace around it.
cy.get('thead th')
.invoke('slice', 4, 7)
.then($th => [...$th].map(th => th.innerText.trim()))
.should('deep.eq', ['Agent', 'User', 'Premium'])
The easiest way I can see is to take all the header texts as one long string and check that "AgentUserPremium" is in it.
In case there are whitespace characters like " " or "\n", add a filter to remove these.
cy.get('thead th')
.invoke('text')
.then(text => text.replace(/\s/g, ''))
.should('contain', 'AgentUserPremium')
If you are checking for headers as part of a test, the a stronger test would be to set the headers as a static variable within your test and then use .each() to check each header in the table.
const headers = [
"",
"Number",
"Code",
"Client",
"Agent",
"User",
"Premium",
"Status",
"Time",
"",
];
cy.get("th")
.should("have.length", headers.length)
.each((header, index) => {
expect(header).to.have.text(headers[index]);
});
Here is a working example based on the screenshot.

Nodejs Elasticsearch query default behaviour

On a daily basis, I'm pushing data (time_series) to Elasticsearch. I created an index pattern, and my index have the name: myindex_* , where * is today date (an index pattern has been setup). Thus after a week, I have: myindex_2022-06-20, myindex_2022-06-21... myindex_2022-06-27.
Let's assume my index is indexing products' prices. Thus inside each myindex_*, I have got:
myindex_2022-06-26 is including many products prices like this:
{
"reference_code": "123456789",
"price": 10.00
},
...
myindex_2022-06-27:
{
"reference_code": "123456789",
"price": 12.00
},
I'm using this query to get the reference code and the corresponding prices. And it works great.
const data = await elasticClient.search({
index: myindex_2022-06-27,
body: {
query: {
match: {
"reference_code": "123456789"
}
}
}
});
But, I would like to have a query that if in the index of the date 2022-06-27, there is no data, then it checks, in the previous index 2022-06-26, and so on (until e.g. 10x).
Not sure, but it seems it's doing this when I replace myindex_2022-06-27 by myindex_* (not sure it's the default behaviour).
The issue is that when I'm using this way, I got prices from other index but it seems to use the oldest one. I would like to get the newest one instead, thus the opposite way.
How should I proceed?
If you query with index wildcard, it should return a list of documents, where every document will include some meta fields as _index and _id.
You can sort by _index, to make elastic search return the latest document at position [0] in your list.
const data = await elasticClient.search({
index: myindex_2022-*,
body: {
query: {
match: {
"reference_code": "123456789"
}
}
sort : { "_index" : "desc" },
}
});

MongoDB search results are slow and inaccurate

On https://cbbanalytics.com/, after logging in with email: stackacct#gmail.com, password: pass123, a search bar appears in the top-right corner. When text is input, the following route fires off:
router.get('/live-search/text/:text', function (req, res) {
try {
let text = req.params.text;
// Use $regex
let queryFilters = { label: { $regex: `${text}`, $options: 'i' } };
// Use $search (text-index)
// let queryFilters = { $text: { $search: text } };
// Return Top 20
db.gs__ptgc_selects
.find(queryFilters)
.limit(20)
.then(data => res.json(data))
.catch(err => res.status(400).json('Error: ' + err));
} catch (error) {
res.status(500).json({ statusCode: 500, message: error.message });
}
});
gs__ptgc_selects is a mongodb collection with 180K documents, and we are searching on the label field present in each document. label is set as a text index in MongoDB Atlas:
The primary issue with the regex implementation is:
each fetch takes ~150ms which is noticeable in the search performance
regex isn't returning the best search results. searching Zio returns Alanya DeFazio before Zion Young. Optimal order of search return would be (i) all 1st names starting with Zio, sorted alphabetically, (ii) all 2nd words starting with Zio, (iii) other words with Zio nested inside the word.
using regex doesn't leverage the text index at all. as a result, Query Targeting: Scanned Objects / Returned has gone above 1000 warnings are returned when the search is used.
If we uncomment let queryFilters = { $text: { $search: text } }; and use this instead of regex:
only exact matches are returned
fetches are still at ~150ms
Is it possible to improve search within our current stack (Node JS, mongoDB, and mongoose)? Or are these limitations unavoidable?
Edit: We had recently created a search-index for the entire gs__ptgc_selects collection, however this doesn't appear to be improving search.

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', ''),
},
});

A find() statement with possible null parameters

I'm trying to figure out how Mongoose and MongoDB works... I'm really new to them, and I can't seem to figure how to return values based on a find statement, where some of the given parameters in the query possible are null - is there an attribute I can set for this or something?
To explain it further, I have a web page that has different input fields that are used to search for a company, however they're not all mandatory.
var Company = mongoose.model('Company');
Company.find({companyName: req.query.companyName, position: req.query.position,
areaOfExpertise: req.query.areaOfExpertise, zip: req.query.zip,
country: req.query.country}, function(err, docs) {
res.json(docs);
});
By filling out all the input fields on the webpage I get a result back, but only that specific one which matches. Let's say I only fill out country, it returns nothing because the rest are empty, but I wish to return all rows which are e.g. in Germany. I hope I expressed myself clearly enough.
You need to wrap the queries with the $or logic operator, for example
var Company = mongoose.model('Company');
Company.find(
{
"$or": [
{ "companyName": req.query.companyName },
{ "position": req.query.position },
{ "areaOfExpertise": req.query.areaOfExpertise },
{ "zip": req.query.zip },
{ "country": req.query.country }
]
}, function(err, docs) {
res.json(docs);
}
);
Another approach would be to construct a query that checks for empty parameters, if they are not null then include it as part of the query. For example, you can just use the req.query object as your query assuming the keys are the same as your document's field, as in the following:
/*
the req.query object will only have two parameters/keys e.g.
req.query = {
position: "Developer",
country: "France"
}
*/
var Company = mongoose.model('Company');
Company.find(req.query, function(err, docs) {
if (err) throw err;
res.json(docs);
});
In the above, the req.query object acts as the query and has an implicit logical AND operation since MongoDB provides an implicit AND operation when specifying a comma separated list of expressions. Using an explicit AND with the $and operator is necessary when the same field or operator has to be specified in multiple expressions.
If you are after a query that satisfies both logical AND and OR i.e. return all documents that match the conditions of both clauses for example given a query for position AND country OR any other fields then you may tweak the query to:
var Company = mongoose.model('Company');
Company.find(
{
"$or": [
{ "companyName": req.query.companyName },
{
"position": req.query.position,
"country": req.query.country
},
{ "areaOfExpertise": req.query.areaOfExpertise },
{ "zip": req.query.zip }
]
}, function(err, docs) {
res.json(docs);
}
);
but then again this could be subject to what query parameters need to be joined as mandatory etc.
I simply ended up deleting the parameters in the query in case they were empty. It seems all the text fields in the submit are submitted as "" (empty). Since there are no such values in the database, it would return nothing. So simple it never crossed my mind...
Example:
if (req.query.companyName == "") {
delete req.query.companyName;
}

Resources