Jora JavaScript Object Query Engine

repository·master·Indexed 41 days ago

https://github.com/discoveryjs/jora

A query language and library for navigating and transforming complex data structures. Jora 1.0.0-beta.16 provides a rich syntax for literals, operators, and advanced data manipulation including mapping, filtering, grouping, and sorting. It features a flexible API for defining custom methods and assertions, optional chaining by default, and a statistics mode for query introspection and autocomplete suggestions.

Tokens
27.4K
Snippets
133
Records
154
Agent score
52%

What's inside Jora

  1. What is Jora?

    master

    Jora is an expressive query language designed for querying and transforming JavaScript-like or JSON data. It acts as a superset of JSON5 and shares significant syntax similarities with JavaScript.

    Key characteristics include:

    • Compact Syntax: Optimized for common data transformation tasks.
    • Automatic Aggregation: Aggregates values across arrays and eliminates duplicates by default.
    • Safety: By default, Jora does not mutate input data or cause side effects. It is designed to be exception-safe; if a path is unreachable or an operator is inapplicable, it returns undefined rather than throwing an error.
    • Extensibility: Supports custom methods.
    • Tooling Support: Includes modes for stat collecting (to power suggestions) and tolerant parsing (useful for editor-based query suggestions).
  2. What is Jora language

    master
    Jora is a query and transformation language designed for JavaScript-like or JSON data. It is a superset of JSON5, meaning it supports all JSON5 syntax while adding advanced querying capabilities. Jora is designed to be immutable, meaning it creates new data rather than modifying the original input.
  3. Explore Jora documentation and API

    master

    The Jora documentation is organized into several key areas to help you master the query language:

  4. Related Jora projects

    master

    Jora is part of a broader ecosystem of tools:

    • Discovery: A project that uses Jora as its core functionality to transform data flows for views and query data for reports.
    • JsonDiscovery: A browser extension (available for Chrome and Firefox) based on Discovery for viewing JSON documents in a browser.
    • jora-cli: A command line interface for transforming JSON-like data using Jora queries.
  5. Use comparator functions for sorting and ordering

    master

    Comparator functions are specialized functions used to return -1, 0, or 1 to order values. They are used by methods like sort(), min(), and max().

    Syntax:

    • expr asc: Sorts in ascending order based on the expression.
    • expr desc: Sorts in descending order based on the expression.
    • Multi-level sorting: You can provide a comma-separated sequence of expressions to define a single comparator (e.g., foo asc, bar.size() desc).

    Type Comparison Order: When comparing different types, Jora follows this hierarchy (from lowest to highest):

    1. boolean
    2. NaN
    3. number
    4. string
    5. null
    6. object
    7. other
    8. undefined
    // Basic descending sort
    $input: [{ foo: 3 }, { foo: 1 }, { foo: 5 }];
    $input.sort(foo desc)
    
    // Multi-level sort example
    // (a, b) => a.foo > b.foo ? 1 : ... (then checks bar.size)
    foo asc, bar.size() desc
  6. Use assertions in Jora queries

    master

    Assertions are functions used to evaluate values against specified criteria. They use a dedicated syntax that is separate from standard methods. The basic syntax is expr is assertion. You can also omit the expression (e.g., is assertion) to assert against the current context.

    Assertions support logical operators for complex evaluations:

    • not: Negation (is not assertion or is not (assertion and assertion))
    • and: Logical AND (is (assertion and assertion))
    • or: Logical OR (is (assertion or assertion))
    • Parentheses () can be used for grouping logical operations.
    expr is assertion
  7. Define custom methods and assertions

    master

    You can extend Jora using methods and assertions. Definitions can be either Functions or Strings (Jora queries).

    Function Definitions

    Functions have access to this, which contains a context reference. You can invoke other methods or assertions using this.method(name, ...args) and this.assertion(name, ...args). Important: You must use standard function declarations (not arrow functions) to ensure this is bound correctly.

    const customMethods = {
      example(current, sortingFn = (a, b) => a - b) {
        // Access context and other methods via 'this'
        if (this.context.value === 'foo' && this.assertion('array', current)) {
          return this.method('sort', current, sortingFn);
        }
        return null;
      }
    };

    String Definitions

    String definitions are Jora queries themselves. They have access to special variables:

    • $: The entry value of the method/assertion definition.
    • $$, @, #: Other special variables.
    • Note: @ refers to the entry value of the definition, not the entry value of the main query.

    All built-in and custom methods/assertions are available within string definitions.

    import jora from 'jora';
    
    // Using setup() for reusable extensions
    const queryWithCustomMethods = jora.setup({
        methods: {
            myMethod($) { /* method logic here */ }
        },
        assertions: {
            odd: '$ % 2 = 1'
        }
    });
    
    // Use the custom query factory
    queryWithCustomMethods('foo.myMethod(is odd)')(data, context);
  8. Handle unique values and array concatenation in mapping

    master

    Jora's mapping logic has specific rules regarding the size and content of the resulting array:

    1. Automatic Uniqueness: Mapping results in an array of unique values. For example, [1, 2, 2].() results in [1, 2].
    2. Array Flattening/Concatenation: If the mapping function returns an array, those elements are merged into the result set. Duplicates are still dropped during this process.
    // Mapping returns unique values
    [ 1, 2, 2, 3, 3, 3 ].() // .() is equivalent to .($)
    // Result: [1, 2, 3]
    
    // Concatenating arrays with overall result
    $input: [
        { "values": [1, 2, 3] },
        { "values": [3, 4] }
    ];
    $input.(values)
    // Result: [ 1, 2, 3, 4 ]
  9. Use mapping with `.(…)` and `map()`

    master

    Mapping in Jora allows you to transform elements of an array, or even primitive types and objects, into a new structure.

    Key Behaviors:

    • Uniqueness: Mapping automatically returns unique values. This means the resulting array may be shorter than the input array.
    • Array Concatenation: If the mapping expression returns an array, those elements are concatenated into the overall result (with duplicates dropped).
    • Undefined Handling: Jora automatically ignores undefined values during mapping, effectively filtering them out of the result.
    • Primitive Support: Mapping can be applied to single values (numbers, strings, etc.) to transform them into objects or other types.
    // Syntax options
    .(expr)
    .map(fn) // or .map(=> expr)
  10. Compare `.[…]` and `filter()` syntax

    master

    While .[…] is generally preferred for concisenness, both syntaxes are functionally equivalent and can be used interchangeably depending on your need for readability or flexibility.

    Equivalence mapping:

    • .filter(fn) is equivalent to .[fn()]
    • .[expr] is equivalent to .filter(=> expr)
  11. How methods work in Jora

    master

    In Jora, methods are functions invoked in a functional style. The value on the left side of the method call (the expression expr) is automatically passed as the first argument to the method. If no expression is provided, the implicit value $ is passed as the first argument.

    This allows for chaining operations where each method receives the result of the previous one.

    // Example of functional chaining
    expr.method1().method2()
    
    // Using the implicit '$' if no expression is provided
    .method(...args)
    
    // Using a function as a method
    method(...args)