8

JOQULAR: High Powered JavaScript Pattern Matching

 5 years ago
source link: https://www.tuicool.com/articles/hit/M73Qzmy
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

The pattern matching approach used by MongoDB for querying databases is quite popular, e.g. {name:{$eq:"joe"},age:{$lt:21}} matches anything with the name “joe” and age less than 21. It is powerful and easy to learn but actually somewhat limited in scope. What if you could have 10x the expressiveness? What if you could also have effectively unlimited pattern matching capability that worked on object keys as well as key values? What if the pattern matching language could also do aliasing, computed values, defaults, validation, and partials reduction? JOQULAR (JavaScript Query Language Representation) is a language that does this, and it is just JavaScript objects with special keys.

First, the big extensions key matching, aliasing, computation, defaults, validation and partials reduction are covered. Then, the more than 10 additional predicates are discussed.

Key Matching

Key matching is covered first, because it is the most powerful extension to the MongoDB approach.

The $_ key can be used to match any key, e.g.

{$_:"joe"}

will match any object with a property that has the value “joe”.

Regular expressions can match keys, e.g.

{[/.*ID/]:{$typeof: "string"}}

will match any objects that have property names ending in “ID” where the value for the property is a string.

Even functions can match keys, e.g.

{[(key) => key==="name" || key==="Name"]:{$typeof: "string"}}

will match any objects with either “name” or “Name” properties where the value for the property is a string.

Aliasing

Aliasing in JOQULAR is trivial. Assume there is an object {name:”joe"} , then ${name:{$typeof: "string",as:"Name"}} will result in the return of the object of {Name: "joe"}.

Computed Values

Computed values can even use the rest of an object, e.g.

${name:${typeof: "string"},firstInitial:{$compute: (object,key) => object.name[0]}}

key is unused above, but it is provided as a second argument if you need it. In this case it would be “firstInitial”.

Default Values

Maybe you want to add data that you know might not exist on objects in the database, just use $default like this:

${name:"joe",size:{$default: "medium"}}.

Default values are assigned after computed values, in case the computation returns undefined or null .

Validation

To add validation, just provide a sub-pattern inside a $valid property and all the normal JOQULAR predicates will be available for validation use rather than pattern matching. If something fails an error will be thrown:

{name:{$typeof: “string",$valid:{$:(value) => value.length>0}}.

If you want to catch the error, just pass onError as a property on your $valid object:

{name:{$typeof: “string",$valid:{$:(value) => value.length>0,onError:(error,value,propertyName,containgObject){ ... do something ...}}.

You can also pass a validation function directly, but will need to handle any errors directly and other predicates become unavailable, e.g.

{name:{$typeof: “string",$valid:(value,propertyName,containingObject) => { if(... test...) return true; }}.

Partials Reduction

Once an object has been matched, it is not necessarily the case that the entire object should be returned. The $return key addresses this. This query will only return city and state in address sub-objects:

{address:{$typeof:"object",$return:({city,state})=>{city,state})}}

Basic Operators

First, JOQULAR assumes all of the basic comparison operators supported by MongoDB $lt , $lte , $eq , $ne , $gte , $gt , $in , $nin ; then it adds $eeq (exact comparison), $neq (alias for $ne ).

It also assumes $and , $or , $not , but adds $xor , and drops $nor as somewhat superfluous. It also provides more power by allowing either nested or array notation, e.g.

{name:{$eq: "joe", $or:{$eq: "mary"}}}

is the same as

{name:{$or: [{$eq: "joe"},{$eq: "mary"}}}

For short expressions, nesting is fine. But for long expressions, an array is far simpler.

Geospatial and Date Matching

MongoDB’s Geospatial operators are also assumed. However, MongoDB does not support a specialized set of Date query operators, so those are added. They include: $date , $day , $fullYear , $hours , $milliseconds , $minutes , $month , $seconds , $time , $UTCDate , $UTCDay , $UTCFullYear , $UTCHours , $UTCSeconds , $UTCMilliseconds , $UTCMinutes , $UTCMonth , $year .

Range Checking

Isomorphic range checking operators are added. The language requires that the underlying implementation determine the type of an object and make appropriate calls. The user of the language should not have to know the underlying types of the data so {<some key>:{$between:[<bound one>,<bound two>]}} should work regardless on the value in the object. It will simply fail the bounds check if it is the wrong type.

This isomorphic approach makes queries resilient to change in schemaless data stores where homonym keys exist across different object instances.

The range operators include $between , $outside.

Collection And Set Operators

Collection and set operators are isomorphic. If an underlying object supports the operator or a function can be written that manifests the correct behavior, then the following become available: $includes , $excludes , $intersects , $disjoint .

Type Checking

$typeof is provided as an alias for $type to be consistent with the JavaScript language. And $instanceof is added. This can take either a string classname or the constructor of a class as its argument.

Unary type checking is also available by providing null as the argument to the test, e.g. {favoriteNumber:{$isOdd:null}} . Type checking is extended to include: $isArray , $isEven , $isOdd , $isCreditCard (using the Luhn algorithm), $isEmail , $isIPAddress .

Text Operators

$text is aliased to $search .

$regexp is aliased to $matches .

The operator $echoes is added to test for a soundex echo.

Inline Tests

$where is aliased to just $.

History

The first version of JOQULAR manifested itself as an in memory database in 2015. At the time is had less predicates that it does today and did not support key matching, aliasing, computed values, defaults, or partials extraction. It has now been deprecated and the name repurposed to denote a language that sits on top of JavaScript.

Availability

ReasonDB (currently in beta) implements 95% of JOQULAR, its primary lack is in the geospatial area. ReasonDB provides a multi-model datastorage API that can sit on-top of almost any key-value store inlcuding localStorage and Redis . It supports indexed key-value storage (similar to localStorage), graph storage (similar to GunDB), and its own SQL like statements . It also supports joins and cross-network cursors with all the power of arrays, e.g. forEach , some , every , etc.

Of course, you could build your own driver for either in-memory or database work.

Thanks for reading! Give us a clap if you learned something useful or were inspired in any way.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK