@depax/dql
v1.0.2
Published
A simple string Query Language parser.
Downloads
8
Readme
DepaxQL
Installation
Install the package normally using NPM or Yarn.
yarn add @depax/dql
Usage
import dql from "@depax/dql";
const query = dql("fields(a,b),eq(a,c)");
// Or with parameters;
const query = dql("fields($2,$3),$1($2,c)", [ "eq", "a", "b" ]);
Converters
Converters provide a means to typecast a value, for example true
could be a string or a boolean, so we have converters
which will attempt to correctly typecast. There are two types of converters, auto
and callback
.
Auto converters
Several auto converters have been defined to try and assume what the value should be. We have an auto converter for
true
so we can search for this and if we see a value true
then it will get converted to the boolean value of true
.
-Infinity
=> (number) -InfinityInfinity
=> (number) Infinitytrue
=> (boolean) truefalse
=> (boolean) falsenull
=> nullundefined
=> undefined
Manual converters
Manual converters are down to the query to apply, so in the above example if we want the string
version of true
we
would do something like eq(field,string:true)
.
Note the string:
, this is the name of the converter followed by a :
then the value.
There are several converters defined;
auto
This is the callback used to perform the auto conversion,bool
andboolean
Boolean converters, this will attempt to convert any of true, yes, 1, on to true, otherwise the value will be false,date
This will try to convert a number or string into a date object,number
This will attempt to convert the value to a number, if it fails it will use0
,re
andregex
This will convert the value to a RegExp object,rei
andregexi
This will convert the value to a RegExp object and apply thei
flag,string
This will convert the value into a string,
Operators
A query string is made up of different operators, there are the comparitors
for comparing a fields value, logical
for combine operators in logic groups such as and
and or
, and modifiers
used for modifiying the results (such as
sorting, limiting, etc).
These are combined using a comma delimited URI encoded string, for example;
fields(displayName,email),and(eq(status,true),eq(email,re:@gmail.com$)),sort(+displayName)
This query is to get the fields displayName
and email
from all users who have a status
of true
and their email
ends with @gmail.com
, and finally should be sorted by their displayName
in ascending
order.
Comparitors
Comparitor operators (with the exception to in
and nin
) take in two arguments, the first being the name of the field
and the second being the value. With in
and nin
the first is the field and every other argument is added into an
array.
eq
: Equals - eq(fieldname,value)ne
: Not Equals - ne(fieldname,value)lt
: Lower than - lt(fieldname,value)lte
: Lower than or Equal to - lte(fieldname,value)gt
: Greater than - gt(fieldname,value)gte
: Greater than or Equal to - gte(fieldname,value)in
: In - in(fieldname,a,b,c,d)nin
: Not In - nin(fieldname,a,b,c,d)
Logical
Logical operators can only contain either comparitors
or other logical
operators
and
: And - and(eq(a,b),eq(b,c))not
: Not - not(eq(a,b),eq(b,c))or
: Or - or(eq(a,b),eq(b,c))nor
: Not Or - nor(eq(a,b),eq(b,c))
Modifiers
Modifiers provide a means to alter the query, so in the form of limit
we can return 25
documents, with an optional
offset, or tell the query to return selected fields
, etc.
fields
: Select Fields - fields(a,b,c,d)limit
: Limit and Offset - limit(25) or limit(25,5)sort
: Sort - sort(a,+b,-c)from
: From Table/Collection - from(users)
Parameter Tokens
The parser allows for taking in an array of parameter tokens, these can be used for operator names or arguments, the
tokens are prefixed with a $
and then a number, this is the index value of the parameters array starting from 1
.
For example;
const query = dql("fields($2,$3),$1($2,c)", [ "eq", "a", "b" ]);
will result in a query string like fields(a,b),eq(a,c)
as $1
is replaced with eq
, $2
is replaced with a
and
$3
is replaced with b
.
Extending
Additional comparitors
, logical
and modifiers
operators and converters
can be added or altered to suit some
requirements.
Comparitor operator
A comparitor
is a callback which outputs the field name and the required value, for example the callback for the eq
operator is;
function (args: string, params: TParams): any[] {
let [ field, value ] = args.split(",");
field = ParseParamTokens(field, params);
value = ParseParamTokens(value, params);
return [ field, { $eq: value } ];
}
The function will take in a string
argument args
, this is the arguments passed into the operator, e.g. for
eq(a,b)
the args
value would be a,b
. The second argument params
is the param tokens passed into the parser and
is used for token replacement.
In the above function we simply split the args
argument by ,
, the first element is the name of the field and the
second is the value. We are using a function called ParseParamTokens
, this takes in a value and the params and will
attempt to find a matching token if relevant, and perform any relevant converters on it.
We then return an array, the first argument being the name of the field, and the second being the value of that field,
so if we return [ field, { $eq: value } ]
, this will be added on to the query object like query.selectors[result[0]] = result[1]
so
then we would have something like;
{
selectors: {
a: { $eq: "b" }
}
}
To add your new comparitor, it just needs to be added to the internal comparitors collection object;
import { ComparisonOperators, TParams } from "@depax/dql";
ComparisonOperators.myop = (args: string, params: TParams): any[] => {
...
};
You could also override or delete a comparitor by;
// Override.
ComparisonOperators.eq = (args: string, params: TParams): any[] => {
...
});
// Delete.
delete ComparisonOperators.eq;
// Rename a comparitor.
ComparisonOperators.myop = ComparisonOperators.eq;
delete ComparisonOperators.eq;
Logical operator
Logical operators are just simple string matches, e.g. and
is added to a $and
property, these values are stored in
the LogicalOperators
object, and can be added to, altered or deleted;
import { LogicalOperators } from "@depax/dql";
LogicalOperators.myand = "$and";
// Delete.
delete LogicalOperators.and;
The parser simply converts the value of the const to a property name, so for example;
LogicalOperators.and = "$and";
// ....
query.selectors[LogicalOperators[match]] = args;
which would result in something like;
{
selectors: {
$and: { ... }
}
}
Modifier operators
Modifiers user callbacks which can directly manipulate the query object, and again are defined on a const called
ModifierOperators
.
A callback would look something like;
import { IQuery, ModifierOperators, ParseParamTokens, TParams } from "@depax/dql";
ModifierOperators.mymodifier = (args: string, params: TParams, query: IQuery): void => {
if (!query.fields) {
query.fields = [];
}
args.split(",")
.forEach((field: string) => {
query.fields.push(ParseParamTokens(field, params));
});
};
Again, like before, they can be added, deleted or altered by using the ModifierOperators
const;
// Override.
ModifierOperators.from = (args: string, params: TParams, query: IQuery): void => {
...
});
// Delete.
delete ModifierOperators.from;
// Rename.
ModifierOperators.table = ModifierOperators.from;
delete ModifierOperators.from;
Converters
Additional converters can be added, or existing ones be deleted or altered as above and will depend on which type of converter is being added/deleted/altered.
Auto converter
To add/delete/alter an auto converter, we use the AutoConverters
const object, and we simply use a value repacement,
for example;
import { AutoConverters } from "@depax/dql";
AutoConverters["undefined"] = undefined;
// This will convert the string "undefined" to the undefined reference.
We could also use this to put in a form of variable replacement;
AutoConverters.hw = "hello";
This will then convert eq(a,hw)
into eq(a,hello)
.
Manual converter
Manual converters are callbacks defined within the Converters
const object, this simply gets given a value and is
expected to return a value, for example the boolean
converter is as follows;
function (value: any): boolean => {
return [
"true", "yes", 1, "1", "on",
].indexOf(value) > -1;
}
So it recieves a value, and returns true if the value is either of true
, yes
, 1
or on
, otherwise it returns
false.
Converters can be added as follows;
import { Converters } from "@depax/dql";
Converters.myconverter = (value: any): any => {
return `hello ${value}`;
};
This example will then convert eq(a,myconverter:world)
to eq(a,hello world)
.