diff --git a/package-lock.json b/package-lock.json index f43d2db..665e1fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@astrojs/svelte": "7.0.9", "@fortawesome/free-brands-svg-icons": "6.7.2", "@fortawesome/free-solid-svg-icons": "6.7.2", - "@jsonquerylang/jsonquery": "4.1.1", + "@jsonquerylang/jsonquery": "5.0.0", "astro": "5.5.6", "fracturedjsonjs": "4.0.2", "rehype-autolink-headings": "7.1.0", @@ -1190,9 +1190,9 @@ } }, "node_modules/@jsonquerylang/jsonquery": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@jsonquerylang/jsonquery/-/jsonquery-4.1.1.tgz", - "integrity": "sha512-Rfyvq70Zrb561BqSuXLsl0rG0/1tz913EQDL/4zpkp+laFGUxXIVPSaJWcdREJwADXLZDkQyaWplzEaPQvh+7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@jsonquerylang/jsonquery/-/jsonquery-5.0.0.tgz", + "integrity": "sha512-RkC/sqdnsfTEP1LlagqTfem+GHDiTT1gwhO/WN7JUF96e61kyVKDIhvfnvmQsRJdHL40qVTtaGIk0Hg1R2Oj1w==", "license": "ISC", "bin": { "jsonquery": "bin/cli.js" diff --git a/package.json b/package.json index 9431c20..2941357 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@astrojs/svelte": "7.0.9", "@fortawesome/free-brands-svg-icons": "6.7.2", "@fortawesome/free-solid-svg-icons": "6.7.2", - "@jsonquerylang/jsonquery": "4.1.1", + "@jsonquerylang/jsonquery": "5.0.0", "astro": "5.5.6", "fracturedjsonjs": "4.0.2", "rehype-autolink-headings": "7.1.0", diff --git a/src/components/QuickReference.svelte b/src/components/QuickReference.svelte index c9f22b4..22c7912 100644 --- a/src/components/QuickReference.svelte +++ b/src/components/QuickReference.svelte @@ -147,8 +147,8 @@ let selectedDoc: ReferenceDoc | undefined = $state() font-size: var(--font-size-mono); } - pre code { - background: none; + pre { + margin: 2px; } .quick-reference-button { diff --git a/src/components/data/examples.ts b/src/components/data/examples.ts index 08d76c1..28cae94 100644 --- a/src/components/data/examples.ts +++ b/src/components/data/examples.ts @@ -37,7 +37,7 @@ export const examples: Example[] = [ { name: 'example 2', input: input2, - query: `filter((.city == "New York") and (.age > 30))\n` + query: `filter(.city == "New York" and .age > 30)\n` }, { name: 'example 3', diff --git a/src/components/data/reference.json b/src/components/data/reference.json index fe333cd..032296e 100644 --- a/src/components/data/reference.json +++ b/src/components/data/reference.json @@ -58,9 +58,9 @@ }, { "name": "Operator", - "syntax": "(left operator right)", - "description": "JSON Query supports all basic operators. When composing multiple operators,\n it is necessary to use parentheses. Operators do not have precedence since\n parentheses are required.", - "examples": ["(.age >= 18)", "(.age >= 18) and (.age <= 65))"], + "syntax": "left operator right", + "description": "JSON Query supports all basic operators. Operators must have both a left and right hand side. To override the default precedence, an operator can be wrapped in parentheses (...).", + "examples": [".age >= 18", "filter(.age >= 18 and .age <= 65)"], "documentation": { "title": "Operators", "urlAnchor": "operators" }, "references": [ { "title": "equal", "urlAnchor": "eq", "syntax": "a == b" }, diff --git a/src/content/documentation.md b/src/content/documentation.md index 040d36a..7d6c33a 100644 --- a/src/content/documentation.md +++ b/src/content/documentation.md @@ -46,7 +46,7 @@ The following table gives an overview of the JSON query Text Format: | Type | Syntax | Example | |-------------------------|------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------| | [Function](#functions) | `name(argument1, argument2, ...)` | `sort(.age, "asc")` | -| [Operator](#operators) | `(left operator right)` | `filter(.age >= 18)` | +| [Operator](#operators) | `left operator right` | `filter(.age >= 18)` | | [Pipe](#pipes) | query1 | query2 | ... | sort(.age) | pick(.name, .age) | | [Object](#objects) | `{ prop1: query1, prop2: query2, ... }` | `{ names: map(.name), total: sum() }` | | [Array](#arrays) | `[ item1, item2, ... ]` | `[ "New York", "Atlanta" ]` | @@ -94,16 +94,16 @@ See page [Function reference](/reference) for a detailed overview of all availab ### Operators -JSON Query supports all basic operators. Operators must be wrapped in parentheses `(...)`, must have both a left and right hand side, and do not have precedence since parentheses are required. The syntax is: +JSON Query supports all basic operators. Operators must have both a left and right hand side. To override the default precedence, an operator can be wrapped in parentheses `(...)`. The syntax is: ```text -(left operator right) +left operator right ``` The following example tests whether a property `age` is greater than or equal to `18`: ```text -(.age >= 18) +.age >= 18 ``` Operators are for example used to specify filter conditions: @@ -112,12 +112,27 @@ Operators are for example used to specify filter conditions: filter(.age >= 18) ``` -When composing multiple operators, it is necessary to use parentheses: +When using multiple operators, they will be evaluated according to their precedence (highest first): ```text -filter((.age >= 18) and (.age <= 65)) +filter(.age >= 18 and .age <= 65) ``` +Note that some operators, like `and`, `or`, `+`, and `-`, support more than two values and are evaluated left-to-right, like `2 + 3 + 4`. Others, like `^` and `==`, do not support more than two values. If needed, it is always possible to use parenthesis, like `(2 ^ 3) ^ 4`. + +The operators have the following precedence, from highest to lowest: + +| Precedence | Associativity | Operators | +|-----------------------------|---------------|--------------------------------------| +| 8: exponentiation | n/a | `^` | +| 7: multiplicative operators | left-to-right | `*`, `/`, `%` | +| 6: additive operators | left-to-right | `+`, `-` | +| 5: relational operators | n/a | `>`, `>=`, `<`, `<=`, `in`, `not in` | +| 4: equality operators | n/a | `==`, `!=` | +| 3: and | left-to-right | `and` | +| 2: or | left-to-right | `or` | +| 1: pipe | left-to-right | `\|` | + See page [Function reference](/reference) for a detailed overview of all available functions and operators. ### Pipes diff --git a/src/content/reference.md b/src/content/reference.md index 647c585..529ce02 100644 --- a/src/content/reference.md +++ b/src/content/reference.md @@ -173,7 +173,7 @@ jsonquery(data, 'filter((.age > 30) and (.address.city == "New York"))') ## sort -Sort a list with objects or values. +Sort a list with objects or values. The function first orders values by type: `boolean`, `number`, `string`, and other types. Strings are compared alphabetically and case-sensitive. Objects and arrays are not re-ordered. ```text sort() @@ -559,7 +559,7 @@ jsonquery(events, 'map(substring(.time, 0, 10))') ## uniq -Create a copy of an array where all duplicates are removed. +Create a copy of an array where all duplicates are removed. Values are compared using the `eq` operator, which does a deep strict equal comparison. ```text uniq() @@ -636,7 +636,7 @@ jsonquery("hello", 'size()') // 5 ## sum -Calculate the sum of all values in an array. +Calculate the sum of all values in an array. The function return `0` in case of an empty array. ```text sum() @@ -651,7 +651,7 @@ jsonquery([2.4, 5.7], 'sum()') // 8.1 ## min -Return the minimum of the values in an array. +Return the minimum of the values in an array. The function returns `null` in case of an empty array. ```text min() @@ -666,7 +666,7 @@ jsonquery([5, 7, 3], 'min()') // 3 ## max -Return the maximum of the values in an array. +Return the maximum of the values in an array. The function returns `null` in case of an empty array. ```text max() @@ -681,7 +681,7 @@ jsonquery([5, 7, 3], 'max()') // 7 ## prod -Calculate the product of the values in an array. +Calculate the product of the values in an array. The function throws an error in case of an empty array. ```text prod() @@ -696,7 +696,7 @@ jsonquery([2, 3, 2, 7, 1, 1], 'prod()') // 84 ## average -Calculate the average of the values in an array. +Calculate the average of the values in an array. The function throws an error in case of an empty array. ```text average() @@ -711,7 +711,7 @@ jsonquery([2, 3, 2, 7, 1], 'average()') // 3 ## eq (`==`) -Test whether two values are strictly equal. This will consider a string `"2"` and a number `2` to be _not_ equal for example since their data type differs. +Test whether two values are deep strict equal. This will consider a string `"2"` and a number `2` to be _not_ equal for example, since their data type differs. Objects and arrays are compared recursively, so `{"id":1,"name":"Joe"}` and `{"name":"Joe","id":1}` are deep equal for example. ```text a == b @@ -741,7 +741,8 @@ jsonquery({ a: 2 }, 'eq(.a, 2)') // true ## gt (`>`) -Test whether `a` is greater than `b`. +Test whether `a` is greater than `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false. + ```text a > b @@ -765,7 +766,7 @@ jsonquery(data, 'filter(.age > 18)') ## gte (`>=`) -Test whether `a` is greater than or equal to `b`. +Test whether `a` is greater than or equal to `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false. ```text a >= b @@ -790,7 +791,7 @@ jsonquery(data, 'filter(.age >= 18)') ## lt (`<`) -Test whether `a` is less than `b`. +Test whether `a` is less than `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false. ```text a < b @@ -814,7 +815,7 @@ jsonquery(data, 'filter(.age < 18)') ## lte (`<=`) -Test whether `a` is less than or equal to `b`. +Test whether `a` is less than or equal to `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false. ```text a <= b @@ -839,7 +840,7 @@ jsonquery(data, 'filter(.age <= 18)') ## ne (`!=`) -Test whether two values are not equal. This is the opposite of the strict equal function `eq`. Two values are considered unequal when their data type differs (for example one is a string and another is a number), or when the value itself is different. For example a string `"2"` and a number `2` are considered unequal, even though their mathematical value is equal. +Test whether two values are not deep strict equal. This is the opposite of the strict equal function `eq`. Two values are considered unequal when their data type differs (for example one is a string and another is a number), or when the value itself is different. For example a string `"2"` and a number `2` are considered unequal, even though their mathematical value is equal. Objects and arrays are compared recursively, so `{"id":1,"name":"Joe"}` and `{"name":"Joe","id":1}` are deep equal for example. ```text a != b @@ -868,11 +869,13 @@ jsonquery({ a: 2 }, 'a != "2"') // true (since not strictly equal) ## and -Test whether both values are truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`. +Test whether two or more values are truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`. The function throws an error in case of zero arguments. ```text a and b +a and b and c and ... and(a, b) +and(a, b, c, ...) ``` Examples: @@ -892,11 +895,13 @@ jsonquery(data, 'filter((.name == "Chris") and (.age == 16))') ## or -Test whether one or both values are truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`. +Test whether at least one of the values is truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`. The function throws an error in case of zero arguments. ```text a or b +a or b or c or ... or(a, b) +or(a, b, c, ...) ``` Examples: @@ -997,7 +1002,7 @@ jsonquery(data, 'if(.kid.age >= .minAge, .messageOk, .messageFail)') ## in -Test whether the search value is one of the values of the provided list. +Test whether the search value is one of the values of the provided list. Values are compared using the `eq` operator, which does a deep strict equal comparison. ```text searchValue in values @@ -1022,7 +1027,7 @@ jsonquery(data, 'filter(.age in [16, 18])') ## not in -Test whether the search value is _not_ one of the values of the provided list. +Test whether the search value is _not_ one of the values of the provided list. Values are compared using the `eq` operator, which does a deep strict equal comparison. ```text searchValue not in values @@ -1155,7 +1160,7 @@ jsonquery(data, '.a / .b') // 3 ## pow (`^`) -Calculate the exponent. Returns the result of raising `a` to the power of `b`, like `a^b` +Calculate the exponent. Returns the result of raising `a` to the power of `b`, like `a ^ b`. The `^` operator does not support more than two values, so if you need to calculate a chain of multiple exponents you'll have to use parenthesis, like `(a ^ b) ^ c`. ```text a ^ b