@josdejong/jsonquery
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAET,SAAS,EAIT,gBAAgB,EAGjB,MAAM,SAAS,CAAA;AAShB,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAU/E"} | ||
| {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAET,SAAS,EAIT,gBAAgB,EAGjB,MAAM,SAAS,CAAA;AAShB,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAqB/E"} |
+34
-27
@@ -1,2 +0,2 @@ | ||
| const m = (t) => Array.isArray(t), y = (t) => t && typeof t == "object" && !m(t), b = (t) => typeof t == "string", o = (t) => m(t) ? (n) => { | ||
| const m = (t) => Array.isArray(t), x = (t) => t && typeof t == "object" && !m(t), b = (t) => typeof t == "string", o = (t) => m(t) ? (n) => { | ||
| let e = n; | ||
@@ -6,6 +6,6 @@ for (const r of t) | ||
| return e; | ||
| } : (n) => n == null ? void 0 : n[t], d = (t) => () => t, h = (t) => { | ||
| } : (n) => n == null ? void 0 : n[t], S = (t) => () => t, h = (t) => { | ||
| const n = t.map((e) => u(e)); | ||
| return (e) => n.reduce((r, c) => c(r), e); | ||
| }, _ = (t) => { | ||
| }, y = (t) => { | ||
| const n = Object.keys(t).map((e) => [e, u(t[e])]); | ||
@@ -16,3 +16,3 @@ return (e) => { | ||
| }; | ||
| }, S = (t) => { | ||
| }, d = (t) => { | ||
| const n = u(t); | ||
@@ -26,4 +26,4 @@ return (e) => e.map(n); | ||
| function c(s, i) { | ||
| const f = e(s), O = e(i); | ||
| return f > O ? r : f < O ? -r : 0; | ||
| const f = e(s), j = e(i); | ||
| return f > j ? r : f < j ? -r : 0; | ||
| } | ||
@@ -36,4 +36,4 @@ return (s) => s.slice().sort(c); | ||
| ]); | ||
| return (e) => m(e) ? e.map((r) => j(r, n)) : j(e, n); | ||
| }, j = (t, n) => { | ||
| return (e) => m(e) ? e.map((r) => O(r, n)) : O(e, n); | ||
| }, O = (t, n) => { | ||
| const e = {}; | ||
@@ -43,3 +43,3 @@ return n.forEach(([r, c]) => { | ||
| }), e; | ||
| }, k = (t) => { | ||
| }, _ = (t) => { | ||
| const n = o(t); | ||
@@ -63,3 +63,3 @@ return (e) => { | ||
| }; | ||
| }, A = () => (t) => t.flat(), B = () => (t) => [...new Set(t)], w = (t) => (n) => Object.values(k(t)(n)).map((e) => e[0]), z = (t) => { | ||
| }, w = () => (t) => t.flat(), A = () => (t) => [...new Set(t)], B = (t) => (n) => Object.values(_(t)(n)).map((e) => e[0]), z = (t) => { | ||
| const n = u(t); | ||
@@ -70,3 +70,3 @@ return (e) => !n(e); | ||
| return (e) => n(e) !== void 0; | ||
| }, F = (t) => (n) => n.slice(0, t), P = () => Object.keys, R = () => Object.values, T = () => (t) => t.reduce((n, e) => n * e), x = () => (t) => t.reduce((n, e) => n + e), V = () => (t) => x()(t) / t.length, D = () => (t) => Math.min(...t), G = () => (t) => Math.max(...t), H = () => Math.abs, I = (t = 0) => (n) => +(Math.round(+(n + "e" + t)) + "e" + -t), J = () => (t) => t.length, K = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ | ||
| }, F = (t) => (n) => n.slice(0, t), P = () => Object.keys, R = () => Object.values, T = () => (t) => t.reduce((n, e) => n * e), k = () => (t) => t.reduce((n, e) => n + e), V = () => (t) => k()(t) / t.length, D = () => (t) => Math.min(...t), G = () => (t) => Math.max(...t), H = () => Math.abs, I = (t = 0) => (n) => +(Math.round(+(n + "e" + t)) + "e" + -t), J = () => (t) => t.length, K = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ | ||
| __proto__: null, | ||
@@ -77,13 +77,13 @@ abs: H, | ||
| filter: v, | ||
| flatten: A, | ||
| flatten: w, | ||
| get: o, | ||
| groupBy: k, | ||
| groupBy: _, | ||
| keyBy: N, | ||
| keys: P, | ||
| limit: F, | ||
| map: S, | ||
| map: d, | ||
| max: G, | ||
| min: D, | ||
| not: z, | ||
| object: _, | ||
| object: y, | ||
| pick: E, | ||
@@ -95,6 +95,6 @@ pipe: h, | ||
| sort: M, | ||
| string: d, | ||
| sum: x, | ||
| uniq: B, | ||
| uniqBy: w, | ||
| string: S, | ||
| sum: k, | ||
| uniq: A, | ||
| uniqBy: B, | ||
| values: R | ||
@@ -139,19 +139,26 @@ }, Symbol.toStringTag, { value: "Module" })), g = (t, n = !1) => (e, r) => { | ||
| } | ||
| }, a = [K], l = [W]; | ||
| }, l = [K], a = [W]; | ||
| function u(t, n) { | ||
| a.unshift({ ...a[0], ...n == null ? void 0 : n.functions }), l.unshift({ ...l[0], ...n == null ? void 0 : n.operators }); | ||
| l.unshift({ ...l[0], ...n == null ? void 0 : n.functions }), a.unshift({ ...a[0], ...n == null ? void 0 : n.operators }); | ||
| try { | ||
| return X(t); | ||
| const e = X(t); | ||
| return (r) => { | ||
| try { | ||
| return e(r); | ||
| } catch (c) { | ||
| throw c.jsonquery = [{ data: r, query: t }, ...c.jsonquery ?? []], c; | ||
| } | ||
| }; | ||
| } finally { | ||
| a.shift(), l.shift(); | ||
| l.shift(), a.shift(); | ||
| } | ||
| } | ||
| function X(t) { | ||
| if (y(t)) | ||
| return _(t); | ||
| if (x(t)) | ||
| return y(t); | ||
| if (m(t)) { | ||
| const [n, ...e] = t, r = a[0][n]; | ||
| const [n, ...e] = t, r = l[0][n]; | ||
| if (r) | ||
| return r(...e); | ||
| const [c, s, ...i] = t, f = l[0][s]; | ||
| const [c, s, ...i] = t, f = a[0][s]; | ||
| return f ? f(c, ...i) : h(t); | ||
@@ -158,0 +165,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"jsonquery.js","sources":["../src/is.ts","../src/functions.ts","../src/operators.ts","../src/compile.ts","../src/jsonquery.ts"],"sourcesContent":["export const isArray = <T>(value: unknown): value is T[] => Array.isArray(value)\n\nexport const isObject = (value: unknown): value is object =>\n value && typeof value === 'object' && !isArray(value)\n\nexport const isString = (value: unknown): value is string => typeof value === 'string'\n","import { Getter, JSONPath, JSONProperty, JSONQuery, JSONQueryObject } from './types'\nimport { compile } from './compile'\nimport { isArray, isString } from './is'\n\nexport const get = (path: JSONPath | JSONProperty) =>\n isArray(path)\n ? (data: unknown) => {\n let value = data\n\n for (const prop of path) {\n value = value?.[prop]\n }\n\n return value\n }\n : (data: unknown) => data?.[path]\n\nexport const string = (text: string) => () => text\n\nexport const pipe = (entries: JSONQuery[]) => {\n const _entries = entries.map((entry) => compile(entry))\n return (data: unknown) => _entries.reduce((data, evaluator) => evaluator(data), data)\n}\n\nexport const object = (query: JSONQueryObject) => {\n const getters: Getter[] = Object.keys(query).map((key) => [key, compile(query[key])])\n\n return (data: unknown) => {\n const obj = {}\n getters.forEach(([key, getter]) => (obj[key] = getter(data)))\n return obj\n }\n}\n\nexport const map = <T>(callback: JSONQuery) => {\n const _callback = compile(callback)\n return (data: T[]) => data.map(_callback)\n}\n\nexport const filter = <T>(predicate: JSONQuery) => {\n const _predicate = compile(predicate)\n return (data: T[]) => data.filter(_predicate)\n}\n\nexport const sort = <T>(path: JSONPath | JSONProperty = [], direction?: 'asc' | 'desc') => {\n const getter = get(path)\n const sign = direction === 'desc' ? -1 : 1\n\n function compare(itemA: unknown, itemB: unknown) {\n const a = getter(itemA)\n const b = getter(itemB)\n return a > b ? sign : a < b ? -sign : 0\n }\n\n return (data: T[]) => data.slice().sort(compare)\n}\n\nexport const pick = (...paths: (JSONPath | JSONProperty)[]) => {\n const getters: Getter[] = paths.map((path) => [\n isString(path) ? path : path[path.length - 1],\n get(path)\n ])\n\n return (data: Record<string, unknown>): unknown => {\n if (isArray(data)) {\n return data.map((item: Record<string, unknown>) => _pick(item, getters))\n }\n\n return _pick(data, getters)\n }\n}\n\nconst _pick = (object: Record<string, unknown>, getters: Getter[]): unknown => {\n const out = {}\n\n getters.forEach(([key, getter]) => {\n out[key] = getter(object)\n })\n\n return out\n}\n\nexport const groupBy = <T>(path: JSONPath | JSONProperty) => {\n const getter = get(path)\n\n return (data: T[]) => {\n const res = {}\n\n for (const item of data) {\n const value = getter(item) as string\n if (res[value]) {\n res[value].push(item)\n } else {\n res[value] = [item]\n }\n }\n\n return res\n }\n}\n\nexport const keyBy = <T>(path: JSONPath | JSONProperty) => {\n const getter = get(path)\n\n return (data: T[]) => {\n const res = {}\n\n data.forEach((item) => {\n const value = getter(item) as string\n res[value] = res[value] ?? item\n })\n\n return res\n }\n}\n\nexport const flatten = () => (data: unknown[]) => data.flat()\n\nexport const uniq =\n () =>\n <T>(data: T[]) => [...new Set(data)]\n\nexport const uniqBy =\n <T>(path: JSONPath | JSONProperty) =>\n (data: T[]): T[] =>\n Object.values(groupBy(path)(data)).map((groups) => groups[0])\n\n// operator not (looks like a function because it has no left operand)\nexport const not = (query: JSONQuery) => {\n const getter = compile(query)\n return (data: unknown) => !getter(data)\n}\n\n// operator exists (looks like a function because it has no left operand)\nexport const exists = (path: JSONPath) => {\n const getter = get(path)\n return (data: unknown) => getter(data) !== undefined\n}\n\nexport const limit =\n (count: number) =>\n <T>(data: T[]) =>\n data.slice(0, count)\n\nexport const keys = () => Object.keys\n\nexport const values = () => Object.values\n\nexport const prod = () => (data: number[]) => data.reduce((a, b) => a * b)\n\nexport const sum = () => (data: number[]) => data.reduce((a, b) => a + b)\n\nexport const average = () => (data: number[]) => sum()(data) / data.length\n\nexport const min = () => (data: number[]) => Math.min(...data)\n\nexport const max = () => (data: number[]) => Math.max(...data)\n\nexport const abs = () => Math.abs\n\nexport const round =\n (digits = 0) =>\n (data: number) => {\n const num = Math.round(Number(data + 'e' + digits))\n return Number(num + 'e' + -digits)\n }\n\nexport const size =\n () =>\n <T>(data: T[]) =>\n data.length\n","import { JSONQuery, Operator, OperatorMap } from './types'\nimport { get } from './functions'\nimport { isString } from './is'\nimport { compile } from './compile'\n\nexport const createOperatorCompiler =\n (op: Operator, textRule = false) =>\n (left: JSONQuery, right: JSONQuery) => {\n // textRule is a special rule: relational operators interpret a string\n // on the right side as a text and not a path\n const a = compile(left)\n const b = textRule && isString(right) ? () => right : compile(right)\n return (data: unknown) => op(a(data), b(data))\n }\n\nconst mapValues = <T, U>(object: Record<string, T>, callback: (value: T) => U) =>\n Object.keys(object).reduce((res, key) => {\n res[key] = callback(object[key])\n\n return res\n }, {})\n\nexport const arithmeticOperators: Record<string, Operator> = {\n '+': (a: number, b: number) => a + b,\n '-': (a: number, b: number) => a - b,\n '*': (a: number, b: number) => a * b,\n '/': (a: number, b: number) => a / b,\n '^': (a: number, b: number) => a ** b,\n '%': (a: number, b: number) => a % b\n}\n\nexport const logicalOperators: Record<string, Operator> = {\n and: (a, b) => a && b,\n or: (a, b) => a || b\n}\n\nexport const relationalOperators: Record<string, Operator> = {\n '==': (a, b) => a === b, // we use strict comparison\n '>': (a, b) => a > b,\n '>=': (a, b) => a >= b,\n '<': (a, b) => a < b,\n '<=': (a, b) => a <= b,\n '!=': (a, b) => a !== b // we use strict comparison\n}\n\nexport const coreOperators: OperatorMap = {\n ...mapValues(arithmeticOperators, createOperatorCompiler),\n ...mapValues(logicalOperators, createOperatorCompiler),\n ...mapValues(relationalOperators, (value) => createOperatorCompiler(value, true)),\n\n in: (path: string, values: string[]) => {\n const getter = get(path)\n return (data: unknown) => values.includes(getter(data))\n },\n\n 'not in': (path: string, values: string[]) => {\n const getter = get(path)\n return (data: unknown) => !values.includes(getter(data))\n },\n\n regex: (path: string, expression: string, options?: string) => {\n const regex = new RegExp(expression, options)\n const getter = get(path)\n return (data: unknown) => regex.test(getter(data) as string)\n }\n}\n","import {\n Evaluator,\n FunctionsMap,\n JSONQuery,\n JSONQueryFunction,\n JSONQueryObject,\n JSONQueryOperator,\n JSONQueryOptions,\n JSONQueryPipe,\n OperatorMap\n} from './types'\nimport { isArray, isObject, isString } from './is'\nimport * as coreFunctions from './functions'\nimport { get, object, pipe } from './functions'\nimport { coreOperators } from './operators'\n\nconst functionsStack: FunctionsMap[] = [coreFunctions]\nconst operatorsStack: OperatorMap[] = [coreOperators]\n\nexport function compile(query: JSONQuery, options?: JSONQueryOptions): Evaluator {\n functionsStack.unshift({ ...functionsStack[0], ...options?.functions })\n operatorsStack.unshift({ ...operatorsStack[0], ...options?.operators })\n\n try {\n return _compile(query)\n } finally {\n functionsStack.shift()\n operatorsStack.shift()\n }\n}\n\nfunction _compile(query: JSONQuery): Evaluator {\n // object\n if (isObject(query)) {\n return object(query as JSONQueryObject)\n }\n\n if (isArray(query)) {\n // function\n const [fnName, ...args] = query as unknown as JSONQueryFunction\n const fn = functionsStack[0][fnName]\n if (fn) {\n return fn(...args)\n }\n\n // operator\n const [left, opName, ...right] = query as unknown as JSONQueryOperator\n const op = operatorsStack[0][opName]\n if (op) {\n return op(left, ...right)\n }\n\n // pipe\n return pipe(query as JSONQueryPipe)\n }\n\n // property\n if (isString(query)) {\n return get(query)\n }\n\n // value\n return () => query\n}\n","import { JSONQuery, JSONQueryOptions } from './types'\nimport { compile } from './compile'\n\nexport function jsonquery(data: unknown, query: JSONQuery, options?: JSONQueryOptions): unknown {\n return compile(query, options)(data)\n}\n"],"names":["isArray","value","isObject","isString","get","path","data","prop","string","text","pipe","entries","_entries","entry","compile","evaluator","object","query","getters","key","obj","getter","map","callback","_callback","filter","predicate","_predicate","sort","direction","sign","compare","itemA","itemB","a","b","pick","paths","item","_pick","out","groupBy","res","keyBy","flatten","uniq","uniqBy","groups","not","exists","limit","count","keys","values","prod","sum","average","min","max","abs","round","digits","size","createOperatorCompiler","op","textRule","left","right","mapValues","arithmeticOperators","logicalOperators","relationalOperators","coreOperators","expression","options","regex","functionsStack","coreFunctions","operatorsStack","_compile","fnName","args","fn","opName","jsonquery"],"mappings":"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"} | ||
| {"version":3,"file":"jsonquery.js","sources":["../src/is.ts","../src/functions.ts","../src/operators.ts","../src/compile.ts","../src/jsonquery.ts"],"sourcesContent":["export const isArray = <T>(value: unknown): value is T[] => Array.isArray(value)\n\nexport const isObject = (value: unknown): value is object =>\n value && typeof value === 'object' && !isArray(value)\n\nexport const isString = (value: unknown): value is string => typeof value === 'string'\n","import { Getter, JSONPath, JSONProperty, JSONQuery, JSONQueryObject } from './types'\nimport { compile } from './compile'\nimport { isArray, isString } from './is'\n\nexport const get = (path: JSONPath | JSONProperty) =>\n isArray(path)\n ? (data: unknown) => {\n let value = data\n\n for (const prop of path) {\n value = value?.[prop]\n }\n\n return value\n }\n : (data: unknown) => data?.[path]\n\nexport const string = (text: string) => () => text\n\nexport const pipe = (entries: JSONQuery[]) => {\n const _entries = entries.map((entry) => compile(entry))\n return (data: unknown) => _entries.reduce((data, evaluator) => evaluator(data), data)\n}\n\nexport const object = (query: JSONQueryObject) => {\n const getters: Getter[] = Object.keys(query).map((key) => [key, compile(query[key])])\n\n return (data: unknown) => {\n const obj = {}\n getters.forEach(([key, getter]) => (obj[key] = getter(data)))\n return obj\n }\n}\n\nexport const map = <T>(callback: JSONQuery) => {\n const _callback = compile(callback)\n return (data: T[]) => data.map(_callback)\n}\n\nexport const filter = <T>(predicate: JSONQuery) => {\n const _predicate = compile(predicate)\n return (data: T[]) => data.filter(_predicate)\n}\n\nexport const sort = <T>(path: JSONPath | JSONProperty = [], direction?: 'asc' | 'desc') => {\n const getter = get(path)\n const sign = direction === 'desc' ? -1 : 1\n\n function compare(itemA: unknown, itemB: unknown) {\n const a = getter(itemA)\n const b = getter(itemB)\n return a > b ? sign : a < b ? -sign : 0\n }\n\n return (data: T[]) => data.slice().sort(compare)\n}\n\nexport const pick = (...paths: (JSONPath | JSONProperty)[]) => {\n const getters: Getter[] = paths.map((path) => [\n isString(path) ? path : path[path.length - 1],\n get(path)\n ])\n\n return (data: Record<string, unknown>): unknown => {\n if (isArray(data)) {\n return data.map((item: Record<string, unknown>) => _pick(item, getters))\n }\n\n return _pick(data, getters)\n }\n}\n\nconst _pick = (object: Record<string, unknown>, getters: Getter[]): unknown => {\n const out = {}\n\n getters.forEach(([key, getter]) => {\n out[key] = getter(object)\n })\n\n return out\n}\n\nexport const groupBy = <T>(path: JSONPath | JSONProperty) => {\n const getter = get(path)\n\n return (data: T[]) => {\n const res = {}\n\n for (const item of data) {\n const value = getter(item) as string\n if (res[value]) {\n res[value].push(item)\n } else {\n res[value] = [item]\n }\n }\n\n return res\n }\n}\n\nexport const keyBy = <T>(path: JSONPath | JSONProperty) => {\n const getter = get(path)\n\n return (data: T[]) => {\n const res = {}\n\n data.forEach((item) => {\n const value = getter(item) as string\n res[value] = res[value] ?? item\n })\n\n return res\n }\n}\n\nexport const flatten = () => (data: unknown[]) => data.flat()\n\nexport const uniq =\n () =>\n <T>(data: T[]) => [...new Set(data)]\n\nexport const uniqBy =\n <T>(path: JSONPath | JSONProperty) =>\n (data: T[]): T[] =>\n Object.values(groupBy(path)(data)).map((groups) => groups[0])\n\n// operator not (looks like a function because it has no left operand)\nexport const not = (query: JSONQuery) => {\n const getter = compile(query)\n return (data: unknown) => !getter(data)\n}\n\n// operator exists (looks like a function because it has no left operand)\nexport const exists = (path: JSONPath) => {\n const getter = get(path)\n return (data: unknown) => getter(data) !== undefined\n}\n\nexport const limit =\n (count: number) =>\n <T>(data: T[]) =>\n data.slice(0, count)\n\nexport const keys = () => Object.keys\n\nexport const values = () => Object.values\n\nexport const prod = () => (data: number[]) => data.reduce((a, b) => a * b)\n\nexport const sum = () => (data: number[]) => data.reduce((a, b) => a + b)\n\nexport const average = () => (data: number[]) => sum()(data) / data.length\n\nexport const min = () => (data: number[]) => Math.min(...data)\n\nexport const max = () => (data: number[]) => Math.max(...data)\n\nexport const abs = () => Math.abs\n\nexport const round =\n (digits = 0) =>\n (data: number) => {\n const num = Math.round(Number(data + 'e' + digits))\n return Number(num + 'e' + -digits)\n }\n\nexport const size =\n () =>\n <T>(data: T[]) =>\n data.length\n","import { JSONQuery, Operator, OperatorMap } from './types'\nimport { get } from './functions'\nimport { isString } from './is'\nimport { compile } from './compile'\n\nexport const createOperatorCompiler =\n (op: Operator, textRule = false) =>\n (left: JSONQuery, right: JSONQuery) => {\n // textRule is a special rule: relational operators interpret a string\n // on the right side as a text and not a path\n const a = compile(left)\n const b = textRule && isString(right) ? () => right : compile(right)\n return (data: unknown) => op(a(data), b(data))\n }\n\nconst mapValues = <T, U>(object: Record<string, T>, callback: (value: T) => U) =>\n Object.keys(object).reduce((res, key) => {\n res[key] = callback(object[key])\n\n return res\n }, {})\n\nexport const arithmeticOperators: Record<string, Operator> = {\n '+': (a: number, b: number) => a + b,\n '-': (a: number, b: number) => a - b,\n '*': (a: number, b: number) => a * b,\n '/': (a: number, b: number) => a / b,\n '^': (a: number, b: number) => a ** b,\n '%': (a: number, b: number) => a % b\n}\n\nexport const logicalOperators: Record<string, Operator> = {\n and: (a, b) => a && b,\n or: (a, b) => a || b\n}\n\nexport const relationalOperators: Record<string, Operator> = {\n '==': (a, b) => a === b, // we use strict comparison\n '>': (a, b) => a > b,\n '>=': (a, b) => a >= b,\n '<': (a, b) => a < b,\n '<=': (a, b) => a <= b,\n '!=': (a, b) => a !== b // we use strict comparison\n}\n\nexport const coreOperators: OperatorMap = {\n ...mapValues(arithmeticOperators, createOperatorCompiler),\n ...mapValues(logicalOperators, createOperatorCompiler),\n ...mapValues(relationalOperators, (value) => createOperatorCompiler(value, true)),\n\n in: (path: string, values: string[]) => {\n const getter = get(path)\n return (data: unknown) => values.includes(getter(data))\n },\n\n 'not in': (path: string, values: string[]) => {\n const getter = get(path)\n return (data: unknown) => !values.includes(getter(data))\n },\n\n regex: (path: string, expression: string, options?: string) => {\n const regex = new RegExp(expression, options)\n const getter = get(path)\n return (data: unknown) => regex.test(getter(data) as string)\n }\n}\n","import {\n Evaluator,\n FunctionsMap,\n JSONQuery,\n JSONQueryFunction,\n JSONQueryObject,\n JSONQueryOperator,\n JSONQueryOptions,\n JSONQueryPipe,\n OperatorMap\n} from './types'\nimport { isArray, isObject, isString } from './is'\nimport * as coreFunctions from './functions'\nimport { get, object, pipe } from './functions'\nimport { coreOperators } from './operators'\n\nconst functionsStack: FunctionsMap[] = [coreFunctions]\nconst operatorsStack: OperatorMap[] = [coreOperators]\n\nexport function compile(query: JSONQuery, options?: JSONQueryOptions): Evaluator {\n functionsStack.unshift({ ...functionsStack[0], ...options?.functions })\n operatorsStack.unshift({ ...operatorsStack[0], ...options?.operators })\n\n try {\n const exec = _compile(query)\n\n return (data) => {\n try {\n return exec(data)\n } catch (err) {\n // attach a stack to the error\n err.jsonquery = [{ data, query }, ...(err.jsonquery ?? [])]\n\n throw err\n }\n }\n } finally {\n functionsStack.shift()\n operatorsStack.shift()\n }\n}\n\nfunction _compile(query: JSONQuery): Evaluator {\n // object\n if (isObject(query)) {\n return object(query as JSONQueryObject)\n }\n\n if (isArray(query)) {\n // function\n const [fnName, ...args] = query as unknown as JSONQueryFunction\n const fn = functionsStack[0][fnName]\n if (fn) {\n return fn(...args)\n }\n\n // operator\n const [left, opName, ...right] = query as unknown as JSONQueryOperator\n const op = operatorsStack[0][opName]\n if (op) {\n return op(left, ...right)\n }\n\n // pipe\n return pipe(query as JSONQueryPipe)\n }\n\n // property\n if (isString(query)) {\n return get(query)\n }\n\n // value\n return () => query\n}\n","import { JSONQuery, JSONQueryOptions } from './types'\nimport { compile } from './compile'\n\nexport function jsonquery(data: unknown, query: JSONQuery, options?: JSONQueryOptions): unknown {\n return compile(query, options)(data)\n}\n"],"names":["isArray","value","isObject","isString","get","path","data","prop","string","text","pipe","entries","_entries","entry","compile","evaluator","object","query","getters","key","obj","getter","map","callback","_callback","filter","predicate","_predicate","sort","direction","sign","compare","itemA","itemB","a","b","pick","paths","item","_pick","out","groupBy","res","keyBy","flatten","uniq","uniqBy","groups","not","exists","limit","count","keys","values","prod","sum","average","min","max","abs","round","digits","size","createOperatorCompiler","op","textRule","left","right","mapValues","arithmeticOperators","logicalOperators","relationalOperators","coreOperators","expression","options","regex","functionsStack","coreFunctions","operatorsStack","exec","_compile","err","fnName","args","fn","opName","jsonquery"],"mappings":"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"} |
+1
-1
| { | ||
| "name": "@josdejong/jsonquery", | ||
| "version": "1.1.6", | ||
| "version": "1.2.0", | ||
| "description": "A small, flexible, and expandable JSON query language", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+172
-25
@@ -117,30 +117,49 @@ # JSON Query | ||
| - `options` is an optional object that can contain the following properties: | ||
| - `functions` is an optional map with extra function creators. A function creator has optional arguments as input and must return a function that can be used to process the query data. For example: | ||
| ```js | ||
| const options = { | ||
| functions: { | ||
| // usage example: ["times", 3] | ||
| times: (value) => (data) => data.map((item) => item * value) | ||
| } | ||
| } | ||
| ``` | ||
| - `operators` is an optional map with extra operator creators. An operator creator receives the left and right side queries as input, and must return a function that implements the operator. Example: | ||
| - `functions` is an optional map with custom function creators. A function creator has optional arguments as input and must return a function that can be used to process the query data. For example: | ||
| ```js | ||
| const options = { | ||
| operators: { | ||
| // a loosely equal operator | ||
| // usage example: ["value", "~=", 2] | ||
| '~=': (left, right) => { | ||
| const a = compile(left) | ||
| const b = compile(right) | ||
| return (data) => a(data) == b(data) | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ```js | ||
| const options = { | ||
| functions: { | ||
| // usage example: ["times", 3] | ||
| times: (value) => (data) => data.map((item) => item * value) | ||
| } | ||
| } | ||
| ``` | ||
| Example: | ||
| If the parameters are not a primitive value but can be a query themselves, the function `compile` can be used to compile them. For example, the actual implementation of the function `filter` is the following: | ||
| ```js | ||
| const options = { | ||
| functions: { | ||
| // usage example: ["filter", ["age", ">", 20 ]] | ||
| filter: (predicate) => { | ||
| const _predicate = compile(predicate) | ||
| return (data) => data.filter(_predicate) | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| You can have a look at the source code of the functions in `/src/functions.ts` for more examples. | ||
| - `operators` is an optional map with custom operator creators. An operator creator receives the left and right side queries as input, and must return a function that implements the operator. Example: | ||
| ```js | ||
| const options = { | ||
| operators: { | ||
| // a loosely equal operator | ||
| // usage example: ["value", "~=", 2] | ||
| '~=': (left, right) => { | ||
| const a = compile(left) | ||
| const b = compile(right) | ||
| return (data) => a(data) == b(data) | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| You can have a look at the source code of the functions in `/src/operators.ts` for more examples. | ||
| Here an example of using the function `jsonquery`: | ||
| ```js | ||
@@ -190,2 +209,53 @@ import { jsonquery } from '@josdejong/jsonquery' | ||
| ## error handling | ||
| When executing a query throws an error, the library attaches a stack to the error message which can give insight in what went wrong. The stack can be found at the property `error.jsonquery` and has type `Array<{ data: unknown, query: JSONQuery }>`. | ||
| ```js | ||
| const data = [ | ||
| { "name": "Chris", "age": 23, "scores": [7.2, 5, 8.0] }, | ||
| { "name": "Emily", "age": 19 }, // scores is missing here! | ||
| { "name": "Joe", "age": 32, "scores": [6.1, 8.1] } | ||
| ] | ||
| try { | ||
| jsonquery(data, [ | ||
| ["pick", "age", "scores"], | ||
| ["map", ["scores", ["sum"]]] | ||
| ]) | ||
| } catch (err) { | ||
| console.log(err.jsonquery) | ||
| // error stack: | ||
| // [ | ||
| // { | ||
| // "data": [ | ||
| // { "name": "Chris", "age": 23, "scores": [7.2, 5, 8.0] }, | ||
| // { "name": "Emily", "age": 19 }, | ||
| // { "name": "Joe", "age": 32, "scores": [6.1, 8.1] } | ||
| // ], | ||
| // "query": [ | ||
| // ["pick", "age", "scores"], | ||
| // ["map", ["scores", ["sum"]]] | ||
| // ] | ||
| // }, | ||
| // { | ||
| // "data": [ | ||
| // { "age": 23, "scores": [7.2, 5, 8.0] }, | ||
| // { "age": 19 }, | ||
| // { "age": 32, "scores": [6.1, 8.1] } | ||
| // ], | ||
| // "query": ["map", ["scores", ["sum"]]] | ||
| // }, | ||
| // { | ||
| // "data": {"age": 19}, | ||
| // "query": ["scores", ["sum"]] | ||
| // }, | ||
| // { | ||
| // "data" : undefined, | ||
| // "query": ["sum"] | ||
| // } | ||
| // ] | ||
| } | ||
| ``` | ||
| ## Syntax | ||
@@ -305,2 +375,3 @@ | ||
| ``` | ||
| There is one special case regarding paths: | ||
@@ -310,2 +381,3 @@ | ||
| f | ||
| ```js | ||
@@ -1326,2 +1398,77 @@ const data = { sort: 42 } | ||
| ## Limitations | ||
| The JSON Query language has some limitations, pitfalls, and gotchas. | ||
| Though the language is easy to learn and understand, it is relatively verbose due to the need for quotes around all keys, and the need for a lot of arrays in square brackets `[...]`. This is a consequence of expressing a query using JSON whilst wanting to keep the language concise. | ||
| The use of arrays `[...]` is quite overloaded. An array can hold a function call, operator, pipe, or path with properties. Given a query being an array containing three strings `[string, string, string]` for example, it's meaning can only be determined by looking up whether the first string matches a known function, then looking up whether the second string matches a known operator, and lastly conclude that it is a path with properties. When making a mistake, the error message you get is mostly unhelpful, and the best way to debug is to build your query step by step, validating that it works after every step. | ||
| What can also be confusing at first is to understand how data is piped through the query. A traditional function call is for example `abs(myValue)`, so you may expect to have to write this in JSON Query like `["abs", "myValue"]`. However, JSON Query has a functional approach where we create a pipeline like: `data -> abs -> result`. So, to get the absolute value of a property `myValue`, you will have to write a pipe first getting this property and then calling abs: `[["get", "myValue"], ["abs"]]"`. | ||
| ### Gotchas | ||
| Here some gotchas. | ||
| 1. Having an problem halfway the query, resulting in a vague error. In the following example, the first part of the query results in `undefined`, and then we try to filter that, resulting in an error: | ||
| ```js | ||
| const data = { | ||
| "friends": [ | ||
| {"name": "Chris", "age": 23, "city": "New York"}, | ||
| {"name": "Emily", "age": 19, "city": "Atlanta"}, | ||
| {"name": "Joe", "age": 16, "city": "New York"} | ||
| ] | ||
| } | ||
| const result = jsonquery(data, [ | ||
| ["get", "friiends"], | ||
| ["filter", ["city", "==", "New York"]] | ||
| ]) | ||
| // result: "Error: e is undefined" | ||
| // expected: an array with two items | ||
| ``` | ||
| 2. Making a typo in a function name, which then is interpreted as getting a property. This results in vague output or in an error. In the following example, the property `"filte"` is read from the data, resulting in `undefined`. After that, the property `"city"` is read from `undefined`, resulting in `undefined`, and lastly, we check whether `undefined` is equal to the string `"New York"`, which is not the case, so, the query returns `false`. | ||
| ```js | ||
| const data = [ | ||
| {"name": "Chris", "age": 23, "city": "New York"}, | ||
| {"name": "Emily", "age": 19, "city": "Atlanta"}, | ||
| {"name": "Joe", "age": 16, "city": "New York"} | ||
| ] | ||
| const result = jsonquery(data, ["filte", ["city", "==", "New York"]]) | ||
| // result: the boolean value false | ||
| // expected: an array with two items | ||
| ``` | ||
| 3. Making a typo in a property name, resulting in unexpected results. | ||
| ```js | ||
| const data = [ | ||
| {"name": "Chris", "age": 23, "city": "New York"}, | ||
| {"name": "Emily", "age": 19, "city": "Atlanta"}, | ||
| {"name": "Joe", "age": 16, "city": "New York"} | ||
| ] | ||
| const result = jsonquery(data, ["filter", ["cities", "==", "New York"]]) | ||
| // result: an empty array | ||
| // expected: an array with two items | ||
| ``` | ||
| 4. Forgetting brackets around a nested query. In the following example, the filter condition has no brackets. Therefore, the property `"city"` is used as condition and the arguments `"=="` and `"New York"` are ignored. | ||
| ```js | ||
| const data = [ | ||
| {"name": "Chris", "age": 23, "city": "New York"}, | ||
| {"name": "Emily", "age": 19, "city": "Atlanta"}, | ||
| {"name": "Joe", "age": 16, "city": "New York"} | ||
| ] | ||
| const result = jsonquery(data, ["filter", "age", ">", 18]) | ||
| // result: the original data | ||
| // expected: an array with two items | ||
| ``` | ||
| ## Development | ||
@@ -1328,0 +1475,0 @@ |
70389
10.44%237
3.04%1505
10.82%