Validating the query parameter and parsing it using regular expression - regex

I am new to regex, can you please tell me how to take a query parameter with all the below combinations.
(ParamName=Operator:ParamValue) is my set of query parameter value. This will be separated with ;(AND) or ,(OR) and i want to group them within braces. Like in below example
Ex: http://,host:port>/get?search=(date=gt:2020-02-06T00:00:00.000Z;(name=eq:Test,department=co:Prod))
Here the date should be greater than 2020-02-06 and name = Test or department contains Prod.
How to parse these query parameters. Please suggest.
Thanks, Vijay

So, I wrote a solution in JavaScript, but it should be adaptable in other languages as well, with a bit of research.
It's quite a bit of code, but what you're looking to achieve is not super easy!
So here's the code bellow, it's thoroughly commented, but please, if you there is something you don't understand, ask away, and I'll be happy to answer you :)
//
// The 2 first regexes are a parameter, which looks like date=gt:2020-02-06T00:00:00.000Z for example.
// The difference between those 2 is that the 1st one has **named capture group**
// For example '(?<operator>...)' is a capture group named 'operator'.
// This will come in handy in the code, to keep things clean
//
const RX_NAMED_PARAMETER = /(?:(?<param>\w+)=(?<operator>\w+):(?<value>[\w-:.]+))/
const parameter = "((\\w+)=(\\w+):([\\w-:.]+)|(true|false))"
//
// The 3rd parameter is an operation between 2 parameters
//
const RX_OPERATION = new RegExp(`\\((?<param1>${parameter})(?:(?<and_or>[,;])(?<param2>${parameter}))?\\)`, '');
// '---------.---------' '-------.------' '----------.---------'
// 1st parameter AND or OR 2nd parameter
my_data = {
date: new Date(2000, 01, 01),
name: 'Joey',
department: 'Production'
}
/**
* This function compates the 2 elements, and returns the bigger one.
* The elements might be dates, numbers, or anything that can be compared.
* The elements **need** to be of the same type
*/
function isGreaterThan(elem1, elem2) {
if (elem1 instanceof Date) {
const date = new Date(elem2).getTime();
if (isNaN(date))
throw new Error(`${elem2} - Not a valid date`);
return elem1.getTime() > date;
}
if (typeof elem1 === 'number') {
const num = Number(elem2);
if (isNaN(num))
throw new Error(`${elem2} - Not a number`);
return elem1 > num;
}
return elem1 > elem2;
}
/**
* Makes an operation as you defined them in your
* post, you might want to change that to suit your needs
*/
function operate(param, operator, value) {
if (!(param in my_data))
throw new Error(`${param} - Invalid parameter!`);
switch (operator) {
case 'eq':
return my_data[param] == value;
case 'co':
return my_data[param].includes(value);
case 'lt':
return isGreaterThan(my_data[param], value);
case 'gt':
return !isGreaterThan(my_data[param], value);
default:
throw new Error(`${operator} - Unsupported operation`);
}
}
/**
* This parses the URL, and returns a boolean
*/
function parseUri(uri) {
let finalResult;
// As long as there are operations (of the form <param1><; or ,><param2>) on the URL
while (RX_OPERATION.test(uri)) {
// We replace the 1st operation by the result of this operation ("true" or "false")
uri = uri.replace(RX_OPERATION, rawOperation => {
// As long as there are parameters in the operations (e.g. "name=eq:Bob")
while (RX_NAMED_PARAMETER.test(rawOperation)) {
// We replace the 1st parameter by its value ("true" or "false")
rawOperation = rawOperation.replace(RX_NAMED_PARAMETER, rawParameter => {
const res = RX_NAMED_PARAMETER.exec(rawParameter);
return '' + operate(
res.groups.param,
res.groups.operator,
res.groups.value,
);
// The "res.groups.xxx" syntax is allowed by the
// usage of capture groups. See the top of the file.
});
}
// At this point, the rawOperation should look like
// (true,false) or (false;false) for example
const res = RX_OPERATION.exec(rawOperation);
let operation;
if (res.groups.param2 === undefined)
operation = res.groups.param1; // In case this is an isolated operation
else
operation = res.groups.param1 + ({',': ' || ', ';': ' && '}[res.groups.and_or]) + res.groups.param2;
finalResult = eval(operation);
return '' + finalResult;
});
}
return finalResult;
}
let res;
res = parseUri("http://,host:port>/get?search=(date=gt:2020-02-06T00:00:00.000Z;(name=eq:Test,department=co:Prod))");
console.log(res);
res = parseUri("http://,host:port>/get?search=(date=lt:2020-02-06T00:00:00.000Z)");
console.log(res);

Related

Variable assignment based on condition

I try to assign a PathBuf result to variable based on a condition:
// USE_OLD_DATA set at compilation time to FALSE or TRUE
let root = env::current_dir().unwrap();
let processed_files_location = if USE_OLD_DATA {
root.join("Result Data 10-28-2020");
} else {
root.join("Result Data");
};
after this ... the processed_files_location = () ?!
Since conditionals are expressions, you can just pass the expression to your join method:
let processed_files_location =
root.join(
if USE_OLD_DATA {
"Result Data 10-28-2020"
} else {
"Result Data"
}
);
And, as pointed out in the comments, when you terminate your branch expressions with ;, it evaluates to the unit tuple () rather than the type you're expecting.

Convert a string into closure in ColdFusion

I have a very basic question. Is it possible to convert a string into a closure? I tried evaluate() but it didn't work.
evaluate( "myFunction = function(val){ return dollarFormat( val ); }" );
What I have in mind is to save custom functions in the database as string and then run it as needed.
Thank you!
Edit: Just to clarify: I want to be able to save "function(val){ return dollarFormat( val ); }" as a string in database and be able to convert it into a functioning closure.
I would go with user2943775 answer:
<cfscript>
FileWrite("/ram/UDFs.cfm", "<cfset myFunction = function(val){ return dollarFormat( val ); }>")
include template="/ram/UDFs.cfm";
writedump(myFunction(10));
</cfscript>
And in your Application.cfc
component {
this.mappings["/ram"] = "ram://";
...
}
I came across a similar solution, though I was unable to use the in-memory filesystem due to security restrictions. In my Application.cfc, I added the following mapping:
this.mappings = {
"/models" = "#APP_ROOT_PATH#cfcs/models",
"/utils" = "#APP_ROOT_PATH#cfcs/utils",
"/modules" = "#APP_ROOT_PATH#_modules",
"/components" = "#APP_ROOT_PATH#cfcs",
"/udfs" = "#APP_ROOT_PATH#includes/udfs" // path for global (and temporary) UDFs
};
The UDF I created is as follows:
/**
* Takes a string representation of a function and returns it as a Closure
* #output false
* #return Closure
*/
private any function toClosure (required string closure) {
local.id = replace(createUUID(), "-", "", "all");
local.udfpath = "/udfs/udf#id#.cfm";
local.script = "<cfscript>local.fn#id# = #closure#;</cfscript>";
try {
fileWrite(expandPath(udfPath), script);
include udfpath;
} catch (any e) {
} finally {
try {
fileDelete(expandPath(udfPath));
} catch (any e) {}
}
if (!structkeyExists(local, "fn#id#") || !isClosure(local["fn#id#"])) {
throw (message="Unable to compile closure");
}
// return the closure
return local["fn#id#"];
}
And the result:
myFn = toClosure("function (num) { return num + 1; }");
myFn(1); // returns 2

Number Greater Than With Float Point

I am fairly new to regular expressions and I am having a time getting one that I have formulated to work correctly. Below is the expression that I have formulated.
^((?!^1[0-6]*\.?[0-2][0-4]+)^(\d+))$
I am trying to build an expression that will verify a number greater than 16.24. The input needs to be able to accept whole numbers like 17 without a user having to put in 17.00 to verify. Any ideas of what I am doing wrong?
One way you could do this would be to use a regular expression to extract the numeric values then parse them to a number and compare them with the desired constant.
Javascript: Code Example
Numbers as strings:
var test = function(str){
return 16.24 < parseFloat(str);
};
console.log( test("234.23") == true ); // true
console.log( test("-234.23") == false ); // true
Numbers hidden within strings with other characters.
var test = function (str) {
var re,
num;
if (/[eE]/.test(str)) {
// search for scientific numbers
re = /-?\d+(\.\d+)?[eE]\d+/;
} else {
// search for whole or decimal numbers
re = /-?\d+(\.\d{1,2})?/;
}
num = str.match(re);
return 16.24 < parseFloat(num);
};
console.log(test("input = 234.23") == true); // true
console.log(test("input = 2e34") == true); // true
console.log(test("input = -2e34") == false); // true
console.log(test("input = -234.23") == false); // true

