gson-query
Advanced tools
Comparing version 1.2.1 to 1.2.2
@@ -1,4 +0,1 @@ | ||
"use strict"; | ||
var stripPointerPrefix = require("gson-pointer/lib/common").stripPointerPrefix; | ||
@@ -9,3 +6,3 @@ var rIsRegExp = /^\{.*\}$/; | ||
function convertToRegExp(pointerPartial) { | ||
return new RegExp(pointerPartial.replace(/^\{|\}$/g, "")); | ||
return new RegExp(pointerPartial.replace(/^\{|\}$/g, "")); | ||
} | ||
@@ -15,5 +12,5 @@ | ||
function splitRegExp(pointer) { | ||
pointer = pointer.replace(/^\{|\/\{/g, "§{"); | ||
pointer = pointer.replace(/\}\/|\}$/g, "}§"); | ||
return pointer.split("§"); | ||
pointer = pointer.replace(/^\{|\/\{/g, "§{"); | ||
pointer = pointer.replace(/\}\/|\}$/g, "}§"); | ||
return pointer.split("§"); | ||
} | ||
@@ -30,27 +27,28 @@ | ||
function parsePointer(pointer) { | ||
var partials, current, result, regex; | ||
pointer = stripPointerPrefix(pointer); | ||
var partials; | ||
var current; | ||
var result; | ||
pointer = stripPointerPrefix(pointer); | ||
if (pointer.indexOf("{") === -1) { | ||
return pointer.split("/"); | ||
if (pointer.indexOf("{") === -1) { | ||
return pointer.split("/"); | ||
} | ||
} else { | ||
result = []; | ||
partials = splitRegExp(pointer); | ||
result = []; | ||
partials = splitRegExp(pointer); | ||
while ((current = partials.shift()) != null) { | ||
if (current === "") { | ||
continue; | ||
} | ||
while ((current = partials.shift()) != null) { | ||
if (current === "") { | ||
continue; | ||
} | ||
if (rIsRegExp.test(current)) { | ||
result.push(current); | ||
if (rIsRegExp.test(current)) { | ||
result.push(current); | ||
} else { | ||
result.push.apply(result, current.split("/")); | ||
} | ||
} | ||
} else { | ||
result.push.apply(result, current.split("/")); | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
} | ||
@@ -57,0 +55,0 @@ |
@@ -1,4 +0,1 @@ | ||
"use strict"; | ||
var pointerDelete = require("gson-pointer/lib/delete"); | ||
@@ -15,8 +12,8 @@ var removeUndefinedItems = require("gson-pointer/lib/removeUndefinedItems"); | ||
matches.forEach(function (match) { | ||
pointerDelete(obj, match[POINTER], true); | ||
pointerDelete(obj, match[POINTER], true); | ||
}); | ||
matches.forEach(function (match) { | ||
if (Array.isArray(match[PARENT])) { | ||
removeUndefinedItems(match[PARENT]); | ||
} | ||
if (Array.isArray(match[PARENT])) { | ||
removeUndefinedItems(match[PARENT]); | ||
} | ||
}); | ||
@@ -23,0 +20,0 @@ return obj; |
@@ -1,4 +0,1 @@ | ||
"use strict"; | ||
var o = require("gson-conform"); | ||
@@ -9,24 +6,24 @@ var common = require("./common"); | ||
var f = { | ||
query: function (query) { | ||
return function (item) { | ||
return valid(item, query); | ||
}; | ||
}, | ||
queryKey: function (obj, query) { | ||
return function (key) { | ||
return valid(obj[key], query); | ||
}; | ||
}, | ||
queryRegExp: function (obj, query, regex) { | ||
return function (key) { | ||
return regex.test(key) ? valid(obj[key], query) : false; | ||
}; | ||
} | ||
}, | ||
query: function (query) { | ||
return function (item) { | ||
return valid(item, query); | ||
}; | ||
}, | ||
queryKey: function (obj, query) { | ||
return function (key) { | ||
return valid(obj[key], query); | ||
}; | ||
}, | ||
queryRegExp: function (obj, query, regex) { | ||
return function (key) { | ||
return regex.test(key) ? valid(obj[key], query) : false; | ||
}; | ||
} | ||
}; | ||
MAP = { | ||
"false": false, | ||
"true": true, | ||
"null": null | ||
}; | ||
var MAP = { | ||
"false": false, | ||
"true": true, | ||
"null": null | ||
}; | ||
@@ -42,5 +39,5 @@ | ||
function filterValues(obj, query) { | ||
return filterKeys(obj, query).map(function (key) { | ||
return obj[key]; | ||
}); | ||
return filterKeys(obj, query).map(function (key) { | ||
return obj[key]; | ||
}); | ||
} | ||
@@ -56,18 +53,20 @@ | ||
function filterKeys(obj, query) { | ||
if (obj && query) { | ||
var matches = query.split("?", 2), keys, regex; | ||
if (matches[0] === "*" || matches[0] === "**") { | ||
keys = o.keys(obj); | ||
return keys.filter(f.queryKey(obj, matches[1])); | ||
if (obj && query) { | ||
var matches = query.split("?", 2); | ||
var keys; | ||
var regex; | ||
if (matches[0] === "*" || matches[0] === "**") { | ||
keys = o.keys(obj); | ||
return keys.filter(f.queryKey(obj, matches[1])); | ||
} else if (common.rIsRegExp.test(matches[0])) { | ||
keys = o.keys(obj); | ||
regex = common.convertToRegExp(matches[0]); | ||
return keys.filter(f.queryRegExp(obj, matches[1], regex)); | ||
} else if (common.rIsRegExp.test(matches[0])) { | ||
keys = o.keys(obj); | ||
regex = common.convertToRegExp(matches[0]); | ||
return keys.filter(f.queryRegExp(obj, matches[1], regex)); | ||
} else if (obj[matches[0]] && valid(obj[matches[0]], matches[1])) { | ||
return [matches[0]]; | ||
} | ||
} | ||
return []; | ||
} else if (obj[matches[0]] && valid(obj[matches[0]], matches[1])) { | ||
return [matches[0]]; | ||
} | ||
} | ||
return []; | ||
} | ||
@@ -83,50 +82,57 @@ | ||
function valid(obj, query) { | ||
if (!query) { | ||
return true; | ||
} | ||
if (!obj) { | ||
return false; | ||
} | ||
if (!query) { | ||
return true; | ||
} | ||
if (!obj) { | ||
return false; | ||
} | ||
var key, value, valid = true, truthy, query; | ||
var tests = query.replace(/(\&\&)/g, "§$1§").replace(/(\|\|)/g, "§$1§").split("§"); | ||
var key; | ||
var value; | ||
var isValid = true; | ||
var truthy; | ||
var or = false; | ||
for (var i = 0, l = tests.length; i < l; i += 2) { | ||
if (tests[i].indexOf(":!") > -1) { | ||
truthy = false; | ||
value = tests[i].split(":!"); | ||
var tests = query | ||
.replace(/(&&)/g, "§$1§") | ||
.replace(/(\|\|)/g, "§$1§") | ||
.split("§"); | ||
} else if (tests[i].indexOf(":") === -1) { | ||
truthy = false; | ||
value = [tests[i], undefined]; | ||
var or = false; | ||
for (var i = 0, l = tests.length; i < l; i += 2) { | ||
if (tests[i].indexOf(":!") > -1) { | ||
truthy = false; | ||
value = tests[i].split(":!"); | ||
} else { | ||
truthy = true; | ||
value = tests[i].split(":"); | ||
} | ||
} else if (tests[i].indexOf(":") === -1) { | ||
truthy = false; | ||
value = [tests[i], undefined]; | ||
key = value[0]; | ||
value = value[1]; | ||
} else { | ||
truthy = true; | ||
value = tests[i].split(":"); | ||
} | ||
if (value === "undefined") { | ||
// undefined is unmappable | ||
value = undefined; | ||
key = value[0]; | ||
value = value[1]; | ||
} else { | ||
value = MAP[value] === undefined ? value : MAP[value]; | ||
} | ||
if (value === "undefined") { | ||
// undefined is unmappable | ||
value = undefined; | ||
value = (truthy ? (value === obj[key]) : (value !== obj[key])); | ||
} else { | ||
value = MAP[value] === undefined ? value : MAP[value]; | ||
} | ||
if (or) { | ||
valid = valid || value; | ||
} else { | ||
valid = valid && value; | ||
} | ||
value = (truthy ? (value === obj[key]) : (value !== obj[key])); | ||
or = tests[i + 1] === "||"; | ||
} | ||
if (or) { | ||
isValid = isValid || value; | ||
} else { | ||
isValid = isValid && value; | ||
} | ||
return valid; | ||
or = tests[i + 1] === "||"; | ||
} | ||
return isValid; | ||
} | ||
@@ -133,0 +139,0 @@ |
@@ -1,4 +0,2 @@ | ||
"use strict"; | ||
/* eslint no-unused-vars: 0 */ | ||
var query = require("./run"); | ||
@@ -5,0 +3,0 @@ |
@@ -1,4 +0,1 @@ | ||
"use strict"; | ||
exports.get = require("./get"); | ||
@@ -5,0 +2,0 @@ exports.run = require("./run"); |
@@ -1,4 +0,1 @@ | ||
"use strict"; | ||
var filter = require("./filter"); | ||
@@ -12,6 +9,5 @@ var parsePointer = require("./common").parsePointer; | ||
* | ||
* @param {[type]} obj [description] | ||
* @param {[type]} jsonPointer function (value, key, parentObject, pointerToValue) | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
* @param {Any} obj | ||
* @param {String} jsonPointer - function (value, key, parentObject, pointerToValue) | ||
* @param {Function} cb | ||
*/ | ||
@@ -22,4 +18,8 @@ function queryRun(obj, jsonPointer, cb) { | ||
// cleanup first and last | ||
if (steps[0] === "") steps.shift(); | ||
if (steps[steps.length - 1] === "") steps.length -= 1; | ||
if (steps[0] === "") { | ||
steps.shift(); | ||
} | ||
if (steps[steps.length - 1] === "") { | ||
steps.length -= 1; | ||
} | ||
@@ -26,0 +26,0 @@ _query(obj, steps, cb, "#"); |
{ | ||
"name": "gson-query", | ||
"version": "1.2.1", | ||
"description": "json-pointer utilities for querying data", | ||
"version": "1.2.2", | ||
"description": "json-pointer utilities for querying and transforming data", | ||
"main": "lib/index.js", | ||
@@ -13,2 +13,3 @@ "repository": { | ||
"tdd": "watch 'npm run test' lib/ app/ test/; exit 0", | ||
"lint": "eslint lib", | ||
"debug": "devtool node_modules/mocha/bin/_mocha -qc -- --recursive test/unit/*.test.js" | ||
@@ -21,9 +22,16 @@ }, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"eslint": "^4.19.1", | ||
"mocha": "^3.1.2", | ||
"watch": "^1.0.1", | ||
"chai": "^3.5.0" | ||
"watch": "^1.0.1" | ||
}, | ||
"keywords": [], | ||
"keywords": [ | ||
"query", | ||
"json", | ||
"json-pointer", | ||
"glob-pattern", | ||
"library" | ||
], | ||
"author": "Sascha Goldhofer <noreply@saschagoldhofer.de> (https://github.com/sagold/)", | ||
"license": "ISC" | ||
"license": "MIT" | ||
} |
321
README.md
# gson query | ||
Install | ||
> 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 i gson-query` | ||
`npm install gson-query --save` | ||
and use with | ||
and get it like | ||
`const query = require("gson-query")` | ||
`const query = require("gson-query");` | ||
At first, **json-query** acts like a normal [**json-pointer**](https://github.com/sagold/json-pointer) where its match | ||
is passed to the given callback function: | ||
The command-line integration can be installed separately by [gson-query-cli](https://github.com/sagold/gson-query-cli) | ||
- [Introduction](#quick-introduction) | ||
- [API](#api) | ||
- [query](#query) | ||
- [callback](#callback) | ||
- [query.run](#query.run) | ||
- [query.get](#query.get) | ||
- [query.delete](#query.delete) | ||
## Quick introduction | ||
**run** a callback-function on each match of your _query_ | ||
```js | ||
const query = require("gson-query"); | ||
const data = { | ||
"parent": { | ||
"child": {"id": "child-1"} | ||
} | ||
query.run(data, "/server/*/services/*", callback); | ||
``` | ||
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 */ } | ||
``` | ||
**get** matches in an array instead of running a callback | ||
```js | ||
let results = query.get(data, "/server/*?state:critical", query.get.VALUE); // or POINTER or ALL | ||
``` | ||
which is the same as | ||
```js | ||
let results = query.get(data, "/server/*/services/*", (value) => value); | ||
``` | ||
or quickly **delete** properties from your data | ||
```js | ||
query.delete(data, "/server/*/services/{szm-.*}"); | ||
``` | ||
## API | ||
All examples import `const query = require("gson-query");` | ||
### query | ||
At first, **json-query** acts like a normal [**json-pointer**](https://github.com/sagold/json-pointer) | ||
```js | ||
let data = { | ||
"parent": { | ||
"child": { "id": "child-1" } | ||
} | ||
}; | ||
query.run(data, "#/parent/child/id", (value, key, object, jsonPointer) => { | ||
// value = "child-1" | ||
// key = "id" | ||
// object = {"id": "child-1"} | ||
// jsonPointer = "#/parent/child/id" | ||
}); | ||
// or get the result in an array | ||
var match = query.get(data, "#/parent/child/id"); | ||
// [ ["child-1", "id", {"id":"child-1"}, "#/parent/child/id"] ] | ||
const result = query.get(data, "#/parent/child/id", query.get.VALUE); | ||
// result: | ||
[ | ||
"child-1" | ||
] | ||
``` | ||
But query also supports **glob-patterns** with `*`: | ||
```js | ||
const query = require("gson-query"); | ||
const data = { | ||
"parent": { | ||
"child": {"id": "child-1"} | ||
}, | ||
"neighbour": { | ||
"child": {"id": "child-2"} | ||
} | ||
let data = { | ||
"parent": { | ||
"child": { "id": "child-1" } | ||
}, | ||
"neighbour": { | ||
"child": { "id": "child-2" } | ||
} | ||
}; | ||
query.run(data, "#/*/child/id", function (value, key, object, jsonPointer) { | ||
// will be called with value: "child-1" and "child-2" | ||
}); | ||
// or get the result in an array | ||
var match = query.get(data, "#/parent/child/id"); | ||
// [ ["child-1", ...], ["child-2", ...] ] | ||
const result = query.get(data, "#/*/child/id", query.get.VALUE); | ||
// result: | ||
[ | ||
"child-1", | ||
"child-2" | ||
] | ||
``` | ||
@@ -56,18 +109,41 @@ | ||
```js | ||
var query = require("gson-query"); | ||
var data = { | ||
"parent": { | ||
"child": {"id": "child-1"} | ||
}, | ||
"neighbour": { | ||
"child": {"id": "child-2"} | ||
} | ||
let data = { | ||
"parent": { | ||
"id": "parent", | ||
"child": {"id": "child-1"} | ||
}, | ||
"neighbour": { | ||
"child": {"id": "child-2"} | ||
} | ||
}; | ||
query.run(data, "#/**/id", function (value, key, object, jsonPointer) { | ||
// will be called with value: "parent" "child-1" and "child-2" | ||
}); | ||
const result = query.get(data, "#/**/id", query.get.VALUE); | ||
// result: | ||
[ | ||
"parent", | ||
"child-1", | ||
"child-2" | ||
] | ||
``` | ||
or simply call `query.run(data, "#/**", callback)` to run callback on each object,array and value. | ||
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" | ||
] | ||
``` | ||
@@ -77,21 +153,25 @@ To **filter** the matched objects, an object-query string may be appended on each single step: | ||
```js | ||
var query = require("gson-query"); | ||
var data = { | ||
"parent": { | ||
"valid": true, | ||
"child": {"id": "child-1"} | ||
}, | ||
"neighbour": { | ||
"valid": false, | ||
"child": {"id": "child-2"} | ||
}, | ||
"dungeons": { | ||
"child": {"id": "child-3"} | ||
} | ||
let data = { | ||
"parent": { | ||
"valid": true, | ||
"child": {"id": "child-1"} | ||
}, | ||
"neighbour": { | ||
"valid": false, | ||
"child": {"id": "child-2"} | ||
}, | ||
"dungeons": { | ||
"child": {"id": "child-3"} | ||
} | ||
}; | ||
query.run(data, "#/**?valid:true&&ignore:undefined/child", function (value, key, object, jsonPointer) { | ||
// will be called with value: {"id": "child-1"} only | ||
}); | ||
let result = query.get(data, "#/**?valid:true&&ignore:undefined/child", query.get.VALUE); | ||
// same result with | ||
query.run(data, "#/**?valid:!false/child", function (value, key, object, jsonPointer) { // ... | ||
result = query.get(data, "#/**?valid:!false/child", query.get.VALUE); | ||
// result: | ||
[ | ||
{ | ||
"valid": true, | ||
"child": {"id": "child-1"} | ||
} | ||
] | ||
``` | ||
@@ -101,57 +181,107 @@ | ||
```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" | ||
} | ||
} | ||
] | ||
``` | ||
**regular expression** must be wrapped with `{.*}`: | ||
```js | ||
var query = require("gson-query"); | ||
var data = { | ||
"albert": {valid: true}, | ||
"alfred": {valid: false}, | ||
"alfons": {valid: true} | ||
let data = { | ||
"albert": {valid: true}, | ||
"alfred": {valid: false}, | ||
"alfons": {valid: true} | ||
}; | ||
query.run(data, "#/{al[^b]}?valid:true", function (value, key, object, jsonPointer) { | ||
// will be executed with value: alfons | ||
}); | ||
const result = query.get(data, "#/{al[^b]}?valid:true", query.get.POINTER); | ||
// result: | ||
[ | ||
"#/alfred" | ||
] | ||
``` | ||
## queryGet | ||
### query.run | ||
If you only require values or pointers, use queryGet to receive an Array or Object as result: | ||
If you want a callback on each match use `query.run(data:object|array, query:string, callback:function):void` | ||
```js | ||
var queryGet = require("gson-query").get; | ||
query.run(data, "#/**/*?valid", (value, key, parent, jsonPointer) => {}); | ||
``` | ||
// default: queryGet.VALUES | ||
var arrayOfValues = queryGet(data, "#/**/id", queryGet.VALUE); | ||
// ["#/..", "#/..", ...] | ||
var arrayOfJsonPointers = queryGet(data, "#/**/id", queryGet.POINTER); | ||
// [arguments, arguments], where arguments = 0:value 1:object 2:key 3:jsonPointer | ||
var arrayOfAllFourArguments = queryGet(data, "#/**/id", queryGet.ALL); | ||
// {"#/..": value, "#/..": value} | ||
var mapOfPointersAndData = queryGet(data, "#/**/id", queryGet.MAP); | ||
// {"#/..": value, "#/..": value} | ||
var mapOfPointersAndData = queryGet(data, "#/**/id", (val, key, parent, pointer) => `custom-${pointer}`); | ||
// ["custom-#/parent/child/id", "custom-#/neighbour/child/id", "custom-#/dungeons/child/id"] | ||
### callback | ||
Each **callback** has the following signature | ||
`callback(value:any, key:string, parent:object|array, jsonPointer:string)` | ||
```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 */ } | ||
``` | ||
## queryDelete | ||
### query.get | ||
Multiple items on objects or in arrays may also be delete with query.delete: | ||
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 | ||
```js | ||
var queryDelete = require("gson-query").delete; | ||
// default: query.get.VALUES | ||
let arrayOfValues = query.get(data, "#/**/id", query.get.VALUE); | ||
// result: [value, value] | ||
queryDelete(data, "#/**/*/data"); | ||
let arrayOfJsonPointers = query.get(data, "#/**/id", query.get.POINTER); | ||
// result: ["#/..", "#/..", ...] | ||
let arrayOfAllFourArguments = query.get(data, "#/**/id", query.get.ALL); | ||
// result: [arguments, arguments], where arguments = 0:value 1:object 2:key 3:jsonPointer | ||
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"] | ||
``` | ||
## Examples | ||
### query.delete | ||
- `query.run(data, "#/**/*", callback);` will iterate over each value of the data object | ||
- `query.run(data, "#/**?valid:true", callback);` will select all objects having its property "valid" set to `true` | ||
Multiple items on objects or in arrays may also be delete with `query.delete(data:object|array, query:string):void`: | ||
```js | ||
query.delete(data, "#/**/*/data"); | ||
``` | ||
for further examples refer to the unit tests | ||
@@ -162,4 +292,1 @@ | ||
- [query.query](https://github.com/sagold/json-query/blob/master/test/unit/query.test.js) | ||
38247
10
290
289
4