Making a case-insensitive "like" query using the mongodb driver in Node - javascript

I'm looking to make a query like mentioned in the title with an array of data.
I have this line working..
collection.find({"name": {"$in":[/papa JOhn's/i,"Taco Bell"]}}).toArray(function(err, items) {
However, the data is going to be dynamic and fed into the query as an array
restaurant_data = [rest1, rest2 .... restn]
I want to essentially have something like
collection.find({"name": {"$in": /restaurant_data/i }}).toArray(function(err, items) {
My Attempt..
let test_data = ['papa john', 'taco bell']
//Get an array of the restaurant names here
collection.find({"name": {"$in": test_data.map(element => {
/element/i
})}}).toArray(function(err, items) {
My working attempt!
I got it doing this... Not sure if it's the most efficient
let test_data = ['papa john', 'taco bell']
collection.find({"name": {"$in": test_data.map((element) => {
var regex = new RegExp(".*" + element + ".*");
return regex
})}}).toArray(function(err, items) {

collection.find({"name": {"$in":
restaurant_data.map(e => new RegExp(e, 'i'))
}}).toArray
has better chance to work. Note that /bla/i will match blabla so
collection.find({"name": {"$in": [/bla/i] } })
will match documents with name blabla Not sure this is what you want or not.

If you're using mongodb 3.2+ I'd recommending you using the property Collation as using Regex might make the queries quite slower, you can use it in both creating indexes as well as querying:
db.peeps.createIndex({UserName:-1}, { collation: {locale:'en', caseLevel:false, caseFirst: "off"} )
db.peeps.find({UserName:'bob'}).collation({locale:'en', caseLevel:false, caseFirst: "off"}) // 3 results!
However, as I've faced this problem as well when I tried using collation it made my queries at least 2 times slower so I ended up sticking with inserting lowerCase data in mongodb and then validating and using toLowerCase in the payload fields instead of using Collation.

Related

I'm trying to make an array with only values ​that are not contained in the other array (non-repeating)

I have an array of available users that can be invited and also another array with all joined users to the particular chat. I need to check which of the available users have joined the chat and should be listed elsewhere.
Finally, I want to get an array with only the available users who have not joined the chat.
let availablеUsers = [{id:1,name:'Dani'}, {id:2,name:'Ani'}, {id:3,name:'Marta'}]
let allUsers = [{id:2,name:'Ani'},{id:10,name:'John'}, {id:3,name:'Marta'}]
The first thing I try to do is find those who are already participating in the chat:
let joinedUsers = availablеUsers.map((user) => {
return allUsers?.find((u) => u.id === user.id);
});
And i get this : [undefined, {… Аni}, {… Marta}]
Then I try to filter the array of available users so that I remove from it those that are in the newly created array and here's the problem I don't know how to do this :/
My idea is something like that:
availablеUsers = availablеUsers.filter((user) => {
//HERE I don't know what logic to write
return joinedUsers?.map((m) => m?.id !== user.id); // this doesn't work, just an example
});
My goal is to have only those users not contained in the other remain in the availableUsers array.
In the example I have given at the end in the array should remain only {id:1,name:'Dani'}
I welcome any suggestions. If it can do it with chaining, without the extra variable for joinedUsers it would be even better!
There's no need for joinedUsers. Just use find() or some() in the filter() callback, and invert the test.
availableUsers = availableUsers.filter(user => !allUsers.some(u => u.id == user.id))
if users are uniquely identified by id you can use just a filter with a Set of known users:
let availablеUsers = [{id:1,name:'Dani'}, {id:2,name:'Ani'}, {id:3,name:'Marta'}]
let allUsers = [{id:2,name:'Ani'},{id:10,name:'John'}, {id:3,name:'Marta'}]
let joinedUsers = availablеUsers.filter(
function ({id}) {
return this.has(id);
},
new Set(allUsers.map(({id}) => id))
);
Accordingly, you can use the same to update availablеUsers in one go:
availablеUsers = availablеUsers.filter(
function ({id}) {
return !this.has(id);
},
new Set(allUsers.map(({id}) => id))
);
it's not super clear why or when you need !== vs === but the concept is: use a set and use filter instead of map when you want to filter + a Set works harder while constructed but it's blazing fast while used via has()

How to search for multiple words on a HTML page using JavaScript?

Right now my code is looking for the words 'Cheese' or 'Bread' within a specific webpage, and if it finds either word it should display an alert. However, it only displays the alert if the first word is found (cheese). Any suggestions on how to fix it so that it will successfully look for more than one word?
var array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.includes('Cheese', 'Bread'));
if (array){
alert("Word found!")
}
This is an obvious change, but we could put an OR operator inside of the statement to signify both of them, like so:
let array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.includes('Cheese') || el.innerText.includes('Bread'));
if (array) alert('Word found!');
You could also do it a more elegant way, like so:
const conditions = ['Cheese', 'Bread'];
const array = Array.from(document.getElementsByClassName('wide-content-host'));
const results = array.find((el) => conditions.some(nEl => el.innerText.includes(nEl)));
if (results) alert('Word found!');
This one works by grabbing the array from the 'wide-content-host' class name, then looping through that array with another loop that is looping through the values of the conditions array. With all of these loops working together, it will check whether or not the elements include the conditions.
** Edit **
In order to make the methods work without case-sensitivity, you would need to make the search cases lowercase e.g. 'cheese' and 'bread', and you would need to make the strings that you are searching through completely lowercase also.
Here are the examples for case-insensitivity:
let array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.toLowerCase().includes('Cheese') || el.innerText.toLowerCase().includes('Bread'));
if (array) alert('Word found!');
or
const conditions = ['cheese', 'bread'];
const array = Array.from(document.getElementsByClassName('wide-content-host'));
const results = array.find((el) => conditions.some(nEl => el.innerText.toLowerCase().includes(nEl)));
if (results) alert('Word found!');
This can be done with regular expressions
let elem = document.querySelector("section");
let entries = elem.innerHTML.match(/(cheese)|(bread)/gi);
if (entries.length > 0) {
alert(`Words were found: ${entries}`);
}
<section>
<p>Cheese Bread</p>
<p>cheeSE BREAD</p>
</section>

Work with returning Neo4j nodes in a javascript project

in my javascript project i use long cypher queries. The beginning of the queries are quite similar. Can i use the node return of one function in a new querie somehow? I use neo4j-driver and the community neo4j version. To simplify my problem i changed the querie in my example.
const doSomething1 = async() =>{
let query = [
'MATCH (person:Person)',
'RETURN person'
].join('\n');
let _person = await session.run(query,{});
return _person;
};
const doSomething2 = async() =>{
let _person = await doSomething1();
let query = [
'WITH {_testPerson} as _testPerson',
'WHERE _testPerson.age = 18',
'RETURN person'
].join('\n');
let _resultTestPerson = await session.run(query,{
_testPerson: _person,
});
return _resultTestPerson;
};
I expect that the "doSomething2" function will return all nodes that are age 18.
Try looking into the UNWIND operator: it takes an array and lets you run a query on each element in it. Your second query might look something like this:
UNWIND {arrayOfPeople} AS person
WHERE person.age = 18
RETURN person
On a side note, the example you gave could be written as one pretty simple query. Maybe your actual problem could be solved in just one query:
MATCH (p:Person)
WHERE p.age = 18
RETURN p
Since doSomething1 already got all the Person nodes, doSomething2 does not need to make another neo4j query.
Instead, doSomething2 should just iterate through the doSomething1 results and filter for the nodes of interest.

Search Function on AngularFireList in Ionic

I am having a bit of trouble wrapping my head around getting this search function to work. I have it setup right now so it will get the right item when I search it but it's spelling has to be exact including capitalization and punctuation. I want it to be able to get the item regardless of the users search term's capitalization and if they just typed the letter 'b' it will include all items that have a 'b' in the items fields.
I know that I want to query the call to the database since it would be quite heavy to do it on the client side but what do you guys think or how would you go about achieving this?
setFilteredItems() {
this.employeeListRef = this.database.list('userProfile',
ref=> ref.orderByChild('lastName'));
this.employeeList = this.employeeListRef.snapshotChanges()
.map(
changes => {
return changes.map(c => ({
key: c.payload.key, ...c.payload.val()
}))
}
);
//if searchterm is null it returns so it can set back the list to all values
//searchterm is declared in constructor
if(!this.searchTerm) {
return;
}
//var term = this.searchTerm.toLowerCase();
this.employeeListRef = this.database.list('userProfile',
ref => ref.orderByChild('lastName').equalTo(term));
this.employeeList = this.employeeListRef.snapshotChanges()
.map(
changes => {
return changes.map(c => ({
key: c.payload.key, ...c.payload.val()
}))
}
);
}
If you look at the ASCII table you can get a good idea of how Firebase stores it's records and why orderByChild might not work as you expect.
b is 98 and B is 66. Their in different positions on the ASCII table.
There are two things you can try to help you access the data in the expression you want.
Try converting the searchable data to lowercase with the user of database methods
Use a cloud function and on-write of a record, save a lowercase version of that record in the object, then search by that record. An example would be;
{ lastName: 'Smith', lowercaseLastName: 'smith' }
You then orderByChild('lowercaseLastName').

Dynogels: Query using OR comparison

I am trying to optimise the following scans into a single scan (or query). The only way I see is to use to use a OR comparator using DynamoDB. I am using dynogels (fork of vogels) in my application but sadly I am not aware of any OR query functionality in there.
let arrivals = yield Reservation.scan()
.where('room').equals(room)
.where('arrival').between(from, to)
.execAsync().then((reply) => reply.Items.map((item) => item.get()));
let departures = yield Reservation.scan()
.where('room').equals(room)
.where('departure').between(from, to)
.execAsync().then((reply) => reply.Items.map((item) => item.get()));
let combined = arrivals.concat(departures);
return Promise.resolve(combined);
Proposed optimisation:
return Reservation.scan()
.where('room').equals(room)
.where('arrival').between(from, to)
.or.where('departure').between(from, to)
.execAsync().then((reply) => reply.Items.map((item) => item.get()));
The scans get me the reservations that end (departure) or start (arrival) (or both) in a specified date range (from, to).
I think this can be achieved using filterexpression.
Sample code using filterexpression:-
const scanItem = Reservation.scan().filterExpression('room = :idVal AND ((arrival BETWEEN :arrDateVal1 AND :arrDateVal2) OR (departure BETWEEN :depDateVal1 AND :depDateVal2)) ')
.expressionAttributeValues({ ':idVal' : '2', ':arrDateVal1' : '21-APR-2017', ':arrDateVal2' : '23-APR-2017'
,':depDateVal1' : '21-APR-2017', ':depDateVal2' : '23-APR-2017'});
scanItem.exec((err, result) => {if(!err) {console.log(JSON.stringify(result,undefined, 2))}});
Note:-
Not sure whether you wanted to specifically use where rather than filterexpression (or) just wanted to achieve the result regardless of where or filterexpression.

Categories