@jsonquerylang/jsonquery
Advanced tools
Comparing version 3.1.1 to 4.0.0
@@ -1,87 +0,87 @@ | ||
const E = (t) => Array.isArray(t), L = (t) => typeof t == "string"; | ||
const E = (t) => Array.isArray(t), I = (t) => typeof t == "string"; | ||
function m(t) { | ||
return (...e) => { | ||
const r = e.map((i) => b(i)), n = r[0], s = r[1]; | ||
return r.length === 1 ? (i) => t(n(i)) : r.length === 2 ? (i) => t(n(i), s(i)) : (i) => t(...r.map((j) => j(i))); | ||
const n = e.map((c) => $(c)), r = n[0], s = n[1]; | ||
return n.length === 1 ? (c) => t(r(c)) : n.length === 2 ? (c) => t(r(c), s(c)) : (c) => t(...n.map((x) => x(c))); | ||
}; | ||
} | ||
const N = { | ||
const k = { | ||
pipe: (...t) => { | ||
const e = t.map((r) => b(r)); | ||
return (r) => e.reduce((n, s) => s(n), r); | ||
const e = t.map((n) => $(n)); | ||
return (n) => e.reduce((r, s) => s(r), n); | ||
}, | ||
object: (t) => { | ||
const e = Object.keys(t).map((r) => [r, b(t[r])]); | ||
return (r) => { | ||
const n = {}; | ||
for (const [s, i] of e) | ||
n[s] = i(r); | ||
return n; | ||
const e = Object.keys(t).map((n) => [n, $(t[n])]); | ||
return (n) => { | ||
const r = {}; | ||
for (const [s, c] of e) | ||
r[s] = c(n); | ||
return r; | ||
}; | ||
}, | ||
array: (...t) => { | ||
const e = t.map((r) => b(r)); | ||
return (r) => e.map((n) => n(r)); | ||
const e = t.map((n) => $(n)); | ||
return (n) => e.map((r) => r(n)); | ||
}, | ||
get: (...t) => { | ||
if (t.length === 0) | ||
return (e) => e; | ||
return (e) => e ?? null; | ||
if (t.length === 1) { | ||
const e = t[0]; | ||
return (r) => r == null ? void 0 : r[e]; | ||
return (n) => (n == null ? void 0 : n[e]) ?? null; | ||
} | ||
return (e) => { | ||
let r = e; | ||
for (const n of t) | ||
r = r == null ? void 0 : r[n]; | ||
return r; | ||
let n = e; | ||
for (const r of t) | ||
n = n == null ? void 0 : n[r]; | ||
return n ?? null; | ||
}; | ||
}, | ||
map: (t) => { | ||
const e = b(t); | ||
return (r) => r.map(e); | ||
const e = $(t); | ||
return (n) => n.map(e); | ||
}, | ||
filter: (t) => { | ||
const e = b(t); | ||
return (r) => r.filter(e); | ||
const e = $(t); | ||
return (n) => n.filter(e); | ||
}, | ||
sort: (t = ["get"], e) => { | ||
const r = b(t), n = e === "desc" ? -1 : 1; | ||
function s(i, j) { | ||
const w = r(i), y = r(j); | ||
return w > y ? n : w < y ? -n : 0; | ||
const n = $(t), r = e === "desc" ? -1 : 1; | ||
function s(c, x) { | ||
const w = n(c), y = n(x); | ||
return w > y ? r : w < y ? -r : 0; | ||
} | ||
return (i) => i.slice().sort(s); | ||
return (c) => c.slice().sort(s); | ||
}, | ||
pick: (...t) => { | ||
const e = t.map( | ||
([n, ...s]) => [s[s.length - 1], N.get(...s)] | ||
), r = (n, s) => { | ||
const i = {}; | ||
for (const [j, w] of s) | ||
i[j] = w(n); | ||
return i; | ||
([r, ...s]) => [s[s.length - 1], k.get(...s)] | ||
), n = (r, s) => { | ||
const c = {}; | ||
for (const [x, w] of s) | ||
c[x] = w(r); | ||
return c; | ||
}; | ||
return (n) => E(n) ? n.map((s) => r(s, e)) : r(n, e); | ||
return (r) => E(r) ? r.map((s) => n(s, e)) : n(r, e); | ||
}, | ||
groupBy: (t) => { | ||
const e = b(t); | ||
return (r) => { | ||
const n = {}; | ||
for (const s of r) { | ||
const i = e(s); | ||
n[i] ? n[i].push(s) : n[i] = [s]; | ||
const e = $(t); | ||
return (n) => { | ||
const r = {}; | ||
for (const s of n) { | ||
const c = e(s); | ||
r[c] ? r[c].push(s) : r[c] = [s]; | ||
} | ||
return n; | ||
return r; | ||
}; | ||
}, | ||
keyBy: (t) => { | ||
const e = b(t); | ||
return (r) => { | ||
const n = {}; | ||
for (const s of r) { | ||
const i = e(s); | ||
n[i] = n[i] ?? s; | ||
const e = $(t); | ||
return (n) => { | ||
const r = {}; | ||
for (const s of n) { | ||
const c = e(s); | ||
r[c] = r[c] ?? s; | ||
} | ||
return n; | ||
return r; | ||
}; | ||
@@ -91,28 +91,38 @@ }, | ||
uniq: () => (t) => [...new Set(t)], | ||
uniqBy: (t) => (e) => Object.values(N.groupBy(t)(e)).map((r) => r[0]), | ||
limit: (t) => (e) => e.slice(0, t), | ||
uniqBy: (t) => (e) => Object.values(k.groupBy(t)(e)).map((n) => n[0]), | ||
limit: (t) => (e) => e.slice(0, Math.max(t, 0)), | ||
size: () => (t) => t.length, | ||
keys: () => Object.keys, | ||
values: () => Object.values, | ||
prod: () => (t) => t.reduce((e, r) => e * r), | ||
sum: () => (t) => t.reduce((e, r) => e + r), | ||
average: () => (t) => N.sum()(t) / t.length, | ||
prod: () => (t) => t.reduce((e, n) => e * n), | ||
sum: () => (t) => t.reduce((e, n) => e + n), | ||
average: () => (t) => k.sum()(t) / t.length, | ||
min: () => (t) => Math.min(...t), | ||
max: () => (t) => Math.max(...t), | ||
in: (t, e) => { | ||
const r = b(t), n = b(e); | ||
return (s) => n(s).includes(r(s)); | ||
const n = $(t), r = $(e); | ||
return (s) => r(s).includes(n(s)); | ||
}, | ||
"not in": (t, e) => { | ||
const r = N.in(t, e); | ||
return (n) => !r(n); | ||
const n = k.in(t, e); | ||
return (r) => !n(r); | ||
}, | ||
regex: (t, e, r) => { | ||
const n = new RegExp(e, r), s = b(t); | ||
return (i) => n.test(s(i)); | ||
regex: (t, e, n) => { | ||
const r = new RegExp(e, n), s = $(t); | ||
return (c) => r.test(s(c)); | ||
}, | ||
and: m((t, e) => t && e), | ||
or: m((t, e) => t || e), | ||
and: m((t, e) => !!(t && e)), | ||
or: m((t, e) => !!(t || e)), | ||
not: m((t) => !t), | ||
exists: m((t) => t !== void 0), | ||
exists: (t) => { | ||
const e = t.slice(1), n = e.pop(), r = k.get(...e); | ||
return (s) => { | ||
const c = r(s); | ||
return !!c && Object.hasOwnProperty.call(c, n); | ||
}; | ||
}, | ||
if: (t, e, n) => { | ||
const r = $(t), s = $(e), c = $(n); | ||
return (x) => r(x) ? s(x) : c(x); | ||
}, | ||
eq: m((t, e) => t === e), | ||
@@ -133,11 +143,11 @@ gt: m((t, e) => t > e), | ||
}, S = []; | ||
function b(t, e) { | ||
S.unshift({ ...N, ...S[0], ...e == null ? void 0 : e.functions }); | ||
function $(t, e) { | ||
S.unshift({ ...k, ...S[0], ...e == null ? void 0 : e.functions }); | ||
try { | ||
const r = E(t) ? P(t, S[0]) : () => t; | ||
return (n) => { | ||
const n = E(t) ? R(t, S[0]) : () => t; | ||
return (r) => { | ||
try { | ||
return r(n); | ||
return n(r); | ||
} catch (s) { | ||
throw s.jsonquery = [{ data: n, query: t }, ...s.jsonquery ?? []], s; | ||
throw s.jsonquery = [{ data: r, query: t }, ...s.jsonquery ?? []], s; | ||
} | ||
@@ -149,9 +159,9 @@ }; | ||
} | ||
function P(t, e) { | ||
const [r, ...n] = t, s = e[r]; | ||
function R(t, e) { | ||
const [n, ...r] = t, s = e[n]; | ||
if (!s) | ||
throw new Error(`Unknown function '${r}'`); | ||
return s(...n); | ||
throw new Error(`Unknown function '${n}'`); | ||
return s(...r); | ||
} | ||
const R = { | ||
const P = { | ||
and: "and", | ||
@@ -173,23 +183,23 @@ or: "or", | ||
"not in": "not in" | ||
}, W = /^[a-zA-Z_$][a-zA-Z\d_$]*$/, I = /^[a-zA-Z_$][a-zA-Z\d_$]*/, U = /^"(?:[^"\\]|\\.)*"/, F = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/, J = /^(0|[1-9][0-9]*)/, z = /^(true|false|null)/, B = /^[ \n\t\r]+/; | ||
function M(t, e) { | ||
const r = () => { | ||
}, L = /^[a-zA-Z_$][a-zA-Z\d_$]*$/, W = /^[a-zA-Z_$][a-zA-Z\d_$]*/, F = /^"(?:[^"\\]|\\.)*"/, U = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/, J = /^(0|[1-9][0-9]*)/, M = /^(true|false|null)/, T = /^[ \n\t\r]+/; | ||
function z(t, e) { | ||
const n = () => { | ||
h(); | ||
const o = n(); | ||
if (h(), t[c] === "|") { | ||
const g = [o]; | ||
for (; t[c] === "|"; ) | ||
c++, h(), g.push(n()); | ||
const u = r(); | ||
if (h(), t[o] === "|") { | ||
const g = [u]; | ||
for (; t[o] === "|"; ) | ||
o++, h(), g.push(r()); | ||
return ["pipe", ...g]; | ||
} | ||
return o; | ||
}, n = () => { | ||
const o = { ...R, ...e == null ? void 0 : e.operators }, g = s(); | ||
return u; | ||
}, r = () => { | ||
const u = { ...P, ...e == null ? void 0 : e.operators }, g = s(); | ||
h(); | ||
for (const x of Object.keys(o).sort((k, v) => v.length - k.length)) { | ||
const k = o[x]; | ||
if (t.substring(c, c + k.length) === k) { | ||
c += k.length, h(); | ||
for (const j of Object.keys(u).sort((N, v) => v.length - N.length)) { | ||
const N = u[j]; | ||
if (t.substring(o, o + N.length) === N) { | ||
o += N.length, h(); | ||
const v = s(); | ||
return [x, g, v]; | ||
return [j, g, v]; | ||
} | ||
@@ -199,85 +209,85 @@ } | ||
}, s = () => { | ||
if (t[c] === "(") { | ||
c++; | ||
const o = r(); | ||
return p(")"), o; | ||
if (t[o] === "(") { | ||
o++; | ||
const u = n(); | ||
return p(")"), u; | ||
} | ||
return i(); | ||
}, i = () => { | ||
const o = []; | ||
if (t[c] === ".") { | ||
for (; t[c] === "."; ) | ||
c++, o.push( | ||
a() ?? u() ?? f() ?? $("Property expected") | ||
return c(); | ||
}, c = () => { | ||
const u = []; | ||
if (t[o] === ".") { | ||
for (; t[o] === "."; ) | ||
o++, u.push( | ||
a() ?? i() ?? f() ?? b("Property expected") | ||
); | ||
return ["get", ...o]; | ||
return ["get", ...u]; | ||
} | ||
return j(); | ||
}, j = () => { | ||
const o = c, g = u(); | ||
if (h(), !g || t[c] !== "(") | ||
return c = o, w(); | ||
c++, !(e != null && e.functions[g]) && !N[g] && $(`Unknown function '${g}'`), h(); | ||
const x = t[c] !== ")" ? [r()] : []; | ||
for (; c < t.length && t[c] !== ")"; ) | ||
h(), p(","), x.push(r()); | ||
return p(")"), [g, ...x]; | ||
return x(); | ||
}, x = () => { | ||
const u = o, g = i(); | ||
if (h(), !g || t[o] !== "(") | ||
return o = u, w(); | ||
o++, !(e != null && e.functions[g]) && !k[g] && b(`Unknown function '${g}'`), h(); | ||
const j = t[o] !== ")" ? [n()] : []; | ||
for (; o < t.length && t[o] !== ")"; ) | ||
h(), p(","), j.push(n()); | ||
return p(")"), [g, ...j]; | ||
}, w = () => { | ||
if (t[c] === "{") { | ||
c++, h(); | ||
const o = {}; | ||
if (t[o] === "{") { | ||
o++, h(); | ||
const u = {}; | ||
let g = !0; | ||
for (; c < t.length && t[c] !== "}"; ) { | ||
for (; o < t.length && t[o] !== "}"; ) { | ||
g ? g = !1 : (p(","), h()); | ||
const x = a() ?? u() ?? f() ?? $("Key expected"); | ||
h(), p(":"), o[x] = r(); | ||
const j = a() ?? i() ?? f() ?? b("Key expected"); | ||
h(), p(":"), u[j] = n(); | ||
} | ||
return p("}"), ["object", o]; | ||
return p("}"), ["object", u]; | ||
} | ||
return y(); | ||
}, y = () => { | ||
if (t[c] === "[") { | ||
c++, h(); | ||
const o = []; | ||
if (t[o] === "[") { | ||
o++, h(); | ||
const u = []; | ||
let g = !0; | ||
for (; c < t.length && t[c] !== "]"; ) | ||
g ? g = !1 : (p(","), h()), o.push(r()); | ||
return p("]"), ["array", ...o]; | ||
for (; o < t.length && t[o] !== "]"; ) | ||
g ? g = !1 : (p(","), h()), u.push(n()); | ||
return p("]"), ["array", ...u]; | ||
} | ||
return a() ?? l() ?? _(); | ||
}, a = () => d(U, JSON.parse), u = () => d(I, (o) => o), l = () => d(F, JSON.parse), f = () => d(J, JSON.parse), _ = () => { | ||
const o = d(z, JSON.parse); | ||
if (o !== void 0) | ||
return o; | ||
$("Value expected"); | ||
}, O = () => { | ||
h(), c < t.length && $(`Unexpected part '${t.substring(c)}'`); | ||
}, d = (o, g) => { | ||
const x = t.substring(c).match(o); | ||
if (x) | ||
return c += x[0].length, g(x[0]); | ||
}, h = () => d(B, (o) => o), p = (o) => { | ||
t[c] !== o && $(`Character '${o}' expected`), c++; | ||
}, $ = (o, g = c) => { | ||
throw new SyntaxError(`${o} (pos: ${g})`); | ||
return a() ?? l() ?? O(); | ||
}, a = () => d(F, JSON.parse), i = () => d(W, (u) => u), l = () => d(U, JSON.parse), f = () => d(J, JSON.parse), O = () => { | ||
const u = d(M, JSON.parse); | ||
if (u !== void 0) | ||
return u; | ||
b("Value expected"); | ||
}, _ = () => { | ||
h(), o < t.length && b(`Unexpected part '${t.substring(o)}'`); | ||
}, d = (u, g) => { | ||
const j = t.substring(o).match(u); | ||
if (j) | ||
return o += j[0].length, g(j[0]); | ||
}, h = () => d(T, (u) => u), p = (u) => { | ||
t[o] !== u && b(`Character '${u}' expected`), o++; | ||
}, b = (u, g = o) => { | ||
throw new SyntaxError(`${u} (pos: ${g})`); | ||
}; | ||
let c = 0; | ||
const A = r(); | ||
return O(), A; | ||
let o = 0; | ||
const A = n(); | ||
return _(), A; | ||
} | ||
const T = 40, Z = " ", D = (t, e) => { | ||
const r = (e == null ? void 0 : e.indentation) ?? Z, n = (a, u) => E(a) ? s(a, u) : JSON.stringify(a), s = (a, u) => { | ||
const B = 40, Z = " ", D = (t, e) => { | ||
const n = (e == null ? void 0 : e.indentation) ?? Z, r = (a, i) => E(a) ? s(a, i) : JSON.stringify(a), s = (a, i) => { | ||
var h; | ||
const [l, ...f] = a; | ||
if (l === "get" && f.length > 0) | ||
return j(f); | ||
return x(f); | ||
if (l === "pipe") { | ||
const p = f.map(($) => n($, u + r)); | ||
const p = f.map((b) => r(b, i + n)); | ||
return y(p, ["", " | ", ""], ["", ` | ||
${u + r}| `, ""]); | ||
${i + n}| `, ""]); | ||
} | ||
if (l === "object") | ||
return i(f[0], u); | ||
return c(f[0], i); | ||
if (l === "array") { | ||
const p = f.map(($) => n($, u)); | ||
const p = f.map((b) => r(b, i)); | ||
return y( | ||
@@ -287,13 +297,13 @@ p, | ||
[`[ | ||
${u + r}`, `, | ||
${u + r}`, ` | ||
${u}]`] | ||
${i + n}`, `, | ||
${i + n}`, ` | ||
${i}]`] | ||
); | ||
} | ||
const _ = ((h = e == null ? void 0 : e.operators) == null ? void 0 : h[l]) ?? R[l]; | ||
if (_ && f.length === 2) { | ||
const [p, $] = f, c = n(p, u), A = n($, u); | ||
return `(${c} ${_} ${A})`; | ||
const O = ((h = e == null ? void 0 : e.operators) == null ? void 0 : h[l]) ?? P[l]; | ||
if (O && f.length === 2) { | ||
const [p, b] = f, o = r(p, i), A = r(b, i); | ||
return `(${o} ${O} ${A})`; | ||
} | ||
const O = f.length === 1 ? u : u + r, d = f.map((p) => n(p, O)); | ||
const _ = f.length === 1 ? i : i + n, d = f.map((p) => r(p, _)); | ||
return f.length === 1 && d[0][0] === "(" ? `${l}${d}` : y( | ||
@@ -303,9 +313,9 @@ d, | ||
f.length === 1 ? [`${l}(`, `, | ||
${u}`, ")"] : [`${l}( | ||
${O}`, `, | ||
${O}`, ` | ||
${u})`] | ||
${i}`, ")"] : [`${l}( | ||
${_}`, `, | ||
${_}`, ` | ||
${i})`] | ||
); | ||
}, i = (a, u) => { | ||
const l = u + r, f = Object.entries(a).map(([_, O]) => `${w(_)}: ${n(O, l)}`); | ||
}, c = (a, i) => { | ||
const l = i + n, f = Object.entries(a).map(([O, _]) => `${w(O)}: ${r(_, l)}`); | ||
return y( | ||
@@ -317,17 +327,17 @@ f, | ||
${l}`, ` | ||
${u}}`] | ||
${i}}`] | ||
); | ||
}, j = (a) => a.map((u) => `.${w(u)}`).join(""), w = (a) => W.test(a) ? a : JSON.stringify(a), y = (a, [u, l, f], [_, O, d]) => u.length + a.reduce((p, $) => p + $.length + l.length, 0) - l.length + f.length <= ((e == null ? void 0 : e.maxLineLength) ?? T) ? u + a.join(l) + f : _ + a.join(O) + d; | ||
return n(t, ""); | ||
}, x = (a) => a.map((i) => `.${w(i)}`).join(""), w = (a) => L.test(a) ? a : JSON.stringify(a), y = (a, [i, l, f], [O, _, d]) => i.length + a.reduce((p, b) => p + b.length + l.length, 0) - l.length + f.length <= ((e == null ? void 0 : e.maxLineLength) ?? B) ? i + a.join(l) + f : O + a.join(_) + d; | ||
return r(t, ""); | ||
}; | ||
function K(t, e, r) { | ||
return b(L(e) ? M(e, r) : e, r)(t); | ||
function K(t, e, n) { | ||
return $(I(e) ? z(e, n) : e, n)(t); | ||
} | ||
export { | ||
m as buildFunction, | ||
b as compile, | ||
$ as compile, | ||
K as jsonquery, | ||
M as parse, | ||
z as parse, | ||
D as stringify | ||
}; | ||
//# sourceMappingURL=jsonquery.js.map |
{ | ||
"name": "@jsonquerylang/jsonquery", | ||
"version": "3.1.1", | ||
"version": "4.0.0", | ||
"description": "A small, flexible, and expandable JSON query language", | ||
@@ -58,2 +58,3 @@ "keywords": [ | ||
"@vitest/coverage-v8": "2.1.2", | ||
"ajv": "8.17.1", | ||
"npm-run-all": "4.1.5", | ||
@@ -60,0 +61,0 @@ "semantic-release": "24.1.2", |
@@ -9,7 +9,7 @@ # JSON Query | ||
![JSON Query Overview](docs/jsonquery-overview.svg) | ||
![JSON Query Overview](https://jsonquerylang.org/jsonquery-overview.svg) | ||
## Features | ||
- Small: just `2.9 kB` when minified and gzipped! The JSON query engine without parse/stringify is only `1.3 kB`. | ||
- Small: just `3.0 kB` when minified and gzipped! The JSON query engine without parse/stringify is only `1.4 kB`. | ||
- Feature rich (40+ powerful functions) | ||
@@ -38,2 +38,3 @@ - Easy to interoperate with thanks to the intermediate JSON format. | ||
- [Function reference](reference/functions.md) | ||
- [Test Suite](test-suite/README.md) | ||
@@ -618,6 +619,16 @@ ## Installation | ||
Another gotcha is that unlike some other query languages, you need to use the `map` function to pick some properties out of an array _for every item_ in the array. When you're just picking a few fields without renaming them, you can use the function `pick` too, which is more concise. | ||
``` | ||
.friends | { firstName: .name, age: .age } WRONG | ||
.friends | map({ firstName: .name, age: .age }) RIGHT | ||
.friends | pick(.name, .age) RIGHT | ||
``` | ||
## Development | ||
To develop, check out the repo, install dependencies once, and then use the following scripts: | ||
### JavaScript | ||
To develop, check out the JavaScript repo, install dependencies once, and then use the following scripts: | ||
```text | ||
@@ -635,2 +646,8 @@ npm run test | ||
### Implement in a new language | ||
Support for JSON Query language can be implemented in new programming languages. Implementing the query engine is most straight forward: this boils down to implementing each of the functions (`sort`, `filter`, `groupBy`, etc.), and creating a compiler which can go through a JSON Query like `["sort", ["get", "name"], "desc"]` look up the function `sort`, and pass the arguments to it. Implementing a parser and stringifier is a bit more work, but the parser and stringifier of the JavaScript implementation can be used as a reference. | ||
There is a JSON based Test Suite available that can be used to ensure that your implementation matches the behavior of the reference implementation, see: [Test Suite](test-suite/README.md). | ||
## Motivation | ||
@@ -637,0 +654,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
84940
603
674
8