Generate all matches from a subset of regex

I need to define a bunch of vector sequences, which are all a series of L,D,R,U for left, down, right, up or x for break. There are optional parts, and either/or parts. I have been using my own invented system for noting it down, but I want to document this for other, potentially non-programmers to read.
I now want to use a subset (I don't plan on using any wildcards, or infinite repetition for example) of regex to define the vector sequence and a script to produce all possible matching strings...
/LDR/ produces ['LDR']
/LDU?R/ produces ['LDR','LDUR']
/R(LD|DR)U/ produces ['RLDU','RDRU']
/DxR[DL]U?RDRU?/ produces ['DxRDRDR','DxRDRDRU','DxRDURDR','DxRDURDRU','DxRLRDR','DxRLRDRU','DxRLURDR','DxRLURDRU']
Is there an existing library I can use to generate all matches?
EDIT
I realised I will only be needing or statements, as optional things can be specified by thing or nothing maybe a, or b, both optional could be (a|b|). Is there another language I could use to define what I am trying to do?
By translating the java code form the link provided by #Dukeling into javascript, I think I have solved my problem...
var Node = function(str){
this.bracket = false;
this.children = [];
this.s = str;
this.next = null;
this.addChild = function(child){
this.children.push(child);
}
}
var printTree = function(root,prefix){
prefix = prefix.replace(/\./g, "");
for(i in root.children){
var child = root.children[i]
printTree(child, prefix + root.s);
}
if(root.children.length < 1){
console.log(prefix + root.s);
}
}
var Stack = function(){
this.arr = []
this.push = function(item){
this.arr.push(item)
}
this.pop = function(){
return this.arr.pop()
}
this.peek = function(){
return this.arr[this.arr.length-1]
}
}
var createTree = function(s){
// this line was causing errors for `a(((b|c)d)e)f` because the `(((` was only
// replacing the forst two brackets.
// var s = s.replace(/(\(|\||\))(\(|\||\))/g, "$1.$2");
// this line fixes it
var s = s.replace(/[(|)]+/g, function(x){ return x.split('').join('.') });
var str = s.split('');
var stack = new Stack();
var root = new Node("");
stack.push(root); // start node
var justFinishedBrackets = false;
for(i in str){
var c = str[i]
if(c == '('){
stack.peek().next = new Node("Y"); // node after brackets
stack.peek().bracket = true; // node before brackets
} else if (c == '|' || c == ')'){
var last = stack.peek(); // for (ab|cd)e, remember b / d so we can add child e to it
while (!stack.peek().bracket){ // while not node before brackets
stack.pop();
}
last.addChild(stack.peek().next); // for (b|c)d, add d as child to b / c
} else {
if (justFinishedBrackets){
var next = stack.pop().next;
next.s = "" + c;
stack.push(next);
} else {
var n = new Node(""+c);
stack.peek().addChild(n);
stack.push(n);
}
}
justFinishedBrackets = (c == ')');
}
return root;
}
// Test it out
var str = "a(c|mo(r|l))e";
var root = createTree(str);
printTree(root, "");
// Prints: ace / amore / amole
I only changed one line, to allow more than two consecutive brackets to be handled, and left the original translation in the comments
I also added a function to return an array of results, instead of printing them...
var getTree = function(root,prefix){
this.out = this.out || []
prefix = prefix.replace(/\./g, "");
for(i in root.children){
var child = root.children[i]
getTree(child, prefix + root.s, out);
}
if(root.children.length < 1){
this.out.push(prefix + root.s);
}
if(!prefix && !root.s){
var out = this.out;
this.out = null
return out;
}
}
// Test it
var str = "a(b|c)d";
var root = createTree(str);
console.log(getTree(root, ""));
// logs ["abd","acd"]
The last part, to allow for empty strings too, so... (ab|c|) means ab or c or nothing, and a convenience shortcut so that ab?c is translated into a(b|)c.
var getMatches = function(str){
str = str.replace(/(.)\?/g,"($1|)")
// replace all instances of `(???|)` with `(???|µ)`
// the µ will be stripped out later
str = str.replace(/\|\)/g,"|µ)")
// fix issues where last character is `)` by inserting token `µ`
// which will be stripped out later
str = str+"µ"
var root = createTree(str);
var res = getTree(root, "");
// strip out token µ
for(i in res){
res[i] = res[i].replace(/µ/g,"")
}
// return the array of results
return res
}
getMatches("a(bc|de?)?f");
// Returns: ["abcf","adef","adf","af"]
The last part is a little hack-ish as it relies on µ not being in the string (not an issue for me) and solves one bug, where a ) at the end on the input string was causing incorrect output, by inserting a µ at the end of each string, and then stripping it from the results. I would be happy for someone to suggest a better way to handle these issues, so it can work as a more general solution.
This code as it stands does everything I need. Thanks for all your help!
I'd imagine what you're trying is quite easy with a tree (as long as it's only or-statements).
Parse a(b|c)d (or any or-statement) into a tree as follows: a has children b and c, b and c have a mutual child d. b and c can both consist of 0 or more nodes (as in c could be g(e|f)h in which case (part of) the tree would be a -> g -> e/f (2 nodes) -> h -> d or c could be empty, in which case (part of) the tree would be a -> d, but an actual physical empty node may simplify things, which you should see when trying to write the code).
Generation of the tree shouldn't be too difficult with either recursion or a stack.
Once you have a tree, it's trivial to recursively iterate through the whole thing and generate all strings.
Also, here is a link to a similar question, providing a library or two.
EDIT:
"shouldn't be too difficult" - okay, maybe not
Here is a somewhat complicated example (Java) that may require some advanced knowledge about stacks.
Here is a slightly simpler version (Java) thanks to inserting a special character between each ((, )), |(, etc.
Note that neither of these are particularly efficient, the point is just to get the idea across.
Here is a JavaScript example that addresses parsing the (a|b) and (a|b|) possibilities, creates an array of possible substrings, and composes the matches based on this answer.
var regex = /\([RLUD]*\|[RLUD]*\|?\)/,
str = "R(LD|DR)U(R|L|)",
substrings = [], matches = [], str_tmp = str, find
while (find = regex.exec(str_tmp)){
var index = find.index
finds = find[0].split(/\|/)
substrings.push(str_tmp.substr(0, index))
if (find[0].match(/\|/g).length == 1)
substrings.push([finds[0].substr(1), finds[1].replace(/.$/, '')])
else if (find[0].match(/\|/g).length == 2){
substrings.push([finds[0].substr(1), ""])
substrings.push([finds[1], ""])
}
str_tmp = str_tmp.substr(index + find[0].length)
}
if (str_tmp) substrings.push([str_tmp])
console.log(substrings) //>>["R", ["LD", "DR"], "U", ["R", ""], ["L", ""]]
//compose matches
function printBin(tree, soFar, iterations) {
if (iterations == tree.length) matches.push(soFar)
else if (tree[iterations].length == 2){
printBin(tree, soFar + tree[iterations][0], iterations + 1)
printBin(tree, soFar + tree[iterations][1], iterations + 1)
}
else printBin(tree, soFar + tree[iterations], iterations + 1)
}
printBin(substrings, "", 0)
console.log(matches) //>>["RLDURL", "RLDUR", "RLDUL", "RLDU", "RDRURL", "RDRUR", "RDRUL", "RDRU"]

Fuzzy Matches on dijit.form.ComboBox / dijit.form.FilteringSelect Subclass

I am trying to extend dijit.form.FilteringSelect with the requirement that all instances of it should match input regardless of where the characters are in the inputted text, and should also ignore whitespace and punctuation (mainly periods and dashes).
For example if an option is "J.P. Morgan" I would want to be able to select that option after typing "JP" or "P Morgan".
Now I know that the part about matching anywhere in the string can be accomplished by passing in queryExpr: "*${0}*" when creating the instance.
What I haven't figured out is how to make it ignore whitespace, periods, and dashes. I have an example of where I'm at here - http://jsfiddle.net/mNYw2/2/. Any help would be appreciated.
the thing to master in this case is the store fetch querystrings.. It will call a function in the attached store to pull out any matching items, so if you have a value entered in the autofilling inputfield, it will eventually end up similar to this in the code:
var query = { this.searchAttr: this.get("value") }; // this is not entirely accurate
this._fetchHandle = this.store.query(query, options);
this._fetchHandle.then( showResultsFunction );
So, when you define select, override the _setStoreAttr to make changes in the store query api
dojo.declare('CustomFilteringSelect', [FilteringSelect], {
constructor: function() {
//???
},
_setStoreAttr: function(store) {
this.inherited(arguments); // allow for comboboxmixin to modify it
// above line eventually calls this._set("store", store);
// so now, 'this' has 'store' set allready
// override here
this.store.query = function(query, options) {
// note that some (Memory) stores has no 'fetch' wrapper
};
}
});
EDIT: override queryEngine function as opposed to query function
Take a look at the file SimpleQueryEngine.js under dojo/store/util. This is essentially what filters the received Array items on the given String query from the FilteringSelect. Ok, it goes like this:
var MyEngine = function(query, options) {
// create our matching query function
switch(typeof query){
default:
throw new Error("Can not query with a " + typeof query);
case "object": case "undefined":
var queryObject = query;
query = function(object){
for(var key in queryObject){
var required = queryObject[key];
if(required && required.test){
if(!required.test(object[key])){
return false;
}
}else if(required != object[key]){
return false;
}
}
return true;
};
break;
case "string":
/// HERE is most likely where you can play with the reqexp matcher.
// named query
if(!this[query]){
throw new Error("No filter function " + query + " was found in store");
}
query = this[query];
// fall through
case "function":
// fall through
}
function execute(array){
// execute the whole query, first we filter
var results = arrayUtil.filter(array, query);
// next we sort
if(options && options.sort){
results.sort(function(a, b){
for(var sort, i=0; sort = options.sort[i]; i++){
var aValue = a[sort.attribute];
var bValue = b[sort.attribute];
if (aValue != bValue) {
return !!sort.descending == aValue > bValue ? -1 : 1;
}
}
return 0;
});
}
// now we paginate
if(options && (options.start || options.count)){
var total = results.length;
results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
results.total = total;
}
return results;
}
execute.matches = query;
return execute;
};
new Store( { queryEngine: MyEngine });
when execute.matches is set on bottom of this function, what happens is, that the string gets called on each item. Each item has a property - Select.searchAttr - which is tested by RegExp like so: new RegExp(query).test(item[searchAttr]); or maybe a bit simpler to understand; item[searchAttr].matches(query);
I have no testing environment, but locate the inline comment above and start using console.debug..
Example:
Stpre.data = [
{ id:'WS', name: 'Will F. Smith' },
{ id:'RD', name:'Robert O. Dinero' },
{ id:'CP', name:'Cle O. Patra' }
];
Select.searchAttr = "name";
Select.value = "Robert Din"; // keyup->autocomplete->query
Select.query will become Select.queryExp.replace("${0]", Select.value), in your simple queryExp case, 'Robert Din'.. This will get fuzzy and it would be up to you to fill in the regular expression, here's something to start with
query = query.substr(1,query.length-2); // '*' be gone
var words = query.split(" ");
var exp = "";
dojo.forEach(words, function(word, idx) {
// check if last word
var nextWord = words[idx+1] ? words[idx+1] : null;
// postfix 'match-all-but-first-letter-of-nextWord'
exp += word + (nextWord ? "[^" + nextWord[0] + "]*" : "");
});
// exp should now be "Robert[^D]*Din";
// put back '*'
query = '*' + exp + '*';