NodeJS Query with MongoDB - node.js

I want to use add regex to my query in mongodb.
server.js
app.post('/form',function(req,res){
var tf = req.body.termFound;
var str1 ='"keyword":{$regex:/'+tf+'/i}';
db.collection('a').find({str1}).toArray(function (err,results){
if (err) return console.log(err)
res.render('stest.ejs',{arr:results})
});
For example if the termFound is LOOPS.
I wish to add the regex so that it will become non case sensitive, allowing me to find field values with (loops) or (loops,java)
"keyword":{$regex:/LOOPS/i}
Which is done from the line 3 code above.
However when i try to send it to my mongoDB for connection. I suspect it gives a different value. As i tried
var str1 ='"keyword":{$regex:/'+tf+'/i}';
var str3 ={str1};
res.send(str3);
i was return with
{"str1":"\"keyword\":{$regex:/LOOPS/i}"}
My final query should be something like
db.collection('a').find({"keyword":$regex:/LOOPS/i}).toArray(function (err,results)
I have experimented with JSON.parse(str1)
var str1 ='"keyword":{$regex:/'+tf+'/i}';
var filters = JSON.parse(str1);
db.collection('a').find(filters).toArray(function (err,results){
but was given
SyntaxError: Unexpected token : in JSON at position 9
at JSON.parse ()
I have spent days trying to fix this.. my question is how can i query do this with regex so as to make my search non case sensitive?

Edit: Managed to find out the answer. Had to use new RegExp() to create the reg exp object!
app.post('/form',function(req,res){
var tf=req.body.toFind;
db.collection('a').find({"keyword":new RegExp(tf,'i')}).toArray(function (err,results){
if (err) return console.log(err)
res.render('stest.ejs',{question:results})
})
});

You are so close here. Instead of trying to pass a string to your MongoDB find as you have above, just pass in the regex.
This way, you can use a template string or concatenate strings (such as '/'+tf+'/i').
UPDATE This method just mentioned won't work for all the reasons I'm getting ready to explain in the following paragraph regarding the original problem. This doesn't create an actual regular expression.
Previously, when you pass in a single string to the find function, Mongo starts looking for the key "str1" and seeing if it found a value of that string, whatever '"keyword":{$regex:/'+tf+'/i}' would evaluate to as a single string (note the surrounding single-quotes). You can't call JSON parse on this particular string because it is not a valid JSON encoded string. See an example below that should work better:
app.post('/form',function(req,res){
var tf = req.body.termFound;
// var reg =`/${tf}/i`; not an actual regex, still a string!!!
var reg = new RegExp(tf, 'i') // as you have since discovered
db.collection('a').find({"keyword": {$regex: reg }}).toArray(function (err,results){
if (err) return console.log(err)
res.render('stest.ejs',{arr:results})
});
})

Related

Cannot convert object to primitive value when attempting to run word count on array created from Firebase records

I am trying to write a node js program that reads values from a Firebase database and aggregates all the words in a specific field for all the
records, but I am getting the below errors ..
[2019-06-24T14:52:14.083Z] #firebase/database: FIREBASE WARNING: Exception was thrown by user callback. TypeError: Cannot convert object to primitive value at C:\Users\xxx\Projects\NodeProjects\QuestionAppNode\index.js:52:38
TypeError: Cannot convert object to primitive value
Below is my node.js code ..
retrieveQuestions();
function retrieveQuestions(){
userQuestionsFBRef.once("value", function(snapshot) {
var fetchedQuestions = [];
snapshot.forEach(function(snapshotChild){
var itemVal = snapshotChild.val();
fetchedQuestions.push(itemVal);
})
var arrayOfQuestions = [];
fetchedQuestions.forEach(function(question){
arrayOfQuestions += question.question
})
console.log("Fetched questions are " + JSON.stringify(fetchedQuestions));
console.log("arrayOfQuestions is " +JSON.stringify(arrayOfQuestions));
var wordcnt = arrayOfQuestions.replace(/[^\w\s]/g, "").split(/\s+/).reduce(function(map, word){
map[word] = (map[word]||0)+1;
return map;
}, Object.create(null));
console.log("Word count is " + wordcnt)
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
The similar code does work if I run it in Chrome console i.e.
var arrayOfQuestions = [{"dateTime":"2019-06-24T14:06:36.983Z","name":"AMA09","question":"Who let the dogs out?"},{"dateTime":"2019-06-24T14:07:11.501Z","name":"AMA09","question":"What is the capital of Senegal?"},{"dateTime":"2019-06-24T14:20:25.222Z","name":"AMA34","question":"Is Free will an illusion?"}];
var newArray = [];
arrayOfQuestions.forEach(question => newArray += question.question);
var wordcnt = newArray.replace(/[^\w\s]/g, "").split(/\s+/).reduce(function(map, word){
map[word] = (map[word]||0)+1;
return map;
}, Object.create(null));
Does anyone have any idea why this is happening?
I realise that the approach I am taking to aggregate the words in these records is probably not the correct way to go i.e. adding all the text in the
question field of the fb records is probably a bit stupid and wouldn't work for large datasets so if someone could offer any suggestions on a
different approach that would be appreciated as well.
Many thanks.
The problem appears to be this line:
console.log("Word count is " + wordcnt)
Since wordcnt is an object without a prototype, that is, Object.create(null), it has no toString method, hence the error "TypeError: Cannot convert object to primitive value".
Solution 1 - Use object literal syntax in your reduce expression:
var wordcnt = arrayOfQuestions
.replace(/[^\w\s]/g, "")
.split(/\s+/)
.reduce(function(map, word){
map[word] = (map[word]||0)+1;
return map;
}, {}); // Object literal instead of Object.create(null)
This creates an object with the usual Object prototype, which has a toString method.
Solution 2 - Don't concatenate in console.log but rather use multiple arguments:
console.log("Word count is", wordcnt) // instead of " + wordcnt)
This allows console.log to do its normal stringifying of objects.
Solution 3 - Convert the wordcnt map to a json string.
console.log("Word count is " + JSON.stringify(wordcnt))
This converts your object to a JSON representation of itself.

how does mongo determine how to serialize an object?

I'm working with a BigNumbers from the bignumber.js package
I'm new to Mongo and I'm curious as to how Mongo knows how to serialize this object correctly (or any other object for that matter)?
I ask because I have 2 scripts which are seemingly identical in they way the insert these objects, but in one script the BigNumbers are inserted as strings in the other script they are inserted as numbers.
The object comes in from an any stream and I need to convert or cast the any object to Altitude.
Sample code:
export interface Altitude {
altitude: BigNumber;
minAltitude: BigNumber;
maxAltitude: BigNumber;
}
... 'message' object of type any from stream ...
// this JSON back and forth stuff seems like it should be unnecessary
let jsonString = JSON.stringify(message);
let alt = JSON.parse(jsonString) as Altitude;
collection.insert(alt, (error: MongoError, result: InsertOneWriteOpResult) => {
...
});
Strings are inserted for the BigNumbers.
Seeing the values being inserted into Mongo as strings I try forcing the issue even more, just to see if it would work:
let alt = JSON.parse(jsonString) as Altitude;
let alt1 = {
size: alt.altitude as BigNumber,
minAltitude: alt.minAltitude as BigNumber,
maxAltitude: alt.minAltitude as BigNumber,
};
collection.insert(alt1, (error: MongoError, result: InsertOneWriteOpResult) => {
...
});
However the values are still inserted as strings. And in my other script everything is numbers.
Ideas?
MongoDB uses BSON Serialisation. The actual implementation would depend on the driver.
Reference https://www.mongodb.com/json-and-bson

JSON encode in Slim framework

I am having problems sending a JSON encoded array to my view.
So I am doing this in my route
$data['values'] = array('name'=>'John');
$data['values'] = json_encode($data['values']);
return $this->view->render($res, 'githubpresentation.html', $data);
And in my view, in the script tag I do this
var values = "{{values}}" ;
console.log(values);
values = JSON.parse(values);
console.log(values);
The first console.log, before the JSON.parse outputs this:
{"name":"John"}
And when I do the JSON.parse I get an error, of course
Unexpected token & in JSON at position 1
Now I could do some sort of replace of the &quot, but do I really need to? Shouldn't I be able to send a JSON from the server?
This is related to the default escaping strategy. You can fix it by using the js one:
var values = "{{values|e('js')}}" ;
console.log(values);
values = JSON.parse(values);
console.log(values);
Output:
{"name":"John"}
Object {name: "John"}

Knex Query build - build chain dynamically

I've traded node-DBI for knex because it has more function that I require.
So far I'd make the same choice again but only one thing is holding me back: writing abstract methods that take a options variable where params like where, innerjoin and such are contained in.
Using node-dbi I could easily forge a string using these variables but I can't seem to create the knex chain dymanicly because after using a switch, you'd get knex.method is not a function.
Any idea how to resolve this?
I'm looking for something as in
`getData(table,options){
var knex=knex
if(options.select)
/** append the select data using knex.select()
if(options.where)
/** append the where data using knex.where(data)*/
if(options.innerJoin)
/** append innerjoin data*/
}`
This way I can avoid having to write alot of DB functions and let my Business Logical Layers handel the requests
/*This function serves as the core of our DB layer
This will generate a SQL query and execute it whilest returning the response prematurely
#param obj:{Object} this is the options object that contain all of the query options
#return Promise{Object}: returns a promise that will be reject or resolved based on the outcome of the query
The reasoning behind this kind of logic is that we want to abstract our layer as much as possible, if evne the slightest
sytnax change occurs in the near future, we can easily update all our code by updating this one
We are using knex as a query builder and are thus relying on Knex to communicate with our DB*/
/*Can also be used to build custom query functions from a data.service. This way our database service will remain
unpolluted from many different functions and logic will be contained in a BLL*/
/* All available options
var options = {
table:'table',
where:{operand:'=',value:'value',valueToEqual:'val2'},
andWhere:[{operand:'=',value:'value',valueToEqual:'val2'}],
orWhere:[{operand:'=',value:'value',valueToEqual:'val2'}],
select:{value:['*']},
insert:{data:{}},
innerJoin:[{table:'tableName',value:'value',valueToEqual:'val2'}],
update:{data:{}}
}*/
/*Test object*/
/*var testobj = {
table:'advantage',
where:{operand:'>',value:'id',valueToEqual:'3'},
select:{value:['*']},
innerJoin:{table:'User_Advantage',value:'User_Advantage.Advantageid',valueToEqual:'id'}
}
var testobj = {
table:'advantage',
where:{operand:'>',value:'id',valueToEqual:'3'},
select:{value:['*']},
innerJoin:{table:'User_Advantage',value:'User_Advantage.Advantageid',valueToEqual:'id'}
}
queryBuilder(testobj)*/
function queryBuilder(options){
var promise = new Promise(function (resolve, reject) {
var query;
for (var prop in options) {
/*logger.info(prop)*/
if (options.hasOwnProperty(prop)) {
switch (prop) {
case 'table':
query = knex(options[prop]);
break;
case 'where':
query[prop](options[prop].value, options[prop].operand, options[prop].valueToEqual);
break;
/*andWhere and orWhere share the same syntax*/
case 'andWhere':
case 'orWhere':
for(let i=0, len=options[prop].length;i<len;i++){
query[prop](options[prop][i].value, options[prop][i].operand, options[prop][i].valueToEqual);
}
break;
case 'select':
query[prop](options[prop].value);
break;
/*Same syntax for update and insert -- switch fallthrough*/
case 'insert':
case 'update':
query[prop](options[prop].data);
break;
case 'innerJoin':
for(let i=0, len=options[prop].length;i<len;i++){
query[prop](options[prop][i].table, options[prop][i].value, options[prop][i].valueToEqual);
}
break;
}
}
}
return query
.then(function (res) {
return resolve(res);
}, function (error) {
logger.error(error)
return reject(error);
})
return reject('Options wrongly formatted');
});
return promise
}
Thanks to Molda I was able to produce the code above. This one takes a Object called options in as a parameter and will build the knex chain based on this value. See the comments for the syntax of Object
Not every knex query option has been included but this will serve as a good base for anyone trying to achieve a similar effect.
Some examples to use this:
/*Will return all values from a certain table
#param: table{String}: string of the table to query
#param: select{Array[String]}: Array of strings of columns to be select -- defaults to ['*'] */
function getAll(table,select) {
/*Select * from table as default*/
var selectVal=select||['*']
var options={
table:table,
select:{value:selectVal}
}
return queryBuilder(options)
}
or a more specific use case:
function getUserAdvantages(userid){
var options = {
table:'advantage',
innerJoin:[{table:TABLE,value:'advantage.id',valueToEqual:'user_advantage.Advantageid'}],
where:{operand:'=',value:'user_advantage.Userid',valueToEqual:userid}
}
return sqlService.queryBuilder(options)
}
Note: the sqlService is a module of node that I export containing the queryBUilder method.
Edit: I wanted to add that the only roadblock I had was using the .from / .insert from Knex. I no longer use these methods as they resulted in errors when using them. Ive used knex(table) as commented.

Sharepoint > Which characters are *not* allowed in a list?

I've tried looking this up and haven't come up with the answer I'm looking for; I've found what cannot be included in filenames, folder names, and site names... but nothing on actual fields in a list.
I noticed that the percent symbol (%) is one that's not allowed in files/sites/folders. But it also doesn't populate when I try to pro grammatically add the fields to the list. I am doing this by using a small C# application that sends the data via Sharepoint 2010's built-in web services. I can manually enter the character, but it messes up each field in the row if I try it through code.
I've tried some of the escape characters that I've found via Google (_x26), but these don't seem to work either. Has anyone else had an issue with this? If these characters are allowed, how can I escape them when sending the data through a web service call?
Thanks in advance!
Justin
Any characters that aren't allowed when you enter a field name get encoded in the internal name. The format is a little different to what you show - try "_x0026_".
I usually avoid issues with weird internal names by creating the field with no spaces or special characters in the name, then renaming it. When you rename a field, only the display name changes and you keep the simple internal name.
Characters not allowed in SharePoint file name:
~, #, %, & , *, {, }, \, :, <, >, ?, /, |, "
Pasted from http://chrisbarba.com/2011/01/27/sharepoint-filename-special-characters-not-allowed/
Am I saying something weird when I state that there usually is a reason for certain characters not being allowed. I don't know which or why, but there probably is a reason.
Since you control which fields need to be there you can also dictate their (internal) names. I'd say follow best practice and name your fields using Camel case. And because you created them, you can just map the fields to the according fields in your data source.
As a follow on to #Elisa's answer, here's some JavaScript / TypeScript code that helps to prevent users from uploading files into SharePoint with invalid characters in the file name implemented on Nintex forms.
Here's the gist of the JavaScript version (note you'll have to obviously adapt for your own needs since this was designed for Nintex) :
//------------------------------------------------------------------------
//JavaScript Version:
//Code from http://cc.davelozinski.com/tips-techniques/nintex-form-tips-techniques/javascript-typescript-for-nintex-forms-to-validate-file-names
//------------------------------------------------------------------------
function validateAttachmentNames(eventObject) {
var textbox$ = NWF$(this);
var attachrowid = this.id.substring(10, 47);
var fileUploadid = attachrowid;
var index = attachrowid.substring(36);
//console.log('index:' + index);
//console.log('attachrowid:' + attachrowid);
//console.log('fileUploadid:' + fileUploadid);
if (index == '') {
attachrowid += '0';
}
var fileName = NWF.FormFiller.Attachments.TrimWhiteSpaces(textbox$.val().replace(/^.*[\\\/]/, ''));
var match = (new RegExp('[~#%\&{}+\|]|\\.\\.|^\\.|\\.$')).test(fileName);
if (match) {
isValid = false;
setTimeout(function () {
NWF$("tr[id^='attachRow']").each(function () {
var arrParts = (NWF$(this).find(".ms-addnew")[0]).href.split('"');
var fileName = arrParts[5];
var attachRow = arrParts[1];
var fileUpload = arrParts[3];
var match = (new RegExp('[~#%\&{}+\|]|\\.\\.|^\\.|\\.$')).test(fileName);
if (match) {
console.log(fileName);
NWF.FormFiller.Attachments.RemoveLocal(attachRow, fileUpload, fileName);
alert('Invalid file: ' + fileName + ' You cannot attach files with the following characters ~ # % & * { } \ : < > ? / + | \n\nThe file has been removed.');
}
});
}, 500);
}
}

Resources