New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

gson-query

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gson-query - npm Package Compare versions

Comparing version 3.0.1 to 4.1.0

dist/gson-query.js

92

lib/get.js

@@ -1,70 +0,48 @@

/* eslint no-unused-vars: 0 */
const query = require("./run");
const { parse } = require("./parser");
const { run, VALUE_INDEX, POINTER_INDEX } = require("./interpreter");
/**
* Returns the query results as an array or object, depending on its callback
*
* ## return type
*
* - get.ALL = 'all' returns all arguments of query callback [value, key, parent, pointer]
* - get.POINTER = 'pointer' returns only the json pointers to the targets
* - get.VALUE = 'value' Default. Returns only the matched value
* - get.MAP = Returns an object with all available pointers and their data, like { pointer: value }
*
* @param {Mixed} obj
* @param {Pointer} jsonPointer
* @param {String} type - type of return value. Defaults to "value"
* @return {Array|Object} containing result in specified format
*/
function queryGet(obj, jsonPointer, type) {
const matches = type === queryGet.MAP ? {} : [];
const cb = getCbFactory(type, matches);
query(obj, jsonPointer, cb);
return matches;
}
const returnTypes = {
value: r => r.map(e => e[VALUE_INDEX]),
pointer: r => r.map(e => e[POINTER_INDEX]),
all: r => r,
map: r => {
const map = {};
r.forEach(e => (map[e[POINTER_INDEX]] = e[VALUE_INDEX]));
return map;
}
};
queryGet.ALL = "all";
queryGet.MAP = "map";
queryGet.POINTER = "pointer";
queryGet.VALUE = "value";
queryGet.getCbFactory = getCbFactory;
Object.keys(returnTypes).forEach(prop => (get[prop.toUpperCase()] = prop));
function get(data, queryString, returnType = "value") {
if (queryString == null) {
return [];
}
function getCbFactory(type, matches) {
if (typeof type === "function") {
return function cb(value, key, obj, pointer) {
matches.push(type(obj[key], key, obj, pointer));
};
queryString = queryString.replace(/(\/$)/g, "");
if (queryString === "") {
queryString = "#";
}
switch (type) {
case queryGet.ALL:
return function cbGetAll(value, key, obj, pointer) {
matches.push([obj[key], key, obj, pointer]);
};
const ast = parse(queryString);
if (ast == null) {
throw new Error(`empty ast for '${queryString}'`);
}
if (ast.rest !== "") {
throw new Error(`Failed parsing queryString from: '${ast.rest}'`);
}
case queryGet.MAP:
return function cbGetMap(value, key, obj, pointer) {
matches[pointer] = value;
};
case queryGet.POINTER:
return function cbGetPointer(value, key, obj, pointer) {
matches.push(pointer);
};
const result = run(data, ast);
if (returnTypes[returnType]) {
return returnTypes[returnType](result);
} else if (typeof returnType === "function") {
return result.map(r => returnType(...r));
}
case queryGet.VALUE:
return function cbGetValue(value, key, obj, pointer) {
matches.push(value);
};
default:
return function cbGetValue(value, key, obj, pointer) {
matches.push(value);
};
}
return result;
}
module.exports = queryGet;
module.exports = get;
exports.get = require("./get");
exports.run = require("./run");
exports.delete = require("./delete");
exports.filter = require("./filter");
exports.pattern = require("./pattern");
exports.remove = require("./remove");
{
"name": "gson-query",
"version": "3.0.1",
"version": "4.1.0",
"description": "json-pointer utilities for querying and transforming data",
"main": "lib/index.js",
"main": "dist/gson-query.js",
"repository": {

@@ -11,5 +11,7 @@ "type": "git",

"scripts": {
"dist": "rm -rf dist; webpack",
"test": "mocha --recursive 'test/unit/**/*.test.js' -R spec; exit 0",
"tdd": "watch 'npm run test' lib/ test/; exit 0",
"lint": "eslint lib test",
"prepublish": "npm run test & npm run lint & npm run dist",
"coverage": "nyc npm run test --reporter=lcov",

@@ -19,2 +21,3 @@ "debug": "devtool node_modules/mocha/bin/_mocha -qc -- --recursive test/unit/*.test.js"

"dependencies": {
"ebnf": "^1.6.3",
"gson-conform": "^1.0.3",

@@ -24,2 +27,5 @@ "gson-pointer": "^3.4.1"

"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/preset-env": "^7.6.2",
"babel-loader": "^8.0.6",
"chai": "^4.2.0",

@@ -29,3 +35,6 @@ "eslint": "^6.4.0",

"nyc": "^14.1.1",
"watch": "^1.0.1"
"uglifyjs-webpack-plugin": "^2.2.0",
"watch": "^1.0.1",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9"
},

@@ -32,0 +41,0 @@ "keywords": [

<h1 align="left"><img src="./docs/gson-query.png" width="256" alt="gson-query"></h1>
**Query and transform your json data using an extended glob-pattern within browsers or nodejs**
> gson-query lets you quickly select values, patterns or types from json-data. Its input requires a simple string, describing a concise query into your data
> Query and transform your json data using an extended glob-pattern. This is a really helpful tool to quickly
>
> - fuzzy search json-data matching some search properties
> - transform data with consistent structures
> - extract information from json-data
**npm package** `gson-query`. The command-line integration can be installed separately from [gson-query-cli](https://github.com/sagold/gson-query-cli)
`npm install gson-query --save`
and get it like
`const query = require("gson-query");`
The command-line integration can be installed separately by [gson-query-cli](https://github.com/sagold/gson-query-cli)
- [Features](#features)
- [Introduction](#quick-introduction)
- [Breaking Changes](#breaking-changes)
- [API](#api)
- [query](#query)
- [callback](#callback)
- [query.run](#queryrun)
- [query.get](#queryget)
- [query.delete](#querydelete)
- [query.pattern](#querypattern)
- [About patterns](#about-patterns)
- [Further examples](#further-examples)
## Breaking Changes
- with version `v3.0.0` (2019/09/26)
- the syntax has changed to es6, which might require code transpilation
- queries for root-pointer (`#`, `#/`, `/`) no callback root object with `(rootObject, null, null, "#")`
- with `v2.0.0` a negated filter (lookahead), e.g. `*?valid:!true` will not return objects where `valid === undefined`. To match objects with missing properties you can still query them explicitly with `*?valid:!true||valid:undefined`
## Features
- [json-pointer](https://github.com/sagold/json-pointer) syntax `#/list/0/id`
- glob-patterns for properties (`*`, `**`)
- regex-support for properties `{any.*}`
- pattern-support for inifinite recursion `/tree(/nodes/*)+/value`
- or-patterns `/node((/left), (/right))`
- finite search in circular-data `**`
- lookahead-rules to test selected property `?property:value` and regex values `?property:{\d+}`
- and typechecks `/value?:array`
## Quick introduction
**run** a callback-function on each match of your _query_
Basically, a **query** is a json-pointer, which describes a path of properties into the json-data
```js
query.run(data, "/server/*/services/*", callback);
import { get } from "gson-query";
const input = { object: { a: { id: "id-a" }, b: { id: "id-b" } } };
const values = get(input, "/object/a/id"); // ["id-a"]
```
a **callback** receives the following arguments
```js
/**
* @param {Any} value - value of the matching query
* @param {String} key - the property or index of the value
* @param {Object|Array} parent - parent[key] === value
* @param {String} jsonPointer - json-pointer in data, pointing to value
*/
function callback(value, key, parent, jsonPointer) => { /* do sth */ }
```
But each property may also be a glob-pattern or a regular expression:
**get** matches in an array instead of running a callback
`*` selects all direct children
```js
let results = query.get(data, "/server/*?state:critical", query.get.VALUE); // or POINTER or ALL
const input = { object: { a: { id: "id-a" }, b: { id: "id-b" } } };
const values = get(input, "/object/*/id"); // ["id-a", "id-b"]
```
which is the same as
`**` selects all values
```js
let results = query.get(data, "/server/*/services/*", (value) => value);
const input = { object: { a: { id: "id-a" }, b: { id: "id-b" } } };
const values = get(input, "/object/**/id");
// [ { a: { id: "id-a" }, b: { id: "id-b" } }, { id: "id-a" }, "id-a", { id: "id-b" }, "id-b" ]
```
or quickly **delete** properties from your data
`{}` calls a regular expression
```js
query.delete(data, "/server/*/services/{szm-.*}");
const input = { object: { a: { id: "id-a" }, b: { id: "id-b" } } };
const values = get(input, "/{obj.*}/{.*}/id"); // ["id-a", "id-b"]
```
Use **patterns** to query patterns recursively
**lookahead** rules are used to validate the current value based on its properties
`?child` tests if a childProperty is defined
```js
query.pattern(data, "/node(/nodes/*)+/value");
```
const input = { object: { a: { id: "id-a" }, b: { id: "id-b" } } };
and to select multiple properties of an object or array:
```js
query.pattern(data, "/server((/store), (/{front-.*}))/services/*");
const values = get(input, "/object/*?id"); // [{ id: "id-a" }, { id: "id-b" }]
```
`?child:value` tests if a childProperty matches a value
## API
```js
const input = { object: { a: { id: "id-a" }, b: { id: "id-b" } } };
All examples import `const query = require("gson-query");`
const values = get(input, "/object/*?id:id-b"); // [{ id: "id-b" }]
```
### query
lookahead rules can also be negated `?child:!value`, tested by regex `?child:{^re+}`, combined `?child&&other` or joined `?child||other`. Undefined may be tested with `?property:undefined`, per default `undefined` is excluded from matches.
At first, **json-query** acts like a normal [**json-pointer**](https://github.com/sagold/json-pointer)
```js
let data = {
"parent": {
"child": { "id": "child-1" }
}
};
const result = query.get(data, "#/parent/child/id", query.get.VALUE);
// result:
[
"child-1"
]
```
**typechecks** can be used to query certain data-types
But query also supports **glob-patterns** with `*`:
`?:<type>`, where `<type>` may be any of `["boolean", "string", "number", "object", "array", "value"]`
```js
let data = {
"parent": {
"child": { "id": "child-1" }
},
"neighbour": {
"child": { "id": "child-2" }
}
};
const result = query.get(data, "#/*/child/id", query.get.VALUE);
// result:
[
"child-1",
"child-2"
]
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
const values = get(input, "/**?:string"); // ["id-b"]
```
and **glob-patterns** with `**`:
`?:value` will match all types except *objects* and *arrays*
```js
let data = {
"parent": {
"id": "parent",
"child": {"id": "child-1"}
},
"neighbour": {
"child": {"id": "child-2"}
}
};
const result = query.get(data, "#/**/id", query.get.VALUE);
// result:
[
"parent",
"child-1",
"child-2"
]
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
const values = get(input, "/**?:value"); // [33, "id-b"]
```
or simply call `query.get(data, "#/**", query.get.VALUE)` to query the value of each property
```js
let data = {
"parent": {
"id": "parent",
"child": { "id": "child-1" }
}
};
const result = query.get(data, "#/**/id", query.get.VALUE);
// result:
[
{
"id":"parent",
"child": { "id":"child-1" }
},
"parent",
{ "id":"child-1" },
"child-1"
]
```
**patterns** can be used to combine queries into a single result (*OR*) and to build up results from recursive queries (*+*)
To **filter** the matched objects, an object-query string may be appended on each single step:
Queries can be grouped by parenthesis, where `/a/b/c = /a(/b)(/c) = /a(/b/c)`.
`((/a), (/b))` resolves both queries on the previous result
```js
let data = {
"parent": {
"valid": true,
"child": {"id": "child-1"}
},
"neighbour": {
"valid": false,
"child": {"id": "child-2"}
},
"dungeons": {
"child": {"id": "child-3"}
}
};
let result = query.get(data, "#/**?valid:true&&ignore:undefined/child", query.get.VALUE);
// same result with
result = query.get(data, "#/**?valid:!false/child", query.get.VALUE);
// result:
[
{
"valid": true,
"child": {"id": "child-1"}
}
]
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
const values = get(input, "/object((/a), (/b))"); // [{ id: 33 }, { id: "id-b" }]
```
or match all objects that have a defined property _valid_ like `query.run(data, "#/**?valid", callback)`.
and the result may be queried further
```js
let data = {
"parent": {
"valid": true,
"child": {"id": "child-1"}
},
"neighbour": {
"valid": false,
"child": {"id": "child-2"}
},
"dungeons": {
"child": {"id": "child-3"}
}
};
const result = query.get(data, "#/**?valid", query.get.VALUE);
// result:
[
{
"valid": true,
"child": {
"id": "child-1"
}
},
{
"valid": false,
"child": {
"id": "child-2"
}
}
]
get(input, "/object((/a), (/b))/id"); // [33, "id-b"]
get(input, "/object((/a), (/b))/id?:number"); // [33]
```
**regular expression** must be wrapped with `{.*}`:
`(/a)+` will repeat the grouped query for all possible results
```js
let data = {
"albert": {valid: true},
"alfred": {valid: false},
"alfons": {valid: true}
const input = {
id: 1,
a: { // first iteration
id: 2,
a: { // second iteration
id: 3
a: 4 // last iteration
}
}
};
const result = query.get(data, "#/{al[^b]}?valid:true", query.get.POINTER);
// result:
[
"#/alfred"
]
const values = get(input, "/(/a)+"); // [{ id: 2, a: { id: 3, a: 4 } }, { id: 3, a: 4 }, 4]
```
### query.run
## Breaking Changes
If you want a callback on each match use `query.run(data:object|array, query:string, callback:function):void`
- with version `v4.0.0` (2019/10/01)
- the api has been simplified to methods `query.get` and `query.delete` (removed `run` and `pattern`)
- default package-entry is a es5-web-bundle
- with version `v3.0.0`
- the syntax has changed to es6, which might require code transpilation
- queries for root-pointer (`#`, `#/`, `/`) now callback root object with `(rootObject, null, null, "#")`
- with `v2.0.0` a negated filter (lookahead), e.g. `*?valid:!true` will not return objects where `valid === undefined`. To match objects with missing properties you can still query them explicitly with `*?valid:!true||valid:undefined`
```js
query.run(data, "#/**/*?valid", (value, key, parent, jsonPointer) => {});
```
## API
### callback
*gson-query* exposes to methods `get` and `remove`
Each **callback** has the following signature
`callback(value:any, key:string, parent:object|array, jsonPointer:string)`
method | signature | description
--------|-------------------------------------------------------------------|------------------------------
get | (input:any, query: string, returnType?:string\|function) | query data, returns results
remove | (input:any, query: string, returnRemoved?:boolean) | delete query targets, returns input
**get**
per default, *get* returns a list of all values
```js
/**
* @param {Any} value - value of the matching query
* @param {String} key - the property or index of the value
* @param {Object|Array} parent - parent[key] === value
* @param {String} jsonPointer - json-pointer in data, pointing to value
*/
function callback(value, key, parent, jsonPointer) => { /* do sth */ }
import { get } from "gson-query";
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
const values = get(input, "/**?:value"); // [33, "id-b"]
```
Using the optional value `returnType` you can change the result type to the following options
`["all", "value", "pointer", "map"]`. The string values can also be accessed as property on `get`: `get.ALL, get.VALUE, get.POINTER, get.MAP`:
### query.get
If you only require values or pointers, use `query.get(data:object|array, query:string, type:TYPE = "all")` to receive an Array or Object as result
returnType | description
------------|------------------------------------------------------------------
"value" | returns all matched values of the query `[33, "id-b"]`
"pointer" | returns json-pointer to results `["#/object/a", "#/object/b"]`
"map" | returns an pairs of `jsonPointer: resultValue` as an object
"all" | returns a list, where each result is an array of `[value, keyToValue, parentObject, jsonPointer]`
function | callback with `(value, keyToValue, parentObject, jsonPointer) => {}`. If a value is returned, the result will be replaced by the return-value
```js
// default: query.get.VALUES
let arrayOfValues = query.get(data, "#/**/id", query.get.VALUE);
// result: [value, value]
import { get } from "gson-query";
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
let arrayOfJsonPointers = query.get(data, "#/**/id", query.get.POINTER);
// result: ["#/..", "#/..", ...]
get(input, "/**?:value", get.VALUE); // [33, "id-b"]
get(input, "/**?:value", get.POINTER); // ["#/object/a/id", "#/object/b/id"]
get(input, "/**?:value", get.MAP); // { "#/object/a/id": 33, "#/object/b/id": "id-b" }
let arrayOfAllFourArguments = query.get(data, "#/**/id", query.get.ALL);
// result: [arguments, arguments], where arguments = 0:value 1:object 2:key 3:jsonPointer
get(input, "/**?:value", get.ALL);
// [
// [33, "id", { id: 33 }, "#/object/a/id"],
// ["id-b", "id", { id: "id-b" }, "#/object/b/id"]
// ]
let mapOfPointersAndData = query.get(data, "#/**/id", query.get.MAP);
// result: {"#/..": value, "#/..": value}
let mapOfPointersAndData = query.get(data, "#/**/id", (val, key, parent, pointer) => `custom-${pointer}`);
// result: ["custom-#/parent/child/id", "custom-#/neighbour/child/id", "custom-#/dungeons/child/id"]
get(input, "/**?:value", (value, key, parent, pointer) => `custom-${pointer}`);
// ["custom-#/object/a/id", "custom-#/object/b/id"]
```
### query.delete
**remove** deletes any match from the input data.
Note: the input will be modified. If this is unwanted behaviour, copy your data up front.
Multiple items on objects or in arrays may also be delete with `query.delete(data:object|array, query:string):void`:
```js
import { remove } from "gson-query";
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
```js
query.delete(data, "#/**/*/data");
remove(input, "/object/*/id"); // { object: { a: {}, b: {} } };
```
Per default, the input object is returned. Setting the optional argument `returnRemoved = true`, will return a list of the removed items
### query.pattern
The pattern-queries behave as the default `query.get` methods:
```js
import query from "gson-query";
import { remove } from "gson-query";
const input = { object: { a: { id: 33 }, b: { id: "id-b" } } };
// predefined callback
const targets = query.pattern(data, "#/*(/node/*?valid)+/valid", query.get.POINTER); // return pointers
const values = query.pattern(data, "#/*(/node/*?valid)+/valid", query.get.VALUES); // return values
// ...
// custom callback
query.pattern(data, "#/*(/node/*?valid)+/valid", (value, key, parent, jsonPointer) => {});
remove(input, "/object/*/id", true); // [ 33, "id-b" ]
```
## About patterns
Pattern-queries enable selection of recursive patterns and offer a way to build up a collection of data for further filterung. A pattern uses brackets `()` to identify repeatable structures and offers multiple selections for the same data-entry.

@@ -339,7 +250,7 @@

const result = query.pattern(data, "#/tree((/left),(/right))*/id");
const result = get(data, "#/tree((/left),(/right))*/id");
// ["1", "2", "3", "4"]
```
**Note** that each pattern-queries are resovled using `query.get` and thus support all mentioned features.
**Note** that each pattern-queries is resovled using `query.get` and thus supports all mentioned features.

@@ -380,5 +291,3 @@ One use-case for pattern-queries can be found in json-schema specification. Any definition in `#/defs` may reference itself or be referenced circular. A linear query cannot describe the corresponding data, but pattern-queries might be sufficient.

- [query.delete](https://github.com/sagold/json-query/blob/master/test/unit/queryDelete.test.js)
- [query.get](https://github.com/sagold/json-query/blob/master/test/unit/queryGet.test.js)
- [query.query](https://github.com/sagold/json-query/blob/master/test/unit/query.test.js)
- [query.pattern](https://github.com/sagold/json-query/blob/master/test/unit/pattern.test.js)
- [query.delete](https://github.com/sagold/json-query/blob/master/test/unit/delete.test.js)
- [query.get](https://github.com/sagold/json-query/blob/master/test/unit/get.test.js)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc