@jsonhero/query
TypeScript icon, indicating that this package has built-in type declarations

1.0.7 • Public • Published

JSON Hero Query

A TypeScript/JavaScript library that provides a simple way of accessing objects inside JSON using paths with filtering

How to install

npm install @jsonhero/query

Getting started

Importing

You can require

const { JSONHeroQuery } = require('@jsonhero/query');

Or if you're using TypeScript:

import { JSONHeroQuery } from '@jsonhero/query';

Sample object

Given the following JSON variable called employees

let employees = {
    people: [
        {
            name: 'Matt',
            age: 36,
            favouriteThings: ['Monzo', 'The Wirecutter', 'Jurassic Park', 'Rocket League'],
        },
        {
          name: 'James',
          age: 39,
          favouriteThings: ['Far Cry 1', 'Far Cry 2', 'Far Cry 3'],
        },
        {
          name: 'Eric',
          age: 38,
          favouriteThings: ['Bitcoin', 'Rocket League'],
        },
        {
          name: 'Dan',
          age: 34,
          favouriteThings: ['Frasier'],
        },
    ],
    count: 4
}

Anatomy of a query

To construct a query you provide an object with an array of paths (with optional filters):

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*',
    filters: [
      {
        type: 'operator',
        key: 'age',
        operatorType: '>=',
        value: 36,
      },
    ],
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees); 
//results is an array with the Matt, James and Eric objects in

Filter types

There are three types of filters, these can be selected by using the type field

Filter name What it does
operator Allows you to perform logical operators tests on the current object
subPath Allows you to perform logical operator tests on the current object or any sub-objects using a path
or Allows you to add sub-filters where if any of the sub-filters pass, this filter will pass
and Allows you to add and conditions inside an or. The top-level filters is and already

Operator filter and operator types

The operator filter type applies the specified test to each object at that path.

If the object fails the test then it will be excluded from the results.

There are several familiar operator types you can use. These can also all be used in the subPath filter type.

Operator name What it does
== Performs == on object and specified value. (not strict equality)
!= Performs != on object and specified value. (not strict inequality)
> Performs > on object and specified value
>= Performs >= on object and specified value
< Performs < on object and specified value
<= Performs <= on object and specified value
startsWith Non-strings will fail this test. Performs string.startsWith(value);
endsWith Non-strings will fail this test. Performs string.endsWith(value);
containsValue For objects and arrays, will check if they contain the specified value
isEmpty If null, undefined, empty array or empty object this is true
isNotEmpty if not null, not undefined, not empty array or not empty object this is true

Another example, specifying a key and using startsWith

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*',
    filters: [
      {
        type: 'operator',
        key: 'name',
        operatorType: 'startsWith',
        value: 'Jam',
      },
    ],
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees); 
//results is an array with just the James object in

A query that doesn't use key, which means it applies the filter to the object at the current path (not a value at the specified sub-key)

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*'
  },
  {
    path: 'name',
    filters: [
      {
        type: 'operator',
        operatorType: '!=',
        value: 'Matt',
      },
    ],
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees); 
//results = ['James', 'Eric', 'Dan']
//note that this is just an array of strings, not objects, because we are specifying name in the path

Sub-path queries

These queries allow you to do advanced filtering. You can specify a path to any sub-object from the current path and then apply filters to determine if the current object should be included or not.

In this query we eliminate any people where one of their favourite things isn't Rocket League, then return their name.

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*',
    filters: [
      {
        type: 'subPath',
        path: 'favouriteThings.*',
        operatorType: '==',
        value: 'Rocket League',
      },
    ],
  },
  {
    path: 'name',
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees); 
//results = ['Matt', 'Eric']

Or queries

Simply put, these allow you to combine filters and have an object pass the test if any of the sub-filters pass.

This query is using subPath queries (similar to the one above) but in this case if any of them pass we will include the person.

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*',
    filters: [
      {
        type: 'or',
        subFilters: [
          {
            type: 'subPath',
            path: 'favouriteThings.*',
            operatorType: '==',
            value: 'Rocket League',
          },
          {
            type: 'subPath',
            path: 'favouriteThings.*',
            operatorType: '==',
            value: 'Monzo',
          },
          {
            type: 'subPath',
            path: 'favouriteThings.*',
            operatorType: '==',
            value: 'Frasier',
          },
        ],
      },
    ],
  },
  {
    path: 'name',
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees); 
//results = ['Matt', 'Eric', 'Dan']

And queries

The top-level filters objects are and filters. If you need to test an and inside an or filter, you can easily do that:

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*',
    filters: [
      {
        type: 'or',
        subFilters: [
          {
            type: 'and',
            subFilters: [
              {
                type: 'subPath',
                path: 'favouriteThings.*',
                operatorType: '==',
                value: 'Rocket League',
              },
              {
                type: 'subPath',
                path: 'favouriteThings.*',
                operatorType: '==',
                value: 'Monzo',
              },
            ],
          },
          {
            type: 'and',
            subFilters: [
              {
                type: 'subPath',
                path: 'favouriteThings.*',
                operatorType: '==',
                value: 'Bitcoin',
              },
              {
                type: 'subPath',
                path: 'favouriteThings.*',
                operatorType: '==',
                value: 'Rocket League',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    path: 'name',
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees); 
//results = ['Matt', 'Eric']

Getting paths as well as the results

You can get the path objects as well as the values by passing an optional object when you query

let queryConfig = [
  {
    path: 'people',
  },
  {
    path: '*',
    filters: [
      {
        type: 'subPath',
        path: 'favouriteThings.*',
        operatorType: 'startsWith',
        value: 'Far Cry',
      },
    ],
  },
  {
    path: 'name',
  },
];

let query = new JSONHeroQuery(queryConfig);
let results = query.all(employees, { includePath: true});
//results will be an object like this 
//{
//  value: 'James',
//  path: a JSONHeroPath for this element
//}

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 1.0.7
    0
    • latest

Version History

Package Sidebar

Install

npm i @jsonhero/query

Weekly Downloads

0

Version

1.0.7

License

MIT

Unpacked Size

33.6 kB

Total Files

19

Last publish

Collaborators

  • ericallam
  • mattaitken