npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@depax/dql

v1.0.2

Published

A simple string Query Language parser.

Downloads

8

Readme

DepaxQL

CircleCI Todos Features Coverage Documentation Report

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) -Infinity
  • Infinity => (number) Infinity
  • true => (boolean) true
  • false => (boolean) false
  • null => null
  • undefined => 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 and boolean 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 use 0,
  • re and regex This will convert the value to a RegExp object,
  • rei and regexi This will convert the value to a RegExp object and apply the i 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).