Jora – JavaScript object query languages and engine

4 months ago 35

NPM version Build Status Coverage Status Twitter

JavaScript object query language, and a library to process and perform Jora queries on data.

STATUS: Jora is stable, but syntax may change in next releases. Still very much work in progress (ideas and thoughts).

Features:

  • Tolerant to data stucture queries (e.g. just returns nothing for paths that not reachable)
  • Compact syntax for common tasks
  • Aggregate values across arrays and eliminate duplicates by default
  • Stat collecting mode (powers suggestions)
  • Tolerant parsing mode (useful to provide suggestions for query in an editor)
  • Extensible DSL on query build by custom method list

Related projects:

Table of content:

Jora is a query language designed for JSON-like data structures. It extends JSON5 and shares many similarities with JavaScript.

See Docs & playground.

// single-line comment /* multi-line comment */

Jora expressions are the building blocks of Jora queries. Expressions can include comments, literals, operators, functions, and variables.

Jora supports literals, which include:

  • Numbers: 42, -3.14, 6.022e23
  • Strings: "hello", 'world', `template${yes}`, "\u{1F600}"
  • Booleans: true, false
  • Regular expressions: /regexp/flags
  • Object literals: { hello: 'world' } (see Object literals)
  • Array literals: [1, 2, 3] (see Array literals)
  • Functions: => … (see Functions)
  • Keywords: NaN, Infinity, null and undefined

See Literals

Jora supports most JavaScript operators, including:

  • Arithmetic: +, -, *, /, %
  • Comparison: =, !=, <, <=, >, >=, ~=
  • Logical: and, or, not (alias no), ??, is, in, not in, has, has no
  • Ternary: ?:
  • Grouing: ( )
  • Pipeline: |

See Operators

Dot, bracket and slice notations

Jora provides notations for accessing properties and elements: dot, bracket and slice notations. Dot notation is similar to JavaScript's property access notation, using a period followed by the property name (e.g., $.propertyName). Bracket notation encloses the property name or index within square brackets (e.g., $['propertyName'] or $[0]), it's also possible to use functions to choose. Slice notation provides a concise syntax to slice elements with optional step (array[5:10:2] selects each odd element from 5th to 10th indecies).

Jora provides a rich set of built-in methods for manipulating data, such as map(), filter(), group(), sort(), reduce(), and many others. You can also define custom functions using the => arrow function syntax, and use them as a method.

Jora has a concise syntax for mapping and filtering. The map(fn) method is equivalent to .(fn()), while the filter(fn) method is equivalent to .[fn()].

Variables in Jora are helpful for storing intermediate results or simplifying complex expressions. To define a variable, use the $variableName: expression; syntax.

See Variables

Install with npm:

Basic usage:

// ESM import jora from 'jora'; // CommonJS const jora = require('jora');

Bundles are available for use in a browser:

  • dist/jora.js – minified IIFE with jora as global
<script src="node_modules/jora/dist/jora.js"></script> <script> jora('query')(data, context); </script>
  • dist/jora.esm.js – minified ES module
<script type="module"> import jora from 'node_modules/jora/dist/jora.esm.js' // ... </script>

By default (for short path) a ESM version is exposing. For IIFE version a full path to a bundle should be specified. One of CDN services like unpkg or jsDelivr can be used:

  • jsDeliver

    <script type="module"> import jora from 'https://cdn.jsdelivr.net/npm/jora'; </script>"><!-- ESM --> <script type="module"> import jora from 'https://cdn.jsdelivr.net/npm/jora'; </script>
    <script src="https://cdn.jsdelivr.net/npm/jora/dist/jora.js"></script>'><!-- IIFE with an export `jora` to global --> <script src="https://cdn.jsdelivr.net/npm/jora/dist/jora.js"></script>
  • unpkg

    <script type="module"> import jora from 'https://unpkg.com/jora'; </script>"><!-- ESM --> <script type="module"> import jora from 'https://unpkg.com/jora'; </script>
    <script src="https://unpkg.com/jora/dist/jora.js"></script>'><!-- IIFE with an export `jora` to global --> <script src="https://unpkg.com/jora/dist/jora.js"></script>
import jora from 'jora'; // create a query const query = jora('foo.bar'); // perform a query const result = query(data, context);

See the details in Jora library API

Get npm dependency paths (as a tree) that have packages with more than one version:

import jora from 'jora'; import { exec } from 'child_process'; function printTree() { // see implementation in examples/npm-ls.js } exec('npm ls --all --json', (error, stdout) => { if (error) { return; } const npmTree = JSON.parse(stdout); const depsPathsToMultipleVersionPackages = jora(` $normalizedDeps: => dependencies.entries().({ name: key, ...value }); $multiVersionPackages: ..$normalizedDeps() .group(=>name, =>version) .({ name: key, versions: value.sort() }) .[versions.size() > 1]; $pathToMultiVersionPackages: => .($name; { name, version, otherVersions: $multiVersionPackages[=>name=$name].versions - version, dependencies: $normalizedDeps() .$pathToMultiVersionPackages() .[name in $multiVersionPackages.name or dependencies] }); $pathToMultiVersionPackages() `)(npmTree); printTree(depsPathsToMultipleVersionPackages); });

Example of output:

[email protected] ├─ [email protected] │ ├─ [email protected] │ │ └─ [email protected] [more versions: 8.1.1] │ ├─ [email protected] │ │ └─ [email protected] [more versions: 3.0.4] │ ├─ [email protected] │ │ └─ [email protected] │ │ └─ [email protected] [more versions: 5.2.1] │ ├─ [email protected] [more versions: 20.2.4] │ └─ [email protected] │ └─ [email protected] [more versions: 20.2.4] ├─ [email protected] │ ├─ @eslint/[email protected] │ │ ├─ [email protected] [more versions: 5.2.0] │ │ └─ [email protected] [more versions: 3.0.4] ...

See more examples in Complex Jora query examples

Read Entire Article