diff --git a/build/example-test-harness/pom-lettuce-async.xml b/build/example-test-harness/pom-lettuce-async.xml index b488bebbf5..b887da8f00 100644 --- a/build/example-test-harness/pom-lettuce-async.xml +++ b/build/example-test-harness/pom-lettuce-async.xml @@ -4,7 +4,8 @@ io.redislettuce-async1.0 17UTF-8 - io.lettucelettuce-core6.5.5.RELEASE + io.lettucelettuce-core7.7.0.RELEASE + com.fasterxml.jackson.corejackson-databind2.17.1 org.junit.jupiterjunit-jupiter5.10.2test org.assertjassertj-core3.25.3test diff --git a/build/example-test-harness/pom-lettuce-reactive.xml b/build/example-test-harness/pom-lettuce-reactive.xml index e3f67e7892..b1fb7d1c25 100644 --- a/build/example-test-harness/pom-lettuce-reactive.xml +++ b/build/example-test-harness/pom-lettuce-reactive.xml @@ -4,7 +4,8 @@ io.redislettuce-reactive1.0 17UTF-8 - io.lettucelettuce-core6.5.5.RELEASE + io.lettucelettuce-core7.7.0.RELEASE + com.fasterxml.jackson.corejackson-databind2.17.1 org.junit.jupiterjunit-jupiter5.10.2test org.assertjassertj-core3.25.3test diff --git a/build/example-test-harness/run.sh b/build/example-test-harness/run.sh index 3adf12f665..3c9cd83544 100755 --- a/build/example-test-harness/run.sh +++ b/build/example-test-harness/run.sh @@ -384,10 +384,12 @@ run_go() { cp "$1" "$d/ex_test.go" (cd "$d" && go mod tidy >/dev/null 2>&1 && go test ./... ) >"$LOG" 2>&1; rc=$? } -run_rust_sync() { rust_run "$WORK/rust-sync" "$1" 'redis = "1.3"' ; } -run_rust_async(){ rust_run "$WORK/rust-async" "$1" 'redis = { version = "1.3", features = ["tokio-comp"] } +run_rust_sync() { rust_run "$WORK/rust-sync" "$1" 'redis = { version = "1.3", features = ["json"] } +serde_json = { version = "1", features = ["preserve_order"] }' ; } +run_rust_async(){ rust_run "$WORK/rust-async" "$1" 'redis = { version = "1.3", features = ["tokio-comp", "json"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } -futures-util = "0.3"' ; } +futures-util = "0.3" +serde_json = { version = "1", features = ["preserve_order"] }' ; } rust_run() { local d="$1" src="$2" deps="$3"; mkdir -p "$d/src" if [ ! -f "$d/Cargo.toml" ]; then diff --git a/content/develop/data-types/json/path.md b/content/develop/data-types/json/path.md index 5133f7f136..b2a2ca64be 100644 --- a/content/develop/data-types/json/path.md +++ b/content/develop/data-types/json/path.md @@ -64,13 +64,13 @@ The following JSONPath syntax table was adapted from Goessner's [path syntax com Beginning with Redis 8.10, the JSON data type supports a richer JSONPath syntax, with additional operators and functions: -- Projection expressions at the top level of a JSONPath query -- `==` and `!=` can now compare any literal, including array and object literals -- Filter negation operator: `!` -- `size`/`sizeof` and `empty` operators on string, array, object, and nodelist -- `in` and `nin` operators: membership test on an array and nodelist -- Operators on numbers: binary `-`, `+`, `*`, `/`, `%`, and unary `-` and `+` -- Operator on object: `~` +- [Projection expressions](#projection-expressions) at the top level of a JSONPath query +- [`==` and `!=`](#comparing-array-and-object-literals) can now compare any literal, including array and object literals +- Filter negation operator: [`!`](#negation-) +- [`size`/`sizeof` and `empty`](#size-sizeof-and-empty) operators on string, array, object, and nodelist +- [`in` and `nin`](#membership-in-and-nin) operators: membership test on an array and nodelist +- [Operators on numbers](#arithmetic-operators): binary `-`, `+`, `*`, `/`, `%`, and unary `-` and `+` +- Operator on object: [`~`](#get-keys-operator-) - `length()` function on array, object, and string - Functions on number: `abs()`, `ceiling()`, `floor()` - Functions on string: `match()`, `search()` @@ -80,7 +80,9 @@ Beginning with Redis 8.10, the JSON data type supports a richer JSONPath syntax, - Function on object: `keys()` - Function on nodelist: `count()` - Function on nodelist with exactly one node: `value()` -- Relations functions on array and nodelist: `subsetof()`, `anyof()`, `noneof()` +- Relations functions on array and nodelist: [`subsetof()`, `anyof()`, `noneof()`](#set-relations-subsetof-anyof-and-noneof) + +These operators can be used within a filter expression (`?()`). {{< warning >}} Beginning with Redis 8.10, two changes to path parsing may affect existing queries: @@ -99,131 +101,6 @@ The following rules apply to the operators and functions described below: - **Arithmetic operators must be surrounded by spaces.** `@.a + 1` is addition, but `@.a+1` is a reference to a field literally named `a+1`, because the field-name character set includes characters such as `+`, `-`, `/`, `%`, `$`, `^`, `:`, and `_`. - **Functions are arity-checked.** Calling a function with the wrong number of arguments produces *Nothing* rather than silently using a subset of the arguments. -## Filter expression operators - -The following operators can be used within a filter expression (`?()`). - -### Negation `!` - -The `!` operator negates a filter condition. It binds more tightly than the logical `&&` and `||` operators, so use parentheses to negate a compound condition. A double negation (`!!`) is equivalent to an existence test. - -``` -> JSON.SET doc $ '[{"a":1,"b":1},{"b":2},{"a":1},{"c":3}]' -OK -> JSON.GET doc '$[?!@.a]' -"[{\"b\":2},{\"c\":3}]" -> JSON.GET doc '$[?!(@.a==1)]' -"[{\"b\":2},{\"c\":3}]" -> JSON.GET doc '$[?!@.a && @.b]' -"[{\"b\":2}]" -``` - -### Comparing array and object literals - -The `==` and `!=` operators can compare any JSON literal, including array and object literals, on either side of the operator. Comparison is by structural (deep) equality. A comparison between values of different types is always false. - -``` -> JSON.SET doc $ '{"arrs":[[1],[2],[1,2],[1,[2]]],"objs":[{"x":1},{"x":2},{"y":1}]}' -OK -> JSON.GET doc '$.arrs[?(@ == [1])]' -"[[1]]" -> JSON.GET doc '$.arrs[?(@ == [1,[2]])]' -"[[1,[2]]]" -> JSON.GET doc '$.objs[?(@ == {"x":1})]' -"[{\"x\":1}]" -``` - -### Arithmetic operators - -The binary operators `+`, `-`, `*`, `/`, and `%` and the unary operators `-` and `+` operate on numbers. Precedence, from highest to lowest, is: unary `-`/`+`, then `*`/`/`/`%`, then `+`/`-`. Use parentheses to override precedence. Division always produces a floating-point result. Division or modulo by zero produces *Nothing*, so no element matches. - -Remember that arithmetic operators must be surrounded by spaces (see [Evaluation semantics](#evaluation-semantics)). - -``` -> JSON.SET doc $ '[{"a":2,"b":3},{"a":5,"b":2}]' -OK -> JSON.GET doc '$[?@.a + 1 == 3]' -"[{\"a\":2,\"b\":3}]" -> JSON.GET doc '$[?@.a + @.b * 2 == 8]' -"[{\"a\":2,\"b\":3}]" -> JSON.GET doc '$[?(@.a + @.b) * 2 == 10]' -"[{\"a\":2,\"b\":3}]" -``` - -### Membership: `in` and `nin` - -`value in array` matches when `value` is a member of `array`; `value nin array` is its strict negation. The right-hand side can be an array literal or a path to an array. Membership uses the same structural comparison and number coercion as `==`. If the right-hand side is not an array, `in` is false (so `nin` is true). - -``` -> JSON.SET doc $ '{"a":[1,2,3,4],"allow":[2,3]}' -OK -> JSON.GET doc '$.a[?@ in [2,4]]' -"[2,4]" -> JSON.GET doc '$.a[?@ nin [2,4]]' -"[1,3]" -> JSON.GET doc '$.a[?@ in $.allow]' -"[2,3]" -``` - -### Set relations: `subsetof`, `anyof`, and `noneof` - -These operators compare two arrays (each side can be an array literal or a path to an array): - -- `subsetof` matches when every element of the left array is a member of the right array. An empty array is a subset of any array. -- `anyof` matches when the two arrays have a non-empty intersection. An empty array never matches. -- `noneof` matches when the two arrays have an empty intersection. An empty array always matches. - -``` -> JSON.SET doc $ '{"a":[[1,2],[1,5],[]]}' -OK -> JSON.GET doc '$.a[?@ subsetof [1,2,3]]' -"[[1,2],[]]" -> JSON.SET doc $ '{"a":[[1,9],[8,9],[]]}' -OK -> JSON.GET doc '$.a[?@ anyof [1,2,3]]' -"[[1,9]]" -> JSON.SET doc $ '{"a":[[4,5],[1,9],[]]}' -OK -> JSON.GET doc '$.a[?@ noneof [1,2,3]]' -"[[4,5],[]]" -``` - -### `size` (`sizeof`) and `empty` - -`left sizeof n` matches when the size of `left` equals the integer `n`, where size is the number of characters in a string, the number of elements in an array, or the number of members in an object. A non-integer `n` is truncated toward zero; a non-numeric `n` never matches. `size` is an alias for `sizeof`. - -`left empty true` matches an empty string, array, or object; `left empty false` matches a non-empty one. - -``` -> JSON.SET doc $ '{"a":[[4,5],[1],[7,8,9]]}' -OK -> JSON.GET doc '$.a[?@ sizeof 2]' -"[[4,5]]" -> JSON.SET doc $ '{"a":[[],[1],"",[2,3],{},{"k":1}]}' -OK -> JSON.GET doc '$.a[?@ empty true]' -"[[],\"\",{}]" -> JSON.GET doc '$.a[?@ empty false]' -"[[1],[2,3],{\"k\":1}]" -``` - -### Get-keys operator `~` - -The terminal `~` operator returns the member names of an object as a flat list of strings. Applied to the root (`$~`), it returns the top-level keys. Applied to a multiple-match receiver, it flattens the keys of every matched object. A non-object receiver contributes no keys. - -``` -> JSON.SET doc $ '{"obj":{"x":1,"y":2},"books":[{"t":"a"},{"t":"b"}]}' -OK -> JSON.GET doc '$.obj~' -"[\"x\",\"y\"]" -> JSON.GET doc '$~' -"[\"obj\",\"books\"]" -> JSON.GET doc '$.books~' -"[]" -``` - -Because `~` is terminal, expressions such as `$.obj~.x`, `$.obj~~`, and `$.obj.keys()~` are parse errors. For a composable alternative that can be chained with other functions, use the [`keys()`](#keys) function. - ## Functions Functions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). A function can be written in prefix form, `length($.arr)`, or in postfix (method) form, `$.arr.length()`. A path segment immediately followed by `(` is a method call, so `$.arr.length()` is a function call while `$.arr.length` is a reference to a field named `length`. @@ -362,39 +239,9 @@ OK "[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]" ``` -## Projection expressions - -The top level of a JSONPath query can be an expression that *computes* a value — arithmetic, a function call, a get-keys operator, and so on — rather than only selecting nodes from the document. A query that is a plain path (for example, `$.a.b`, `$..x`, or even a fully parenthesized path such as `($.a)`) still selects nodes; anything else (for example, `$.a + 1`, `-$.a`, `$.arr.length()`, `length($.arr)`, `$.obj~`, or `$.obj.keys()`) is a projection. - -A projection that evaluates to *Nothing* produces an empty reply (`[]`). - -``` -> JSON.SET doc $ '{"a":2,"b":4,"arr":[1,2,3]}' -OK -> JSON.GET doc '$.a + 1' -"[3]" -> JSON.GET doc '$.a * $.b' -"[8]" -> JSON.GET doc '($.a + $.b) / 2' -"[3.0]" -> JSON.GET doc '$.arr.length()' -"[3]" -> JSON.GET doc '$.a / 0' -"[]" -``` - -When [`JSON.GET`]({{< relref "commands/json.get/" >}}) is given more than one path, projections and plain paths can be mixed, and each path becomes a key in the returned object: - -``` -> JSON.GET doc '$.a + 1' '$.b' -"{\"$.a + 1\":[3],\"$.b\":[4]}" -``` - -[`JSON.MGET`]({{< relref "commands/json.mget/" >}}) evaluates the projection independently for each key. A missing key, or a per-key evaluation error, yields a null reply for that key rather than failing the whole request. - -## Multi-language JSONPath examples +## JSONPath examples -The following multi-language JSONPath examples use this JSON document, which stores details about items in a store's inventory: +The following examples use this JSON document, which stores details about items in a store's inventory: {{< trimmable head="12" tail="8" >}} ```json @@ -546,7 +393,128 @@ OK "[\"Quaoar\",\"Weywot\"]" {{< /clients-example >}} -See [Filter expression operators](#filter-expression-operators) for more information. +Beginning with Redis 8.10, the JSONPath filter syntax supports the following operators. + +#### Negation `!` + +The `!` operator negates a filter condition. It binds more tightly than the logical `&&` and `||` operators, so use parentheses to negate a compound condition. A double negation (`!!`) is equivalent to an existence test. + +{{< clients-example set="json_path_ops" step="filter_negation" description="Filter negation: Use the ! operator to invert a filter condition when you need to select elements that do not match" difficulty="advanced" >}} +> JSON.SET doc $ '[{"a":1,"b":1},{"b":2},{"a":1},{"c":3}]' +OK +> JSON.GET doc '$[?!@.a]' +"[{\"b\":2},{\"c\":3}]" +> JSON.GET doc '$[?!(@.a==1)]' +"[{\"b\":2},{\"c\":3}]" +> JSON.GET doc '$[?!@.a && @.b]' +"[{\"b\":2}]" +{{< /clients-example >}} + +#### Comparing array and object literals + +The `==` and `!=` operators can compare any JSON literal, including array and object literals, on either side of the operator. Comparison is by structural (deep) equality. A comparison between values of different types is always false. + +{{< clients-example set="json_path_ops" step="filter_literal_eq" description="Literal comparison: Use == and != to compare array and object literals by structural equality when you need to match a whole nested value" difficulty="advanced" >}} +> JSON.SET doc $ '{"arrs":[[1],[2],[1,2],[1,[2]]],"objs":[{"x":1},{"x":2},{"y":1}]}' +OK +> JSON.GET doc '$.arrs[?(@ == [1])]' +"[[1]]" +> JSON.GET doc '$.arrs[?(@ == [1,[2]])]' +"[[1,[2]]]" +> JSON.GET doc '$.objs[?(@ == {"x":1})]' +"[{\"x\":1}]" +{{< /clients-example >}} + +#### Arithmetic operators + +The binary operators `+`, `-`, `*`, `/`, and `%` and the unary operators `-` and `+` operate on numbers. Precedence, from highest to lowest, is: unary `-`/`+`, then `*`/`/`/`%`, then `+`/`-`. Use parentheses to override precedence. Division always produces a floating-point result. Division or modulo by zero produces *Nothing*, so no element matches. + +Remember that arithmetic operators must be surrounded by spaces (see [Evaluation semantics](#evaluation-semantics)). + +{{< clients-example set="json_path_ops" step="filter_arithmetic" description="Arithmetic in filters: Use +, -, *, / and % inside a filter condition when you need to compute a value before comparing it" difficulty="advanced" >}} +> JSON.SET doc $ '[{"a":2,"b":3},{"a":5,"b":2}]' +OK +> JSON.GET doc '$[?@.a + 1 == 3]' +"[{\"a\":2,\"b\":3}]" +> JSON.GET doc '$[?@.a + @.b * 2 == 8]' +"[{\"a\":2,\"b\":3}]" +> JSON.GET doc '$[?(@.a + @.b) * 2 == 10]' +"[{\"a\":2,\"b\":3}]" +{{< /clients-example >}} + +#### Membership: `in` and `nin` + +`value in array` matches when `value` is a member of `array`; `value nin array` is its strict negation. The right-hand side can be an array literal or a path to an array. Membership uses the same structural comparison and number coercion as `==`. If the right-hand side is not an array, `in` is false (so `nin` is true). + +{{< clients-example set="json_path_ops" step="filter_membership" description="Membership filters: Use in and nin to test whether a value is a member of an array when you need set-style matching" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[1,2,3,4],"allow":[2,3]}' +OK +> JSON.GET doc '$.a[?@ in [2,4]]' +"[2,4]" +> JSON.GET doc '$.a[?@ nin [2,4]]' +"[1,3]" +> JSON.GET doc '$.a[?@ in $.allow]' +"[2,3]" +{{< /clients-example >}} + +#### Set relations: `subsetof`, `anyof`, and `noneof` + +These operators compare two arrays (each side can be an array literal or a path to an array): + +- `subsetof` matches when every element of the left array is a member of the right array. An empty array is a subset of any array. +- `anyof` matches when the two arrays have a non-empty intersection. An empty array never matches. +- `noneof` matches when the two arrays have an empty intersection. An empty array always matches. + +{{< clients-example set="json_path_ops" step="filter_set_relations" description="Set relation filters: Use subsetof, anyof, and noneof to compare two arrays when you need to match based on their overlap" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[[1,2],[1,5],[]]}' +OK +> JSON.GET doc '$.a[?@ subsetof [1,2,3]]' +"[[1,2],[]]" +> JSON.SET doc $ '{"a":[[1,9],[8,9],[]]}' +OK +> JSON.GET doc '$.a[?@ anyof [1,2,3]]' +"[[1,9]]" +> JSON.SET doc $ '{"a":[[4,5],[1,9],[]]}' +OK +> JSON.GET doc '$.a[?@ noneof [1,2,3]]' +"[[4,5],[]]" +{{< /clients-example >}} + +#### `size` (`sizeof`) and `empty` + +`left sizeof n` matches when the size of `left` equals the integer `n`, where size is the number of characters in a string, the number of elements in an array, or the number of members in an object. A non-integer `n` is truncated toward zero; a non-numeric `n` never matches. `size` is an alias for `sizeof`. + +`left empty true` matches an empty string, array, or object; `left empty false` matches a non-empty one. + +{{< clients-example set="json_path_ops" step="filter_size_empty" description="Size and emptiness filters: Use sizeof and empty to filter by the size or emptiness of a string, array, or object" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[[4,5],[1],[7,8,9]]}' +OK +> JSON.GET doc '$.a[?@ sizeof 2]' +"[[4,5]]" +> JSON.SET doc $ '{"a":[[],[1],"",[2,3],{},{"k":1}]}' +OK +> JSON.GET doc '$.a[?@ empty true]' +"[[],\"\",{}]" +> JSON.GET doc '$.a[?@ empty false]' +"[[1],[2,3],{\"k\":1}]" +{{< /clients-example >}} + +#### Get-keys operator `~` + +The terminal `~` operator returns the member names of an object as a flat list of strings. Applied to the root (`$~`), it returns the top-level keys. Applied to a multiple-match receiver, it flattens the keys of every matched object. A non-object receiver contributes no keys. + +Because `~` is terminal, expressions such as `$.obj~.x`, `$.obj~~`, and `$.obj.keys()~` are parse errors. For a composable alternative that can be chained with other functions, use the [`keys()`](#keys) function. + +{{< clients-example set="json_path_ops" step="filter_getkeys" description="Get-keys filter: Use the ~ operator to retrieve an object's member names as a list of strings" difficulty="advanced" >}} +> JSON.SET doc $ '{"obj":{"x":1,"y":2},"books":[{"t":"a"},{"t":"b"}]}' +OK +> JSON.GET doc '$.obj~' +"[\"x\",\"y\"]" +> JSON.GET doc '$~' +"[\"obj\",\"books\"]" +> JSON.GET doc '$.books~' +"[]" +{{< /clients-example >}} ### Update examples @@ -582,6 +550,36 @@ JSONPath queries also work with other JSON commands that accept a path as an arg "[[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]" {{< /clients-example >}} +## Projection expressions + +The top level of a JSONPath query can be an expression that *computes* a value — arithmetic, a function call, a get-keys operator, and so on — rather than only selecting nodes from the document. A query that is a plain path (for example, `$.a.b`, `$..x`, or even a fully parenthesized path such as `($.a)`) still selects nodes; anything else (for example, `$.a + 1`, `-$.a`, `$.arr.length()`, `length($.arr)`, `$.obj~`, or `$.obj.keys()`) is a projection. + +A projection that evaluates to *Nothing* produces an empty reply (`[]`). + +``` +> JSON.SET doc $ '{"a":2,"b":4,"arr":[1,2,3]}' +OK +> JSON.GET doc '$.a + 1' +"[3]" +> JSON.GET doc '$.a * $.b' +"[8]" +> JSON.GET doc '($.a + $.b) / 2' +"[3.0]" +> JSON.GET doc '$.arr.length()' +"[3]" +> JSON.GET doc '$.a / 0' +"[]" +``` + +When [`JSON.GET`]({{< relref "commands/json.get/" >}}) is given more than one path, projections and plain paths can be mixed, and each path becomes a key in the returned object: + +``` +> JSON.GET doc '$.a + 1' '$.b' +"{\"$.a + 1\":[3],\"$.b\":[4]}" +``` + +[`JSON.MGET`]({{< relref "commands/json.mget/" >}}) evaluates the projection independently for each key. A missing key, or a per-key evaluation error, yields a null reply for that key rather than failing the whole request. + ## Legacy path syntax RedisJSON v1 had the following path implementation. JSON v2 still supports this legacy path in addition to JSONPath. diff --git a/local_examples/json_path_ops/go-redis/json_path_ops_test.go b/local_examples/json_path_ops/go-redis/json_path_ops_test.go new file mode 100644 index 0000000000..658d67f1e6 --- /dev/null +++ b/local_examples/json_path_ops/go-redis/json_path_ops_test.go @@ -0,0 +1,444 @@ +// EXAMPLE: json_path_ops +// HIDE_START +package example_commands_test + +import ( + "context" + "fmt" + + "github.com/redis/go-redis/v9" +) + +// HIDE_END + +func ExampleClient_json_path_ops_filter_negation() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_negation + res1, err := rdb.JSONSet(ctx, "doc", "$", + `[{"a":1,"b":1},{"b":2},{"a":1},{"c":3}]`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$[?!@.a]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"b":2},{"c":3}] + + res3, err := rdb.JSONGet(ctx, "doc", `$[?!(@.a==1)]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [{"b":2},{"c":3}] + + res4, err := rdb.JSONGet(ctx, "doc", `$[?!@.a && @.b]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [{"b":2}] + // STEP_END + + // Output: + // OK + // [{"b":2},{"c":3}] + // [{"b":2},{"c":3}] + // [{"b":2}] +} + +func ExampleClient_json_path_ops_filter_literal_eq() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_literal_eq + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"arrs":[[1],[2],[1,2],[1,[2]]],"objs":[{"x":1},{"x":2},{"y":1}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.arrs[?(@ == [1])]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [[1]] + + res3, err := rdb.JSONGet(ctx, "doc", `$.arrs[?(@ == [1,[2]])]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [[1,[2]]] + + res4, err := rdb.JSONGet(ctx, "doc", `$.objs[?(@ == {"x":1})]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [{"x":1}] + // STEP_END + + // Output: + // OK + // [[1]] + // [[1,[2]]] + // [{"x":1}] +} + +func ExampleClient_json_path_ops_filter_arithmetic() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_arithmetic + res1, err := rdb.JSONSet(ctx, "doc", "$", + `[{"a":2,"b":3},{"a":5,"b":2}]`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$[?@.a + 1 == 3]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"a":2,"b":3}] + + res3, err := rdb.JSONGet(ctx, "doc", `$[?@.a + @.b * 2 == 8]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [{"a":2,"b":3}] + + res4, err := rdb.JSONGet(ctx, "doc", `$[?(@.a + @.b) * 2 == 10]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [{"a":2,"b":3}] + // STEP_END + + // Output: + // OK + // [{"a":2,"b":3}] + // [{"a":2,"b":3}] + // [{"a":2,"b":3}] +} + +func ExampleClient_json_path_ops_filter_membership() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_membership + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[1,2,3,4],"allow":[2,3]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?@ in [2,4]]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [2,4] + + res3, err := rdb.JSONGet(ctx, "doc", `$.a[?@ nin [2,4]]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [1,3] + + res4, err := rdb.JSONGet(ctx, "doc", `$.a[?@ in $.allow]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [2,3] + // STEP_END + + // Output: + // OK + // [2,4] + // [1,3] + // [2,3] +} + +func ExampleClient_json_path_ops_filter_set_relations() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_set_relations + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[[1,2],[1,5],[]]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?@ subsetof [1,2,3]]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [[1,2],[]] + + res3, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[[1,9],[8,9],[]]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> OK + + res4, err := rdb.JSONGet(ctx, "doc", `$.a[?@ anyof [1,2,3]]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [[1,9]] + + res5, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[[4,5],[1,9],[]]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res5) // >>> OK + + res6, err := rdb.JSONGet(ctx, "doc", `$.a[?@ noneof [1,2,3]]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res6) // >>> [[4,5],[]] + // STEP_END + + // Output: + // OK + // [[1,2],[]] + // OK + // [[1,9]] + // OK + // [[4,5],[]] +} + +func ExampleClient_json_path_ops_filter_size_empty() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_size_empty + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[[4,5],[1],[7,8,9]]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?@ sizeof 2]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [[4,5]] + + res3, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[[],[1],"",[2,3],{},{"k":1}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> OK + + res4, err := rdb.JSONGet(ctx, "doc", `$.a[?@ empty true]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [[],"",{}] + + res5, err := rdb.JSONGet(ctx, "doc", `$.a[?@ empty false]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res5) // >>> [[1],[2,3],{"k":1}] + // STEP_END + + // Output: + // OK + // [[4,5]] + // OK + // [[],"",{}] + // [[1],[2,3],{"k":1}] +} + +func ExampleClient_json_path_ops_filter_getkeys() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.FlushDB(ctx) + rdb.Del(ctx, "doc") + // REMOVE_END + + // STEP_START filter_getkeys + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"obj":{"x":1,"y":2},"books":[{"t":"a"},{"t":"b"}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.obj~`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> ["x","y"] + + res3, err := rdb.JSONGet(ctx, "doc", `$~`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> ["obj","books"] + + res4, err := rdb.JSONGet(ctx, "doc", `$.books~`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [] + // STEP_END + + // Output: + // OK + // ["x","y"] + // ["obj","books"] + // [] +} diff --git a/local_examples/json_path_ops/jedis/JsonPathOpsExample.java b/local_examples/json_path_ops/jedis/JsonPathOpsExample.java new file mode 100644 index 0000000000..d99c364d81 --- /dev/null +++ b/local_examples/json_path_ops/jedis/JsonPathOpsExample.java @@ -0,0 +1,192 @@ +// EXAMPLE: json_path_ops +// REMOVE_START +package io.redis.examples; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +// REMOVE_END + +// HIDE_START +import redis.clients.jedis.RedisClient; +import redis.clients.jedis.json.Path; +import redis.clients.jedis.json.Path2; +import org.json.JSONObject; +// HIDE_END + +// HIDE_START +public class JsonPathOpsExample { + + @Test + public void run() { + RedisClient jedis = RedisClient.create("redis://localhost:6379"); + + // REMOVE_START + jedis.del("doc"); + // REMOVE_END +// HIDE_END + + // STEP_START filter_negation + String res1 = jedis.jsonSet("doc", new Path2("$"), "[{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}]"); + System.out.println(res1); // >>> OK + + Object res2 = jedis.jsonGet("doc", new Path2("$[?!@.a]")); + System.out.println(res2); // >>> [{"b":2},{"c":3}] + + Object res3 = jedis.jsonGet("doc", new Path2("$[?!(@.a==1)]")); + System.out.println(res3); // >>> [{"b":2},{"c":3}] + + Object res4 = jedis.jsonGet("doc", new Path2("$[?!@.a && @.b]")); + System.out.println(res4); // >>> [{"b":2}] + // STEP_END + // REMOVE_START + assertEquals("[{\"b\":2},{\"c\":3}]", res2.toString()); + assertEquals("[{\"b\":2},{\"c\":3}]", res3.toString()); + assertEquals("[{\"b\":2}]", res4.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START filter_literal_eq + String res5 = jedis.jsonSet( + "doc", + new Path2("$"), + "{\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}" + ); + System.out.println(res5); // >>> OK + + Object res6 = jedis.jsonGet("doc", new Path2("$.arrs[?(@ == [1])]")); + System.out.println(res6); // >>> [[1]] + + Object res7 = jedis.jsonGet("doc", new Path2("$.arrs[?(@ == [1,[2]])]")); + System.out.println(res7); // >>> [[1,[2]]] + + Object res8 = jedis.jsonGet("doc", new Path2("$.objs[?(@ == {\"x\":1})]")); + System.out.println(res8); // >>> [{"x":1}] + // STEP_END + // REMOVE_START + assertEquals("[[1]]", res6.toString()); + assertEquals("[[1,[2]]]", res7.toString()); + assertEquals("[{\"x\":1}]", res8.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START filter_arithmetic + String res9 = jedis.jsonSet("doc", new Path2("$"), "[{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}]"); + System.out.println(res9); // >>> OK + + Object res10 = jedis.jsonGet("doc", new Path2("$[?@.a + 1 == 3]")); + System.out.println(res10); // >>> [{"a":2,"b":3}] + + Object res11 = jedis.jsonGet("doc", new Path2("$[?@.a + @.b * 2 == 8]")); + System.out.println(res11); // >>> [{"a":2,"b":3}] + + Object res12 = jedis.jsonGet("doc", new Path2("$[?(@.a + @.b) * 2 == 10]")); + System.out.println(res12); // >>> [{"a":2,"b":3}] + // STEP_END + // REMOVE_START + assertEquals("[{\"a\":2,\"b\":3}]", res10.toString()); + assertEquals("[{\"a\":2,\"b\":3}]", res11.toString()); + assertEquals("[{\"a\":2,\"b\":3}]", res12.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START filter_membership + String res13 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[1,2,3,4],\"allow\":[2,3]}"); + System.out.println(res13); // >>> OK + + Object res14 = jedis.jsonGet("doc", new Path2("$.a[?@ in [2,4]]")); + System.out.println(res14); // >>> [2,4] + + Object res15 = jedis.jsonGet("doc", new Path2("$.a[?@ nin [2,4]]")); + System.out.println(res15); // >>> [1,3] + + Object res16 = jedis.jsonGet("doc", new Path2("$.a[?@ in $.allow]")); + System.out.println(res16); // >>> [2,3] + // STEP_END + // REMOVE_START + assertEquals("[2,4]", res14.toString()); + assertEquals("[1,3]", res15.toString()); + assertEquals("[2,3]", res16.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START filter_set_relations + String res17 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[[1,2],[1,5],[]]}"); + System.out.println(res17); // >>> OK + + Object res18 = jedis.jsonGet("doc", new Path2("$.a[?@ subsetof [1,2,3]]")); + System.out.println(res18); // >>> [[1,2],[]] + + String res19 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[[1,9],[8,9],[]]}"); + System.out.println(res19); // >>> OK + + Object res20 = jedis.jsonGet("doc", new Path2("$.a[?@ anyof [1,2,3]]")); + System.out.println(res20); // >>> [[1,9]] + + String res21 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[[4,5],[1,9],[]]}"); + System.out.println(res21); // >>> OK + + Object res22 = jedis.jsonGet("doc", new Path2("$.a[?@ noneof [1,2,3]]")); + System.out.println(res22); // >>> [[4,5],[]] + // STEP_END + // REMOVE_START + assertEquals("[[1,2],[]]", res18.toString()); + assertEquals("[[1,9]]", res20.toString()); + assertEquals("[[4,5],[]]", res22.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START filter_size_empty + String res23 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[[4,5],[1],[7,8,9]]}"); + System.out.println(res23); // >>> OK + + Object res24 = jedis.jsonGet("doc", new Path2("$.a[?@ sizeof 2]")); + System.out.println(res24); // >>> [[4,5]] + + String res25 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]}"); + System.out.println(res25); // >>> OK + + Object res26 = jedis.jsonGet("doc", new Path2("$.a[?@ empty true]")); + System.out.println(res26); // >>> [[],"",{}] + + Object res27 = jedis.jsonGet("doc", new Path2("$.a[?@ empty false]")); + System.out.println(res27); // >>> [[1],[2,3],{"k":1}] + // STEP_END + // REMOVE_START + assertEquals("[[4,5]]", res24.toString()); + assertEquals("[[],\"\",{}]", res26.toString()); + assertEquals("[[1],[2,3],{\"k\":1}]", res27.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START filter_getkeys + String res28 = jedis.jsonSet( + "doc", + new Path2("$"), + "{\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}" + ); + System.out.println(res28); // >>> OK + + Object res29 = jedis.jsonGet("doc", new Path2("$.obj~")); + System.out.println(res29); // >>> ["x","y"] + + Object res30 = jedis.jsonGet("doc", new Path2("$~")); + System.out.println(res30); // >>> ["obj","books"] + + Object res31 = jedis.jsonGet("doc", new Path2("$.books~")); + System.out.println(res31); // >>> [] + // STEP_END + // REMOVE_START + assertEquals("[\"x\",\"y\"]", res29.toString()); + assertEquals("[\"obj\",\"books\"]", res30.toString()); + assertEquals("[]", res31.toString()); + jedis.del("doc"); + // REMOVE_END + + +// HIDE_START + jedis.close(); + } +} +// HIDE_END diff --git a/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java b/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java new file mode 100644 index 0000000000..e6655c35f3 --- /dev/null +++ b/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java @@ -0,0 +1,307 @@ +// EXAMPLE: json_path_ops +package io.redis.examples.async; + +import io.lettuce.core.*; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.json.JsonPath; +import io.lettuce.core.json.JsonParser; +import io.lettuce.core.json.JsonObject; + +import java.util.concurrent.CompletableFuture; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +public class JsonPathOpsExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // REMOVE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // REMOVE_END + + JsonParser parser = asyncCommands.getJsonParser(); + + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_negation + CompletableFuture filterNegationExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("[{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}]")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?!@.a]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[{"b":2},{"c":3}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"b\":2},{\"c\":3}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?!(@.a==1)]")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> [[{"b":2},{"c":3}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"b\":2},{\"c\":3}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?!@.a && @.b]")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[{"b":2}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"b\":2}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterNegationExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_literal_eq + CompletableFuture filterLiteralEqExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue( + "{\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.arrs[?(@ == [1])]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[[1]]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[1]]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.arrs[?(@ == [1,[2]])]")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> [[[1,[2]]]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[[1,[2]]]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.objs[?(@ == {\"x\":1})]")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[{"x":1}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"x\":1}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterLiteralEqExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_arithmetic + CompletableFuture filterArithmeticExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("[{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}]")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?@.a + 1 == 3]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[{"a":2,"b":3}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"a\":2,\"b\":3}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?@.a + @.b * 2 == 8]")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> [[{"a":2,"b":3}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"a\":2,\"b\":3}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?(@.a + @.b) * 2 == 10]")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[{"a":2,"b":3}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"a\":2,\"b\":3}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterArithmeticExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_membership + CompletableFuture filterMembershipExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[1,2,3,4],\"allow\":[2,3]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ in [2,4]]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[2,4]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[2,4]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ nin [2,4]]")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> [[1,3]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[1,3]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ in $.allow]")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[2,3]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[2,3]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterMembershipExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_set_relations + CompletableFuture filterSetRelationsExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[[1,2],[1,5],[]]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ subsetof [1,2,3]]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[[1,2],[]]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[1,2],[]]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[1,9],[8,9],[]]}")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ anyof [1,2,3]]")); + }).thenCompose(res4 -> { + System.out.println(res4); // >>> [[[1,9]]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[[1,9]]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[4,5],[1,9],[]]}")); + }).thenCompose(res5 -> { + System.out.println(res5); // >>> OK + // REMOVE_START + assertThat(res5).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ noneof [1,2,3]]")); + }).thenAccept(res6 -> { + System.out.println(res6); // >>> [[[4,5],[]]] + // REMOVE_START + assertThat(res6.toString()).isEqualTo("[[[4,5],[]]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterSetRelationsExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_size_empty + CompletableFuture filterSizeEmptyExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[[4,5],[1],[7,8,9]]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ sizeof 2]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[[4,5]]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[4,5]]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]}")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ empty true]")); + }).thenCompose(res4 -> { + System.out.println(res4); // >>> [[[],"",{}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[[],\"\",{}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?@ empty false]")); + }).thenAccept(res5 -> { + System.out.println(res5); // >>> [[[1],[2,3],{"k":1}]] + // REMOVE_START + assertThat(res5.toString()).isEqualTo("[[[1],[2,3],{\"k\":1}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterSizeEmptyExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START filter_getkeys + CompletableFuture filterGetkeysExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue( + "{\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.obj~")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [["x","y"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[\"x\",\"y\"]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$~")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> [["obj","books"]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[\"obj\",\"books\"]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.books~")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + filterGetkeysExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + } finally { + redisClient.shutdown(); + } + } + +} diff --git a/local_examples/json_path_ops/lettuce-reactive/JsonPathOpsExample.java b/local_examples/json_path_ops/lettuce-reactive/JsonPathOpsExample.java new file mode 100644 index 0000000000..4c5d74ad52 --- /dev/null +++ b/local_examples/json_path_ops/lettuce-reactive/JsonPathOpsExample.java @@ -0,0 +1,333 @@ +// EXAMPLE: json_path_ops +package io.redis.examples.reactive; + +import io.lettuce.core.*; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.json.JsonPath; +import io.lettuce.core.json.JsonParser; +import io.lettuce.core.json.JsonObject; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import reactor.core.publisher.Mono; + +public class JsonPathOpsExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + + JsonParser parser = reactiveCommands.getJsonParser(); + + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_negation + Mono filterNegationExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("[{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}]")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?!@.a]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"b":2},{"c":3}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"b\":2},{\"c\":3}]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?!(@.a==1)]")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[{"b":2},{"c":3}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"b\":2},{\"c\":3}]]"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?!@.a && @.b]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[{"b":2}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"b\":2}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterNegationExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_literal_eq + Mono filterLiteralEqExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue( + "{\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.arrs[?(@ == [1])]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[[1]]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[1]]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.arrs[?(@ == [1,[2]])]")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[[1,[2]]]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[[1,[2]]]]"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.objs[?(@ == {\"x\":1})]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[{"x":1}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"x\":1}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterLiteralEqExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_arithmetic + Mono filterArithmeticExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("[{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}]")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?@.a + 1 == 3]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"a":2,"b":3}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"a\":2,\"b\":3}]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?@.a + @.b * 2 == 8]")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[{"a":2,"b":3}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"a\":2,\"b\":3}]]"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?(@.a + @.b) * 2 == 10]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[{"a":2,"b":3}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"a\":2,\"b\":3}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterArithmeticExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_membership + Mono filterMembershipExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[1,2,3,4],\"allow\":[2,3]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ in [2,4]]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[2,4]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[2,4]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ nin [2,4]]")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[1,3]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[1,3]]"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ in $.allow]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[2,3]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[2,3]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterMembershipExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_set_relations + Mono filterSetRelationsExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[[1,2],[1,5],[]]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ subsetof [1,2,3]]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[[1,2],[]]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[1,2],[]]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[1,9],[8,9],[]]}"))) + .doOnNext(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ anyof [1,2,3]]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[[1,9]]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[[1,9]]]"); + // REMOVE_END + }) + .flatMap(res4 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[4,5],[1,9],[]]}"))) + .doOnNext(res5 -> { + System.out.println(res5); // >>> OK + // REMOVE_START + assertThat(res5).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res5 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ noneof [1,2,3]]")).collectList()) + .doOnNext(res6 -> { + System.out.println(res6); // >>> [[[4,5],[]]] + // REMOVE_START + assertThat(res6.toString()).isEqualTo("[[[4,5],[]]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterSetRelationsExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_size_empty + Mono filterSizeEmptyExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[[4,5],[1],[7,8,9]]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ sizeof 2]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[[4,5]]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[4,5]]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]}"))) + .doOnNext(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ empty true]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[[],"",{}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[[],\"\",{}]]"); + // REMOVE_END + }) + .flatMap(res4 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?@ empty false]")).collectList()) + .doOnNext(res5 -> { + System.out.println(res5); // >>> [[[1],[2,3],{"k":1}]] + // REMOVE_START + assertThat(res5.toString()).isEqualTo("[[[1],[2,3],{\"k\":1}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterSizeEmptyExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START filter_getkeys + Mono filterGetkeysExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.obj~")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [["x","y"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[\"x\",\"y\"]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$~")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [["obj","books"]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[\"obj\",\"books\"]]"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.books~")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + filterGetkeysExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + } finally { + redisClient.shutdown(); + } + } + +} diff --git a/local_examples/json_path_ops/node-redis/json-path-ops.js b/local_examples/json_path_ops/node-redis/json-path-ops.js new file mode 100644 index 0000000000..30702f9c39 --- /dev/null +++ b/local_examples/json_path_ops/node-redis/json-path-ops.js @@ -0,0 +1,179 @@ +// EXAMPLE: json_path_ops +// HIDE_START +import assert from 'node:assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// REMOVE_START +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_negation +const res1 = await client.json.set('doc', '$', [{ a: 1, b: 1 }, { b: 2 }, { a: 1 }, { c: 3 }]); +console.log(res1); // >>> OK + +const res2 = await client.json.get('doc', { path: '$[?!@.a]' }); +console.log(res2); // >>> [ { b: 2 }, { c: 3 } ] + +const res3 = await client.json.get('doc', { path: '$[?!(@.a==1)]' }); +console.log(res3); // >>> [ { b: 2 }, { c: 3 } ] + +const res4 = await client.json.get('doc', { path: '$[?!@.a && @.b]' }); +console.log(res4); // >>> [ { b: 2 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res2, [{ b: 2 }, { c: 3 }]); +assert.deepEqual(res3, [{ b: 2 }, { c: 3 }]); +assert.deepEqual(res4, [{ b: 2 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_literal_eq +const res5 = await client.json.set('doc', '$', { + arrs: [[1], [2], [1, 2], [1, [2]]], + objs: [{ x: 1 }, { x: 2 }, { y: 1 }] +}); +console.log(res5); // >>> OK + +const res6 = await client.json.get('doc', { path: '$.arrs[?(@ == [1])]' }); +console.log(res6); // >>> [ [ 1 ] ] + +const res7 = await client.json.get('doc', { path: '$.arrs[?(@ == [1,[2]])]' }); +console.log(res7); // >>> [ [ 1, [ 2 ] ] ] + +const res8 = await client.json.get('doc', { path: '$.objs[?(@ == {"x":1})]' }); +console.log(res8); // >>> [ { x: 1 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res6, [[1]]); +assert.deepEqual(res7, [[1, [2]]]); +assert.deepEqual(res8, [{ x: 1 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_arithmetic +const res9 = await client.json.set('doc', '$', [{ a: 2, b: 3 }, { a: 5, b: 2 }]); +console.log(res9); // >>> OK + +const res10 = await client.json.get('doc', { path: '$[?@.a + 1 == 3]' }); +console.log(res10); // >>> [ { a: 2, b: 3 } ] + +const res11 = await client.json.get('doc', { path: '$[?@.a + @.b * 2 == 8]' }); +console.log(res11); // >>> [ { a: 2, b: 3 } ] + +const res12 = await client.json.get('doc', { path: '$[?(@.a + @.b) * 2 == 10]' }); +console.log(res12); // >>> [ { a: 2, b: 3 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res10, [{ a: 2, b: 3 }]); +assert.deepEqual(res11, [{ a: 2, b: 3 }]); +assert.deepEqual(res12, [{ a: 2, b: 3 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_membership +const res13 = await client.json.set('doc', '$', { a: [1, 2, 3, 4], allow: [2, 3] }); +console.log(res13); // >>> OK + +const res14 = await client.json.get('doc', { path: '$.a[?@ in [2,4]]' }); +console.log(res14); // >>> [ 2, 4 ] + +const res15 = await client.json.get('doc', { path: '$.a[?@ nin [2,4]]' }); +console.log(res15); // >>> [ 1, 3 ] + +const res16 = await client.json.get('doc', { path: '$.a[?@ in $.allow]' }); +console.log(res16); // >>> [ 2, 3 ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res14, [2, 4]); +assert.deepEqual(res15, [1, 3]); +assert.deepEqual(res16, [2, 3]); +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_set_relations +const res17 = await client.json.set('doc', '$', { a: [[1, 2], [1, 5], []] }); +console.log(res17); // >>> OK + +const res18 = await client.json.get('doc', { path: '$.a[?@ subsetof [1,2,3]]' }); +console.log(res18); // >>> [ [ 1, 2 ], [] ] + +const res19 = await client.json.set('doc', '$', { a: [[1, 9], [8, 9], []] }); +console.log(res19); // >>> OK + +const res20 = await client.json.get('doc', { path: '$.a[?@ anyof [1,2,3]]' }); +console.log(res20); // >>> [ [ 1, 9 ] ] + +const res21 = await client.json.set('doc', '$', { a: [[4, 5], [1, 9], []] }); +console.log(res21); // >>> OK + +const res22 = await client.json.get('doc', { path: '$.a[?@ noneof [1,2,3]]' }); +console.log(res22); // >>> [ [ 4, 5 ], [] ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res18, [[1, 2], []]); +assert.deepEqual(res20, [[1, 9]]); +assert.deepEqual(res22, [[4, 5], []]); +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_size_empty +const res23 = await client.json.set('doc', '$', { a: [[4, 5], [1], [7, 8, 9]] }); +console.log(res23); // >>> OK + +const res24 = await client.json.get('doc', { path: '$.a[?@ sizeof 2]' }); +console.log(res24); // >>> [ [ 4, 5 ] ] + +const res25 = await client.json.set('doc', '$', { a: [[], [1], '', [2, 3], {}, { k: 1 }] }); +console.log(res25); // >>> OK + +const res26 = await client.json.get('doc', { path: '$.a[?@ empty true]' }); +console.log(res26); // >>> [ [], '', {} ] + +const res27 = await client.json.get('doc', { path: '$.a[?@ empty false]' }); +console.log(res27); // >>> [ [ 1 ], [ 2, 3 ], { k: 1 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res24, [[4, 5]]); +assert.deepEqual(res26, [[], '', {}]); +assert.deepEqual(res27, [[1], [2, 3], { k: 1 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START filter_getkeys +const res28 = await client.json.set('doc', '$', { + obj: { x: 1, y: 2 }, + books: [{ t: 'a' }, { t: 'b' }] +}); +console.log(res28); // >>> OK + +const res29 = await client.json.get('doc', { path: '$.obj~' }); +console.log(res29); // >>> [ 'x', 'y' ] + +const res30 = await client.json.get('doc', { path: '$~' }); +console.log(res30); // >>> [ 'obj', 'books' ] + +const res31 = await client.json.get('doc', { path: '$.books~' }); +console.log(res31); // >>> [] +// STEP_END + +// REMOVE_START +assert.deepEqual(res29, ['x', 'y']); +assert.deepEqual(res30, ['obj', 'books']); +assert.deepEqual(res31, []); +await client.del('doc'); +// REMOVE_END + + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs b/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs new file mode 100644 index 0000000000..6e19daeb46 --- /dev/null +++ b/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs @@ -0,0 +1,222 @@ +// EXAMPLE: json_path_ops +// HIDE_START + +using System.Text.Json; +using NRedisStack; +using NRedisStack.RedisStackCommands; +using NRedisStack.Tests; +using StackExchange.Redis; + +// HIDE_END + +// REMOVE_START +namespace Doc; + +[Collection("DocsTests")] +// REMOVE_END + +// HIDE_START +public class JsonPathOpsExample +// REMOVE_START +: AbstractNRedisStackTest, IDisposable +// REMOVE_END +{ + // REMOVE_START + public JsonPathOpsExample(EndpointsFixture fixture) : base(fixture) { } + + [Fact] + // REMOVE_END + public void Run() + { + //REMOVE_START + // This is needed because we're constructing ConfigurationOptions in the test before calling GetConnection + SkipIfTargetConnectionDoesNotExist(EndpointsFixture.Env.Standalone); + var _ = GetCleanDatabase(EndpointsFixture.Env.Standalone); + //REMOVE_END + var muxer = ConnectionMultiplexer.Connect("localhost:6379"); + var db = muxer.GetDatabase(); + // REMOVE_START + // Clear any keys here before using them in tests. + db.KeyDelete("doc"); + // REMOVE_END + // HIDE_END + + // STEP_START filter_negation + bool res1 = db.JSON().Set("doc", "$", "[{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}]"); + Console.WriteLine(res1); // >>> True + + RedisResult res2 = db.JSON().Get("doc", path: "$[?!@.a]"); + Console.WriteLine(res2); // >>> [{"b":2},{"c":3}] + + RedisResult res3 = db.JSON().Get("doc", path: "$[?!(@.a==1)]"); + Console.WriteLine(res3); // >>> [{"b":2},{"c":3}] + + RedisResult res4 = db.JSON().Get("doc", path: "$[?!@.a && @.b]"); + Console.WriteLine(res4); // >>> [{"b":2}] + // STEP_END + + // REMOVE_START + Assert.True(res1); + Assert.Equal("[{\"b\":2},{\"c\":3}]", (string?)res2); + Assert.Equal("[{\"b\":2},{\"c\":3}]", (string?)res3); + Assert.Equal("[{\"b\":2}]", (string?)res4); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START filter_literal_eq + bool res5 = db.JSON().Set( + "doc", + "$", + "{\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}" + ); + Console.WriteLine(res5); // >>> True + + RedisResult res6 = db.JSON().Get("doc", path: "$.arrs[?(@ == [1])]"); + Console.WriteLine(res6); // >>> [[1]] + + RedisResult res7 = db.JSON().Get("doc", path: "$.arrs[?(@ == [1,[2]])]"); + Console.WriteLine(res7); // >>> [[1,[2]]] + + RedisResult res8 = db.JSON().Get("doc", path: "$.objs[?(@ == {\"x\":1})]"); + Console.WriteLine(res8); // >>> [{"x":1}] + // STEP_END + + // REMOVE_START + Assert.True(res5); + Assert.Equal("[[1]]", (string?)res6); + Assert.Equal("[[1,[2]]]", (string?)res7); + Assert.Equal("[{\"x\":1}]", (string?)res8); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START filter_arithmetic + bool res9 = db.JSON().Set("doc", "$", "[{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}]"); + Console.WriteLine(res9); // >>> True + + RedisResult res10 = db.JSON().Get("doc", path: "$[?@.a + 1 == 3]"); + Console.WriteLine(res10); // >>> [{"a":2,"b":3}] + + RedisResult res11 = db.JSON().Get("doc", path: "$[?@.a + @.b * 2 == 8]"); + Console.WriteLine(res11); // >>> [{"a":2,"b":3}] + + RedisResult res12 = db.JSON().Get("doc", path: "$[?(@.a + @.b) * 2 == 10]"); + Console.WriteLine(res12); // >>> [{"a":2,"b":3}] + // STEP_END + + // REMOVE_START + Assert.True(res9); + Assert.Equal("[{\"a\":2,\"b\":3}]", (string?)res10); + Assert.Equal("[{\"a\":2,\"b\":3}]", (string?)res11); + Assert.Equal("[{\"a\":2,\"b\":3}]", (string?)res12); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START filter_membership + bool res13 = db.JSON().Set("doc", "$", "{\"a\":[1,2,3,4],\"allow\":[2,3]}"); + Console.WriteLine(res13); // >>> True + + RedisResult res14 = db.JSON().Get("doc", path: "$.a[?@ in [2,4]]"); + Console.WriteLine(res14); // >>> [2,4] + + RedisResult res15 = db.JSON().Get("doc", path: "$.a[?@ nin [2,4]]"); + Console.WriteLine(res15); // >>> [1,3] + + RedisResult res16 = db.JSON().Get("doc", path: "$.a[?@ in $.allow]"); + Console.WriteLine(res16); // >>> [2,3] + // STEP_END + + // REMOVE_START + Assert.True(res13); + Assert.Equal("[2,4]", (string?)res14); + Assert.Equal("[1,3]", (string?)res15); + Assert.Equal("[2,3]", (string?)res16); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START filter_set_relations + bool res17 = db.JSON().Set("doc", "$", "{\"a\":[[1,2],[1,5],[]]}"); + Console.WriteLine(res17); // >>> True + + RedisResult res18 = db.JSON().Get("doc", path: "$.a[?@ subsetof [1,2,3]]"); + Console.WriteLine(res18); // >>> [[1,2],[]] + + bool res19 = db.JSON().Set("doc", "$", "{\"a\":[[1,9],[8,9],[]]}"); + Console.WriteLine(res19); // >>> True + + RedisResult res20 = db.JSON().Get("doc", path: "$.a[?@ anyof [1,2,3]]"); + Console.WriteLine(res20); // >>> [[1,9]] + + bool res21 = db.JSON().Set("doc", "$", "{\"a\":[[4,5],[1,9],[]]}"); + Console.WriteLine(res21); // >>> True + + RedisResult res22 = db.JSON().Get("doc", path: "$.a[?@ noneof [1,2,3]]"); + Console.WriteLine(res22); // >>> [[4,5],[]] + // STEP_END + + // REMOVE_START + Assert.True(res17); + Assert.Equal("[[1,2],[]]", (string?)res18); + Assert.True(res19); + Assert.Equal("[[1,9]]", (string?)res20); + Assert.True(res21); + Assert.Equal("[[4,5],[]]", (string?)res22); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START filter_size_empty + bool res23 = db.JSON().Set("doc", "$", "{\"a\":[[4,5],[1],[7,8,9]]}"); + Console.WriteLine(res23); // >>> True + + RedisResult res24 = db.JSON().Get("doc", path: "$.a[?@ sizeof 2]"); + Console.WriteLine(res24); // >>> [[4,5]] + + bool res25 = db.JSON().Set("doc", "$", "{\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]}"); + Console.WriteLine(res25); // >>> True + + RedisResult res26 = db.JSON().Get("doc", path: "$.a[?@ empty true]"); + Console.WriteLine(res26); // >>> [[],"",{}] + + RedisResult res27 = db.JSON().Get("doc", path: "$.a[?@ empty false]"); + Console.WriteLine(res27); // >>> [[1],[2,3],{"k":1}] + // STEP_END + + // REMOVE_START + Assert.True(res23); + Assert.Equal("[[4,5]]", (string?)res24); + Assert.True(res25); + Assert.Equal("[[],\"\",{}]", (string?)res26); + Assert.Equal("[[1],[2,3],{\"k\":1}]", (string?)res27); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START filter_getkeys + bool res28 = db.JSON().Set( + "doc", + "$", + "{\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}" + ); + Console.WriteLine(res28); // >>> True + + RedisResult res29 = db.JSON().Get("doc", path: "$.obj~"); + Console.WriteLine(res29); // >>> ["x","y"] + + RedisResult res30 = db.JSON().Get("doc", path: "$~"); + Console.WriteLine(res30); // >>> ["obj","books"] + + RedisResult res31 = db.JSON().Get("doc", path: "$.books~"); + Console.WriteLine(res31); // >>> [] + // STEP_END + + // REMOVE_START + Assert.True(res28); + Assert.Equal("[\"x\",\"y\"]", (string?)res29); + Assert.Equal("[\"obj\",\"books\"]", (string?)res30); + Assert.Equal("[]", (string?)res31); + db.KeyDelete("doc"); + // REMOVE_END + + + // HIDE_START + } +} +// HIDE_END diff --git a/local_examples/json_path_ops/predis/JsonPathOpsTest.php b/local_examples/json_path_ops/predis/JsonPathOpsTest.php new file mode 100644 index 0000000000..1afecbc588 --- /dev/null +++ b/local_examples/json_path_ops/predis/JsonPathOpsTest.php @@ -0,0 +1,223 @@ +// EXAMPLE: json_path_ops +redis = new PredisClient([ + 'scheme' => 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'database' => 0, + ]); + + // Clean up before each test + $this->redis->flushall(); + } + + public function testJsonPathOps(): void + { + // STEP_START filter_negation + $res1 = $this->redis->jsonset('doc', '$', json_encode( + [["a" => 1, "b" => 1], ["b" => 2], ["a" => 1], ["c" => 3]] + )); + echo $res1 . PHP_EOL; // >>> OK + + $res2 = $this->redis->jsonget('doc', '', '', '', '$[?!@.a]'); + echo $res2 . PHP_EOL; // >>> [{"b":2},{"c":3}] + + $res3 = $this->redis->jsonget('doc', '', '', '', '$[?!(@.a==1)]'); + echo $res3 . PHP_EOL; // >>> [{"b":2},{"c":3}] + + $res4 = $this->redis->jsonget('doc', '', '', '', '$[?!@.a && @.b]'); + echo $res4 . PHP_EOL; // >>> [{"b":2}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res1); + $this->assertEquals('[{"b":2},{"c":3}]', $res2); + $this->assertEquals('[{"b":2},{"c":3}]', $res3); + $this->assertEquals('[{"b":2}]', $res4); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START filter_literal_eq + $res5 = $this->redis->jsonset('doc', '$', json_encode([ + "arrs" => [[1], [2], [1, 2], [1, [2]]], + "objs" => [["x" => 1], ["x" => 2], ["y" => 1]], + ])); + echo $res5 . PHP_EOL; // >>> OK + + $res6 = $this->redis->jsonget('doc', '', '', '', '$.arrs[?(@ == [1])]'); + echo $res6 . PHP_EOL; // >>> [[1]] + + $res7 = $this->redis->jsonget('doc', '', '', '', '$.arrs[?(@ == [1,[2]])]'); + echo $res7 . PHP_EOL; // >>> [[1,[2]]] + + $res8 = $this->redis->jsonget('doc', '', '', '', '$.objs[?(@ == {"x":1})]'); + echo $res8 . PHP_EOL; // >>> [{"x":1}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res5); + $this->assertEquals('[[1]]', $res6); + $this->assertEquals('[[1,[2]]]', $res7); + $this->assertEquals('[{"x":1}]', $res8); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START filter_arithmetic + $res9 = $this->redis->jsonset('doc', '$', json_encode( + [["a" => 2, "b" => 3], ["a" => 5, "b" => 2]] + )); + echo $res9 . PHP_EOL; // >>> OK + + $res10 = $this->redis->jsonget('doc', '', '', '', '$[?@.a + 1 == 3]'); + echo $res10 . PHP_EOL; // >>> [{"a":2,"b":3}] + + $res11 = $this->redis->jsonget('doc', '', '', '', '$[?@.a + @.b * 2 == 8]'); + echo $res11 . PHP_EOL; // >>> [{"a":2,"b":3}] + + $res12 = $this->redis->jsonget('doc', '', '', '', '$[?(@.a + @.b) * 2 == 10]'); + echo $res12 . PHP_EOL; // >>> [{"a":2,"b":3}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res9); + $this->assertEquals('[{"a":2,"b":3}]', $res10); + $this->assertEquals('[{"a":2,"b":3}]', $res11); + $this->assertEquals('[{"a":2,"b":3}]', $res12); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START filter_membership + $res13 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [1, 2, 3, 4], "allow" => [2, 3]] + )); + echo $res13 . PHP_EOL; // >>> OK + + $res14 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ in [2,4]]'); + echo $res14 . PHP_EOL; // >>> [2,4] + + $res15 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ nin [2,4]]'); + echo $res15 . PHP_EOL; // >>> [1,3] + + $res16 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ in $.allow]'); + echo $res16 . PHP_EOL; // >>> [2,3] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res13); + $this->assertEquals('[2,4]', $res14); + $this->assertEquals('[1,3]', $res15); + $this->assertEquals('[2,3]', $res16); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START filter_set_relations + $res17 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [[1, 2], [1, 5], []]] + )); + echo $res17 . PHP_EOL; // >>> OK + + $res18 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ subsetof [1,2,3]]'); + echo $res18 . PHP_EOL; // >>> [[1,2],[]] + + $res19 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [[1, 9], [8, 9], []]] + )); + echo $res19 . PHP_EOL; // >>> OK + + $res20 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ anyof [1,2,3]]'); + echo $res20 . PHP_EOL; // >>> [[1,9]] + + $res21 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [[4, 5], [1, 9], []]] + )); + echo $res21 . PHP_EOL; // >>> OK + + $res22 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ noneof [1,2,3]]'); + echo $res22 . PHP_EOL; // >>> [[4,5],[]] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res17); + $this->assertEquals('[[1,2],[]]', $res18); + $this->assertEquals('OK', $res19); + $this->assertEquals('[[1,9]]', $res20); + $this->assertEquals('OK', $res21); + $this->assertEquals('[[4,5],[]]', $res22); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START filter_size_empty + $res23 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [[4, 5], [1], [7, 8, 9]]] + )); + echo $res23 . PHP_EOL; // >>> OK + + $res24 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ sizeof 2]'); + echo $res24 . PHP_EOL; // >>> [[4,5]] + + $res25 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [[], [1], "", [2, 3], new \stdClass(), ["k" => 1]]] + )); + echo $res25 . PHP_EOL; // >>> OK + + $res26 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ empty true]'); + echo $res26 . PHP_EOL; // >>> [[],"",{}] + + $res27 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ empty false]'); + echo $res27 . PHP_EOL; // >>> [[1],[2,3],{"k":1}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res23); + $this->assertEquals('[[4,5]]', $res24); + $this->assertEquals('OK', $res25); + $this->assertEquals('[[],"",{}]', $res26); + $this->assertEquals('[[1],[2,3],{"k":1}]', $res27); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START filter_getkeys + $res28 = $this->redis->jsonset('doc', '$', json_encode([ + "obj" => ["x" => 1, "y" => 2], + "books" => [["t" => "a"], ["t" => "b"]], + ])); + echo $res28 . PHP_EOL; // >>> OK + + $res29 = $this->redis->jsonget('doc', '', '', '', '$.obj~'); + echo $res29 . PHP_EOL; // >>> ["x","y"] + + $res30 = $this->redis->jsonget('doc', '', '', '', '$~'); + echo $res30 . PHP_EOL; // >>> ["obj","books"] + + $res31 = $this->redis->jsonget('doc', '', '', '', '$.books~'); + echo $res31 . PHP_EOL; // >>> [] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res28); + $this->assertEquals('["x","y"]', $res29); + $this->assertEquals('["obj","books"]', $res30); + $this->assertEquals('[]', $res31); + $this->redis->del('doc'); + // REMOVE_END + + } + + protected function tearDown(): void + { + // Clean up after each test + $this->redis->flushall(); + $this->redis->disconnect(); + } +} diff --git a/local_examples/json_path_ops/redis-py/json_path_ops.py b/local_examples/json_path_ops/redis-py/json_path_ops.py new file mode 100644 index 0000000000..430ed2b265 --- /dev/null +++ b/local_examples/json_path_ops/redis-py/json_path_ops.py @@ -0,0 +1,201 @@ +# EXAMPLE: json_path_ops +# HIDE_START +import redis + +r = redis.Redis(decode_responses=True) +# HIDE_END + +# REMOVE_START +r.delete("doc") +# REMOVE_END + +# STEP_START filter_negation +res1 = r.json().set("doc", "$", [{"a": 1, "b": 1}, {"b": 2}, {"a": 1}, {"c": 3}]) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$[?!@.a]") +print(res2) +# >>> [{'b': 2}, {'c': 3}] + +res3 = r.json().get("doc", "$[?!(@.a==1)]") +print(res3) +# >>> [{'b': 2}, {'c': 3}] + +res4 = r.json().get("doc", "$[?!@.a && @.b]") +print(res4) +# >>> [{'b': 2}] +# STEP_END + +# REMOVE_START +assert res2 == [{"b": 2}, {"c": 3}] +assert res3 == [{"b": 2}, {"c": 3}] +assert res4 == [{"b": 2}] +r.delete("doc") +# REMOVE_END + +# STEP_START filter_literal_eq +res1 = r.json().set( + "doc", + "$", + {"arrs": [[1], [2], [1, 2], [1, [2]]], "objs": [{"x": 1}, {"x": 2}, {"y": 1}]}, +) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.arrs[?(@ == [1])]") +print(res2) +# >>> [[1]] + +res3 = r.json().get("doc", "$.arrs[?(@ == [1,[2]])]") +print(res3) +# >>> [[1, [2]]] + +res4 = r.json().get("doc", '$.objs[?(@ == {"x":1})]') +print(res4) +# >>> [{'x': 1}] +# STEP_END + +# REMOVE_START +assert res2 == [[1]] +assert res3 == [[1, [2]]] +assert res4 == [{"x": 1}] +r.delete("doc") +# REMOVE_END + +# STEP_START filter_arithmetic +res1 = r.json().set("doc", "$", [{"a": 2, "b": 3}, {"a": 5, "b": 2}]) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$[?@.a + 1 == 3]") +print(res2) +# >>> [{'a': 2, 'b': 3}] + +res3 = r.json().get("doc", "$[?@.a + @.b * 2 == 8]") +print(res3) +# >>> [{'a': 2, 'b': 3}] + +res4 = r.json().get("doc", "$[?(@.a + @.b) * 2 == 10]") +print(res4) +# >>> [{'a': 2, 'b': 3}] +# STEP_END + +# REMOVE_START +assert res2 == [{"a": 2, "b": 3}] +assert res3 == [{"a": 2, "b": 3}] +assert res4 == [{"a": 2, "b": 3}] +r.delete("doc") +# REMOVE_END + +# STEP_START filter_membership +res1 = r.json().set("doc", "$", {"a": [1, 2, 3, 4], "allow": [2, 3]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?@ in [2,4]]") +print(res2) +# >>> [2, 4] + +res3 = r.json().get("doc", "$.a[?@ nin [2,4]]") +print(res3) +# >>> [1, 3] + +res4 = r.json().get("doc", "$.a[?@ in $.allow]") +print(res4) +# >>> [2, 3] +# STEP_END + +# REMOVE_START +assert res2 == [2, 4] +assert res3 == [1, 3] +assert res4 == [2, 3] +r.delete("doc") +# REMOVE_END + +# STEP_START filter_set_relations +res1 = r.json().set("doc", "$", {"a": [[1, 2], [1, 5], []]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?@ subsetof [1,2,3]]") +print(res2) +# >>> [[1, 2], []] + +res3 = r.json().set("doc", "$", {"a": [[1, 9], [8, 9], []]}) +print(res3) +# >>> True + +res4 = r.json().get("doc", "$.a[?@ anyof [1,2,3]]") +print(res4) +# >>> [[1, 9]] + +res5 = r.json().set("doc", "$", {"a": [[4, 5], [1, 9], []]}) +print(res5) +# >>> True + +res6 = r.json().get("doc", "$.a[?@ noneof [1,2,3]]") +print(res6) +# >>> [[4, 5], []] +# STEP_END + +# REMOVE_START +assert res2 == [[1, 2], []] +assert res4 == [[1, 9]] +assert res6 == [[4, 5], []] +r.delete("doc") +# REMOVE_END + +# STEP_START filter_size_empty +res1 = r.json().set("doc", "$", {"a": [[4, 5], [1], [7, 8, 9]]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?@ sizeof 2]") +print(res2) +# >>> [[4, 5]] + +res3 = r.json().set("doc", "$", {"a": [[], [1], "", [2, 3], {}, {"k": 1}]}) +print(res3) +# >>> True + +res4 = r.json().get("doc", "$.a[?@ empty true]") +print(res4) +# >>> [[], '', {}] + +res5 = r.json().get("doc", "$.a[?@ empty false]") +print(res5) +# >>> [[1], [2, 3], {'k': 1}] +# STEP_END + +# REMOVE_START +assert res2 == [[4, 5]] +assert res4 == [[], "", {}] +assert res5 == [[1], [2, 3], {"k": 1}] +r.delete("doc") +# REMOVE_END + +# STEP_START filter_getkeys +res1 = r.json().set("doc", "$", {"obj": {"x": 1, "y": 2}, "books": [{"t": "a"}, {"t": "b"}]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.obj~") +print(res2) +# >>> ['x', 'y'] + +res3 = r.json().get("doc", "$~") +print(res3) +# >>> ['obj', 'books'] + +res4 = r.json().get("doc", "$.books~") +print(res4) +# >>> [] +# STEP_END + +# REMOVE_START +assert res2 == ["x", "y"] +assert res3 == ["obj", "books"] +assert res4 == [] +r.delete("doc") +# REMOVE_END diff --git a/local_examples/json_path_ops/ruby/json_path_ops.rb b/local_examples/json_path_ops/ruby/json_path_ops.rb new file mode 100644 index 0000000000..717377fe3d --- /dev/null +++ b/local_examples/json_path_ops/ruby/json_path_ops.rb @@ -0,0 +1,174 @@ +# EXAMPLE: json_path_ops +# HIDE_START +require 'redis' + +r = Redis.new +# HIDE_END + +# REMOVE_START +def assert_equal(expected, actual) + raise "Expected #{expected.inspect}, got #{actual.inspect}" unless actual == expected +end + +r.del('doc') +# REMOVE_END + +# STEP_START filter_negation +res1 = r.json_set('doc', '$', [{ 'a' => 1, 'b' => 1 }, { 'b' => 2 }, { 'a' => 1 }, { 'c' => 3 }]) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$[?!@.a]') +p res2 # >>> [{"b"=>2}, {"c"=>3}] + +res3 = r.json_get('doc', '$[?!(@.a==1)]') +p res3 # >>> [{"b"=>2}, {"c"=>3}] + +res4 = r.json_get('doc', '$[?!@.a && @.b]') +p res4 # >>> [{"b"=>2}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'b' => 2 }, { 'c' => 3 }], res2) +assert_equal([{ 'b' => 2 }, { 'c' => 3 }], res3) +assert_equal([{ 'b' => 2 }], res4) +r.del('doc') +# REMOVE_END + +# STEP_START filter_literal_eq +res1 = r.json_set( + 'doc', '$', + { 'arrs' => [[1], [2], [1, 2], [1, [2]]], 'objs' => [{ 'x' => 1 }, { 'x' => 2 }, { 'y' => 1 }] } +) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.arrs[?(@ == [1])]') +p res2 # >>> [[1]] + +res3 = r.json_get('doc', '$.arrs[?(@ == [1,[2]])]') +p res3 # >>> [[1, [2]]] + +res4 = r.json_get('doc', '$.objs[?(@ == {"x":1})]') +p res4 # >>> [{"x"=>1}] +# STEP_END + +# REMOVE_START +assert_equal([[1]], res2) +assert_equal([[1, [2]]], res3) +assert_equal([{ 'x' => 1 }], res4) +r.del('doc') +# REMOVE_END + +# STEP_START filter_arithmetic +res1 = r.json_set('doc', '$', [{ 'a' => 2, 'b' => 3 }, { 'a' => 5, 'b' => 2 }]) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$[?@.a + 1 == 3]') +p res2 # >>> [{"a"=>2, "b"=>3}] + +res3 = r.json_get('doc', '$[?@.a + @.b * 2 == 8]') +p res3 # >>> [{"a"=>2, "b"=>3}] + +res4 = r.json_get('doc', '$[?(@.a + @.b) * 2 == 10]') +p res4 # >>> [{"a"=>2, "b"=>3}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'a' => 2, 'b' => 3 }], res2) +assert_equal([{ 'a' => 2, 'b' => 3 }], res3) +assert_equal([{ 'a' => 2, 'b' => 3 }], res4) +r.del('doc') +# REMOVE_END + +# STEP_START filter_membership +res1 = r.json_set('doc', '$', { 'a' => [1, 2, 3, 4], 'allow' => [2, 3] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?@ in [2,4]]') +p res2 # >>> [2, 4] + +res3 = r.json_get('doc', '$.a[?@ nin [2,4]]') +p res3 # >>> [1, 3] + +res4 = r.json_get('doc', '$.a[?@ in $.allow]') +p res4 # >>> [2, 3] +# STEP_END + +# REMOVE_START +assert_equal([2, 4], res2) +assert_equal([1, 3], res3) +assert_equal([2, 3], res4) +r.del('doc') +# REMOVE_END + +# STEP_START filter_set_relations +res1 = r.json_set('doc', '$', { 'a' => [[1, 2], [1, 5], []] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?@ subsetof [1,2,3]]') +p res2 # >>> [[1, 2], []] + +res3 = r.json_set('doc', '$', { 'a' => [[1, 9], [8, 9], []] }) +puts res3 # >>> OK + +res4 = r.json_get('doc', '$.a[?@ anyof [1,2,3]]') +p res4 # >>> [[1, 9]] + +res5 = r.json_set('doc', '$', { 'a' => [[4, 5], [1, 9], []] }) +puts res5 # >>> OK + +res6 = r.json_get('doc', '$.a[?@ noneof [1,2,3]]') +p res6 # >>> [[4, 5], []] +# STEP_END + +# REMOVE_START +assert_equal([[1, 2], []], res2) +assert_equal([[1, 9]], res4) +assert_equal([[4, 5], []], res6) +r.del('doc') +# REMOVE_END + +# STEP_START filter_size_empty +res1 = r.json_set('doc', '$', { 'a' => [[4, 5], [1], [7, 8, 9]] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?@ sizeof 2]') +p res2 # >>> [[4, 5]] + +res3 = r.json_set('doc', '$', { 'a' => [[], [1], '', [2, 3], {}, { 'k' => 1 }] }) +puts res3 # >>> OK + +res4 = r.json_get('doc', '$.a[?@ empty true]') +p res4 # >>> [[], "", {}] + +res5 = r.json_get('doc', '$.a[?@ empty false]') +p res5 # >>> [[1], [2, 3], {"k"=>1}] +# STEP_END + +# REMOVE_START +assert_equal([[4, 5]], res2) +assert_equal([[], '', {}], res4) +assert_equal([[1], [2, 3], { 'k' => 1 }], res5) +r.del('doc') +# REMOVE_END + +# STEP_START filter_getkeys +res1 = r.json_set('doc', '$', { 'obj' => { 'x' => 1, 'y' => 2 }, 'books' => [{ 't' => 'a' }, { 't' => 'b' }] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.obj~') +p res2 # >>> ["x", "y"] + +res3 = r.json_get('doc', '$~') +p res3 # >>> ["obj", "books"] + +res4 = r.json_get('doc', '$.books~') +p res4 # >>> [] +# STEP_END + +# REMOVE_START +assert_equal(%w[x y], res2) +assert_equal(%w[obj books], res3) +assert_equal([], res4) +r.del('doc') +r.close +# REMOVE_END diff --git a/local_examples/json_path_ops/rust-async/json_path_ops.rs b/local_examples/json_path_ops/rust-async/json_path_ops.rs new file mode 100644 index 0000000000..92a637ab23 --- /dev/null +++ b/local_examples/json_path_ops/rust-async/json_path_ops.rs @@ -0,0 +1,453 @@ +// EXAMPLE: json_path_ops +#[cfg(test)] +mod json_path_ops_tests { + use redis::{AsyncCommands, JsonAsyncCommands}; + use serde_json::json; + + #[tokio::test] + async fn run() { + let mut r = match redis::Client::open("redis://127.0.0.1") { + Ok(client) => { + match client.get_multiplexed_async_connection().await { + Ok(conn) => conn, + Err(e) => { + println!("Failed to connect to Redis: {e}"); + return; + } + } + }, + Err(e) => { + println!("Failed to create Redis client: {e}"); + return; + } + }; + + // Clean up any existing data + let _: Result = r.del("doc").await; + + // STEP_START filter_negation + let _: bool = match r.json_set("doc", "$", &json!([{"a":1,"b":1},{"b":2},{"a":1},{"c":3}])).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$[?!@.a]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [{"b":2},{"c":3}] + // REMOVE_START + assert_eq!(res1, r#"[{"b":2},{"c":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?!(@.a==1)]").await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [{"b":2},{"c":3}] + // REMOVE_START + assert_eq!(res2, r#"[{"b":2},{"c":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?!@.a && @.b]").await { + Ok(res3) => { + let res3: String = res3; + println!("{res3}"); // >>> [{"b":2}] + // REMOVE_START + assert_eq!(res3, r#"[{"b":2}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START filter_literal_eq + let _: bool = match r.json_set( + "doc", + "$", + &json!({"arrs":[[1],[2],[1,2],[1,[2]]],"objs":[{"x":1},{"x":2},{"y":1}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.arrs[?(@ == [1])]").await { + Ok(res4) => { + let res4: String = res4; + println!("{res4}"); // >>> [[1]] + // REMOVE_START + assert_eq!(res4, "[[1]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.arrs[?(@ == [1,[2]])]").await { + Ok(res5) => { + let res5: String = res5; + println!("{res5}"); // >>> [[1,[2]]] + // REMOVE_START + assert_eq!(res5, "[[1,[2]]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", r#"$.objs[?(@ == {"x":1})]"#).await { + Ok(res6) => { + let res6: String = res6; + println!("{res6}"); // >>> [{"x":1}] + // REMOVE_START + assert_eq!(res6, r#"[{"x":1}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START filter_arithmetic + let _: bool = match r.json_set("doc", "$", &json!([{"a":2,"b":3},{"a":5,"b":2}])).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$[?@.a + 1 == 3]").await { + Ok(res7) => { + let res7: String = res7; + println!("{res7}"); // >>> [{"a":2,"b":3}] + // REMOVE_START + assert_eq!(res7, r#"[{"a":2,"b":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?@.a + @.b * 2 == 8]").await { + Ok(res8) => { + let res8: String = res8; + println!("{res8}"); // >>> [{"a":2,"b":3}] + // REMOVE_START + assert_eq!(res8, r#"[{"a":2,"b":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?(@.a + @.b) * 2 == 10]").await { + Ok(res9) => { + let res9: String = res9; + println!("{res9}"); // >>> [{"a":2,"b":3}] + // REMOVE_START + assert_eq!(res9, r#"[{"a":2,"b":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START filter_membership + let _: bool = match r.json_set("doc", "$", &json!({"a":[1,2,3,4],"allow":[2,3]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?@ in [2,4]]").await { + Ok(res10) => { + let res10: String = res10; + println!("{res10}"); // >>> [2,4] + // REMOVE_START + assert_eq!(res10, "[2,4]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ nin [2,4]]").await { + Ok(res11) => { + let res11: String = res11; + println!("{res11}"); // >>> [1,3] + // REMOVE_START + assert_eq!(res11, "[1,3]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ in $.allow]").await { + Ok(res12) => { + let res12: String = res12; + println!("{res12}"); // >>> [2,3] + // REMOVE_START + assert_eq!(res12, "[2,3]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START filter_set_relations + let _: bool = match r.json_set("doc", "$", &json!({"a":[[1,2],[1,5],[]]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?@ subsetof [1,2,3]]").await { + Ok(res13) => { + let res13: String = res13; + println!("{res13}"); // >>> [[1,2],[]] + // REMOVE_START + assert_eq!(res13, "[[1,2],[]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set("doc", "$", &json!({"a":[[1,9],[8,9],[]]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?@ anyof [1,2,3]]").await { + Ok(res14) => { + let res14: String = res14; + println!("{res14}"); // >>> [[1,9]] + // REMOVE_START + assert_eq!(res14, "[[1,9]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set("doc", "$", &json!({"a":[[4,5],[1,9],[]]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?@ noneof [1,2,3]]").await { + Ok(res15) => { + let res15: String = res15; + println!("{res15}"); // >>> [[4,5],[]] + // REMOVE_START + assert_eq!(res15, "[[4,5],[]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START filter_size_empty + let _: bool = match r.json_set("doc", "$", &json!({"a":[[4,5],[1],[7,8,9]]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?@ sizeof 2]").await { + Ok(res16) => { + let res16: String = res16; + println!("{res16}"); // >>> [[4,5]] + // REMOVE_START + assert_eq!(res16, "[[4,5]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set("doc", "$", &json!({"a":[[],[1],"",[2,3],{},{"k":1}]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?@ empty true]").await { + Ok(res17) => { + let res17: String = res17; + println!("{res17}"); // >>> [[],"",{}] + // REMOVE_START + assert_eq!(res17, r#"[[],"",{}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ empty false]").await { + Ok(res18) => { + let res18: String = res18; + println!("{res18}"); // >>> [[1],[2,3],{"k":1}] + // REMOVE_START + assert_eq!(res18, r#"[[1],[2,3],{"k":1}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START filter_getkeys + let _: bool = match r.json_set( + "doc", + "$", + &json!({"obj":{"x":1,"y":2},"books":[{"t":"a"},{"t":"b"}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.obj~").await { + Ok(res19) => { + let res19: String = res19; + println!("{res19}"); // >>> ["x","y"] + // REMOVE_START + assert_eq!(res19, r#"["x","y"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$~").await { + Ok(res20) => { + let res20: String = res20; + println!("{res20}"); // >>> ["obj","books"] + // REMOVE_START + assert_eq!(res20, r#"["obj","books"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.books~").await { + Ok(res21) => { + let res21: String = res21; + println!("{res21}"); // >>> [] + // REMOVE_START + assert_eq!(res21, "[]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + } +} diff --git a/local_examples/json_path_ops/rust-sync/json_path_ops.rs b/local_examples/json_path_ops/rust-sync/json_path_ops.rs new file mode 100644 index 0000000000..b9e38ebdef --- /dev/null +++ b/local_examples/json_path_ops/rust-sync/json_path_ops.rs @@ -0,0 +1,513 @@ +// EXAMPLE: json_path_ops +#[cfg(test)] +mod json_path_ops_tests { + use redis::{Commands, JsonCommands}; + use serde_json::json; + + #[test] + fn run() { + let mut r = match redis::Client::open("redis://127.0.0.1") { + Ok(client) => { + match client.get_connection() { + Ok(conn) => conn, + Err(e) => { + println!("Failed to connect to Redis: {e}"); + return; + } + } + }, + Err(e) => { + println!("Failed to create Redis client: {e}"); + return; + } + }; + + // Clean up any existing data + let _: Result = r.del("doc"); + + // STEP_START filter_negation + match r.json_set("doc", "$", &json!([{"a":1,"b":1},{"b":2},{"a":1},{"c":3}])) { + Ok(res1) => { + let res1: bool = res1; + println!("{}", if res1 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res1); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?!@.a]") { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [{"b":2},{"c":3}] + // REMOVE_START + assert_eq!(res2, r#"[{"b":2},{"c":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?!(@.a==1)]") { + Ok(res3) => { + let res3: String = res3; + println!("{res3}"); // >>> [{"b":2},{"c":3}] + // REMOVE_START + assert_eq!(res3, r#"[{"b":2},{"c":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?!@.a && @.b]") { + Ok(res4) => { + let res4: String = res4; + println!("{res4}"); // >>> [{"b":2}] + // REMOVE_START + assert_eq!(res4, r#"[{"b":2}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START filter_literal_eq + match r.json_set( + "doc", + "$", + &json!({"arrs":[[1],[2],[1,2],[1,[2]]],"objs":[{"x":1},{"x":2},{"y":1}]}), + ) { + Ok(res5) => { + let res5: bool = res5; + println!("{}", if res5 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res5); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.arrs[?(@ == [1])]") { + Ok(res6) => { + let res6: String = res6; + println!("{res6}"); // >>> [[1]] + // REMOVE_START + assert_eq!(res6, "[[1]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.arrs[?(@ == [1,[2]])]") { + Ok(res7) => { + let res7: String = res7; + println!("{res7}"); // >>> [[1,[2]]] + // REMOVE_START + assert_eq!(res7, "[[1,[2]]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", r#"$.objs[?(@ == {"x":1})]"#) { + Ok(res8) => { + let res8: String = res8; + println!("{res8}"); // >>> [{"x":1}] + // REMOVE_START + assert_eq!(res8, r#"[{"x":1}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START filter_arithmetic + match r.json_set("doc", "$", &json!([{"a":2,"b":3},{"a":5,"b":2}])) { + Ok(res9) => { + let res9: bool = res9; + println!("{}", if res9 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res9); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?@.a + 1 == 3]") { + Ok(res10) => { + let res10: String = res10; + println!("{res10}"); // >>> [{"a":2,"b":3}] + // REMOVE_START + assert_eq!(res10, r#"[{"a":2,"b":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?@.a + @.b * 2 == 8]") { + Ok(res11) => { + let res11: String = res11; + println!("{res11}"); // >>> [{"a":2,"b":3}] + // REMOVE_START + assert_eq!(res11, r#"[{"a":2,"b":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?(@.a + @.b) * 2 == 10]") { + Ok(res12) => { + let res12: String = res12; + println!("{res12}"); // >>> [{"a":2,"b":3}] + // REMOVE_START + assert_eq!(res12, r#"[{"a":2,"b":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START filter_membership + match r.json_set("doc", "$", &json!({"a":[1,2,3,4],"allow":[2,3]})) { + Ok(res13) => { + let res13: bool = res13; + println!("{}", if res13 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res13); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ in [2,4]]") { + Ok(res14) => { + let res14: String = res14; + println!("{res14}"); // >>> [2,4] + // REMOVE_START + assert_eq!(res14, "[2,4]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ nin [2,4]]") { + Ok(res15) => { + let res15: String = res15; + println!("{res15}"); // >>> [1,3] + // REMOVE_START + assert_eq!(res15, "[1,3]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ in $.allow]") { + Ok(res16) => { + let res16: String = res16; + println!("{res16}"); // >>> [2,3] + // REMOVE_START + assert_eq!(res16, "[2,3]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START filter_set_relations + match r.json_set("doc", "$", &json!({"a":[[1,2],[1,5],[]]})) { + Ok(res17) => { + let res17: bool = res17; + println!("{}", if res17 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res17); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ subsetof [1,2,3]]") { + Ok(res18) => { + let res18: String = res18; + println!("{res18}"); // >>> [[1,2],[]] + // REMOVE_START + assert_eq!(res18, "[[1,2],[]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"a":[[1,9],[8,9],[]]})) { + Ok(res19) => { + let res19: bool = res19; + println!("{}", if res19 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res19); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ anyof [1,2,3]]") { + Ok(res20) => { + let res20: String = res20; + println!("{res20}"); // >>> [[1,9]] + // REMOVE_START + assert_eq!(res20, "[[1,9]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"a":[[4,5],[1,9],[]]})) { + Ok(res21) => { + let res21: bool = res21; + println!("{}", if res21 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res21); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ noneof [1,2,3]]") { + Ok(res22) => { + let res22: String = res22; + println!("{res22}"); // >>> [[4,5],[]] + // REMOVE_START + assert_eq!(res22, "[[4,5],[]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START filter_size_empty + match r.json_set("doc", "$", &json!({"a":[[4,5],[1],[7,8,9]]})) { + Ok(res23) => { + let res23: bool = res23; + println!("{}", if res23 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res23); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ sizeof 2]") { + Ok(res24) => { + let res24: String = res24; + println!("{res24}"); // >>> [[4,5]] + // REMOVE_START + assert_eq!(res24, "[[4,5]]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"a":[[],[1],"",[2,3],{},{"k":1}]})) { + Ok(res25) => { + let res25: bool = res25; + println!("{}", if res25 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res25); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ empty true]") { + Ok(res26) => { + let res26: String = res26; + println!("{res26}"); // >>> [[],"",{}] + // REMOVE_START + assert_eq!(res26, r#"[[],"",{}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?@ empty false]") { + Ok(res27) => { + let res27: String = res27; + println!("{res27}"); // >>> [[1],[2,3],{"k":1}] + // REMOVE_START + assert_eq!(res27, r#"[[1],[2,3],{"k":1}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START filter_getkeys + match r.json_set( + "doc", + "$", + &json!({"obj":{"x":1,"y":2},"books":[{"t":"a"},{"t":"b"}]}), + ) { + Ok(res28) => { + let res28: bool = res28; + println!("{}", if res28 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res28); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.obj~") { + Ok(res29) => { + let res29: String = res29; + println!("{res29}"); // >>> ["x","y"] + // REMOVE_START + assert_eq!(res29, r#"["x","y"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$~") { + Ok(res30) => { + let res30: String = res30; + println!("{res30}"); // >>> ["obj","books"] + // REMOVE_START + assert_eq!(res30, r#"["obj","books"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.books~") { + Ok(res31) => { + let res31: String = res31; + println!("{res31}"); // >>> [] + // REMOVE_START + assert_eq!(res31, "[]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + } +}