From c99dab972401267f0aa06024a9ecb3c2a2ad3fca Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 4 Sep 2026 13:17:30 +0100 Subject: [PATCH 1/2] DOC-7032: Add tested client examples for JSONPath functions Move the illustrative CLI blocks in "Functions" into runnable clients-example demos under "Filter examples", covering length(), count(), value(), keys(), match()/search(), concat(), abs()/ceiling()/floor(), first()/last()/index(), min()/max()/avg()/sum()/stddev(), and append(). Extends the json_path_ops TCE set from the filter-operators PR; verified against a live Redis 8.10 server across all 11 clients. Co-Authored-By: Claude Sonnet 5 --- content/develop/data-types/json/path.md | 210 ++++---- .../go-redis/json_path_ops_test.go | 506 +++++++++++++++++ .../jedis/JsonPathOpsExample.java | 164 ++++++ .../lettuce-async/JsonPathOpsExample.java | 304 +++++++++++ .../lettuce-reactive/JsonPathOpsExample.java | 335 ++++++++++++ .../json_path_ops/node-redis/json-path-ops.js | 176 ++++++ .../nredisstack/JsonPathOpsExample.cs | 188 +++++++ .../json_path_ops/predis/JsonPathOpsTest.php | 216 ++++++++ .../json_path_ops/redis-py/json_path_ops.py | 206 +++++++ .../json_path_ops/ruby/json_path_ops.rb | 174 ++++++ .../json_path_ops/rust-async/json_path_ops.rs | 444 +++++++++++++++ .../json_path_ops/rust-sync/json_path_ops.rs | 508 ++++++++++++++++++ 12 files changed, 3338 insertions(+), 93 deletions(-) diff --git a/content/develop/data-types/json/path.md b/content/develop/data-types/json/path.md index b2a2ca64be..cf0c6de1fc 100644 --- a/content/develop/data-types/json/path.md +++ b/content/develop/data-types/json/path.md @@ -109,135 +109,43 @@ Functions can appear inside filter expressions and, when they return a value, as Returns the number of characters in a string, elements in an array, or members in an object. Any other type produces *Nothing*. -``` -> JSON.SET doc $ '{"a":[[1,2,3],[1],"abcd","x"]}' -OK -> JSON.GET doc '$.a[?length(@) > 2]' -"[[1,2,3],\"abcd\"]" -``` - ### `count()` Returns the number of nodes selected by a query. An absent path counts as `0`; a single node counts as `1`. -``` -> JSON.SET doc $ '[{"a":1,"b":2,"c":3},{"a":1}]' -OK -> JSON.GET doc '$[?count(@.*) == 3]' -"[{\"a\":1,\"b\":2,\"c\":3}]" -``` - ### `value()` Returns the value of a query that selects exactly one node. A query that selects zero or more than one node produces *Nothing*. -``` -> JSON.SET doc $ '[{"a":1},{"a":2}]' -OK -> JSON.GET doc '$[?value(@.a) == 1]' -"[{\"a\":1}]" -``` - ### `keys()` Returns the member names of an object as a list of strings, like the `~` operator. Unlike `~`, `keys()` is composable and can be chained with other functions. -``` -> JSON.SET doc $ '{"obj":{"x":1,"y":2}}' -OK -> JSON.GET doc '$.obj.keys()' -"[\"x\",\"y\"]" -> JSON.GET doc '$.obj.keys().count()' -"[2]" -``` - ### `match()` and `search()` Both test a string against a regular expression pattern ([RFC 9485](https://datatracker.ietf.org/doc/rfc9485/) I-Regexp). `match()` requires the whole string to match (anchored), while `search()` matches any substring (the same behavior as the `=~` operator). An invalid pattern produces no match. -``` -> JSON.SET doc $ '{"a":["abc","xabc","a","b"]}' -OK -> JSON.GET doc '$.a[?match(@, "a.*")]' -"[\"abc\",\"a\"]" -> JSON.SET doc $ '{"a":["abc","xyz","b"]}' -OK -> JSON.GET doc '$.a[?search(@, "b")]' -"[\"abc\",\"b\"]" -``` - ### `concat()` Concatenates its string arguments into a single string. It requires at least one argument, and any non-string argument produces *Nothing*. -``` -> JSON.SET doc $ '{"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}' -OK -> JSON.GET doc '$.a[?concat(@.x, @.y) == "ab"]' -"[{\"x\":\"a\",\"y\":\"b\"}]" -``` - ### `abs()`, `ceiling()`, and `floor()` Operate on a number: `abs()` returns the absolute value, `ceiling()` rounds up to the nearest integer, and `floor()` rounds down. An integer argument stays an integer and a floating-point argument stays a float. A result that overflows the signed 64-bit integer range produces *Nothing*. -``` -> JSON.SET doc $ '{"a":[2.1,3.9,1.0]}' -OK -> JSON.GET doc '$.a[?ceiling(@) == 3]' -"[2.1]" -> JSON.SET doc $ '{"a":[2.1,2.9,3.5]}' -OK -> JSON.GET doc '$.a[?floor(@) == 2]' -"[2.1,2.9]" -> JSON.SET doc $ '{"a":[{"n":-5},{"n":5},{"n":-3}]}' -OK -> JSON.GET doc '$.a[?abs(@.n) == 5]' -"[{\"n\":-5},{\"n\":5}]" -``` - ### `first()`, `last()`, and `index()` `first(array)` and `last(array)` return the first and last element of an array. `index(array, n)` returns the element at index `n`; a negative `n` counts from the end, a fractional `n` is truncated toward zero, and an out-of-range index produces *Nothing*. -``` -> JSON.SET doc $ '{"a":[{"n":[1,2]},{"n":[9,8]}]}' -OK -> JSON.GET doc '$.a[?first(@.n) == 1]' -"[{\"n\":[1,2]}]" -> JSON.GET doc '$.a[?last(@.n) == 8]' -"[{\"n\":[9,8]}]" -> JSON.GET doc '$.a[?index(@.n, -1) == 2]' -"[{\"n\":[1,2]}]" -``` - ### `min()`, `max()`, `avg()`, `sum()`, and `stddev()` These aggregation functions operate on an array of numbers. `stddev()` returns the *population* standard deviation (dividing by N). The functions are strict: an array that contains any non-numeric element, or an empty array, produces *Nothing* — elements are never silently skipped. -``` -> JSON.SET doc $ '{"a":[{"n":[3,1,2]},{"n":[5,6]}]}' -OK -> JSON.GET doc '$.a[?sum(@.n) == 6]' -"[{\"n\":[3,1,2]}]" -> JSON.GET doc '$.a[?avg(@.n) == 2]' -"[{\"n\":[3,1,2]}]" -``` - ### `append()` `append(value, ...)` returns the matched array with the given value or values added after its elements. It is a read-only query-time projection and does not modify the stored document — to mutate an array in place, use the [`JSON.ARRAPPEND`]({{< relref "commands/json.arrappend/" >}}) command instead. A multiple-value argument is added as a single element (it is not spread), and a *Nothing* argument makes the whole result *Nothing*. -``` -> JSON.SET doc $ '{"arr":[1,2,3]}' -OK -> JSON.GET doc '$.arr.append(9)' -"[1,2,3,9]" -> JSON.SET doc $ '{"books":[{"t":"a","price":30},{"t":"b","price":5}]}' -OK -> JSON.GET doc '$.books[?(@.price >= 10)].append({"t":"X"})' -"[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]" -``` +See [Filter examples](#filter-examples) for a runnable example of each function. ## JSONPath examples @@ -516,6 +424,122 @@ OK "[]" {{< /clients-example >}} +Beginning with Redis 8.10, JSONPath also supports the functions described in [Functions](#functions). The following examples demonstrate each one. + +#### `length()` + +{{< clients-example set="json_path_ops" step="func_length" description="Length function: Use length() to get the number of characters in a string, elements in an array, or members in an object" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[[1,2,3],[1],"abcd","x"]}' +OK +> JSON.GET doc '$.a[?length(@) > 2]' +"[[1,2,3],\"abcd\"]" +{{< /clients-example >}} + +#### `count()` + +{{< clients-example set="json_path_ops" step="func_count" description="Count function: Use count() to get the number of nodes selected by a query" difficulty="advanced" >}} +> JSON.SET doc $ '[{"a":1,"b":2,"c":3},{"a":1}]' +OK +> JSON.GET doc '$[?count(@.*) == 3]' +"[{\"a\":1,\"b\":2,\"c\":3}]" +{{< /clients-example >}} + +#### `value()` + +{{< clients-example set="json_path_ops" step="func_value" description="Value function: Use value() to get the value of a query that selects exactly one node" difficulty="advanced" >}} +> JSON.SET doc $ '[{"a":1},{"a":2}]' +OK +> JSON.GET doc '$[?value(@.a) == 1]' +"[{\"a\":1}]" +{{< /clients-example >}} + +#### `keys()` + +{{< clients-example set="json_path_ops" step="func_keys" description="Keys function: Use keys() to get an object's member names as a composable, chainable list of strings" difficulty="advanced" >}} +> JSON.SET doc $ '{"obj":{"x":1,"y":2}}' +OK +> JSON.GET doc '$.obj.keys()' +"[\"x\",\"y\"]" +> JSON.GET doc '$.obj.keys().count()' +"[2]" +{{< /clients-example >}} + +#### `match()` and `search()` + +{{< clients-example set="json_path_ops" step="func_match_search" description="Regex functions: Use match() for an anchored (whole-string) regular expression match and search() for a partial match" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":["abc","xabc","a","b"]}' +OK +> JSON.GET doc '$.a[?match(@, "a.*")]' +"[\"abc\",\"a\"]" +> JSON.SET doc $ '{"a":["abc","xyz","b"]}' +OK +> JSON.GET doc '$.a[?search(@, "b")]' +"[\"abc\",\"b\"]" +{{< /clients-example >}} + +#### `concat()` + +{{< clients-example set="json_path_ops" step="func_concat" description="Concat function: Use concat() to join string arguments into a single string for comparison in a filter" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}' +OK +> JSON.GET doc '$.a[?concat(@.x, @.y) == "ab"]' +"[{\"x\":\"a\",\"y\":\"b\"}]" +{{< /clients-example >}} + +#### `abs()`, `ceiling()`, and `floor()` + +{{< clients-example set="json_path_ops" step="func_math" description="Numeric functions: Use abs(), ceiling(), and floor() to transform a number before comparing it in a filter" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[2.1,3.9,1.0]}' +OK +> JSON.GET doc '$.a[?ceiling(@) == 3]' +"[2.1]" +> JSON.SET doc $ '{"a":[2.1,2.9,3.5]}' +OK +> JSON.GET doc '$.a[?floor(@) == 2]' +"[2.1,2.9]" +> JSON.SET doc $ '{"a":[{"n":-5},{"n":5},{"n":-3}]}' +OK +> JSON.GET doc '$.a[?abs(@.n) == 5]' +"[{\"n\":-5},{\"n\":5}]" +{{< /clients-example >}} + +#### `first()`, `last()`, and `index()` + +{{< clients-example set="json_path_ops" step="func_array_access" description="Array access functions: Use first(), last(), and index() to pick a single element out of an array before comparing it" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[{"n":[1,2]},{"n":[9,8]}]}' +OK +> JSON.GET doc '$.a[?first(@.n) == 1]' +"[{\"n\":[1,2]}]" +> JSON.GET doc '$.a[?last(@.n) == 8]' +"[{\"n\":[9,8]}]" +> JSON.GET doc '$.a[?index(@.n, -1) == 2]' +"[{\"n\":[1,2]}]" +{{< /clients-example >}} + +#### `min()`, `max()`, `avg()`, `sum()`, and `stddev()` + +{{< clients-example set="json_path_ops" step="func_aggregate" description="Aggregation functions: Use sum(), avg(), min(), max(), or stddev() to reduce an array of numbers to a single value in a filter" difficulty="advanced" >}} +> JSON.SET doc $ '{"a":[{"n":[3,1,2]},{"n":[5,6]}]}' +OK +> JSON.GET doc '$.a[?sum(@.n) == 6]' +"[{\"n\":[3,1,2]}]" +> JSON.GET doc '$.a[?avg(@.n) == 2]' +"[{\"n\":[3,1,2]}]" +{{< /clients-example >}} + +#### `append()` + +{{< clients-example set="json_path_ops" step="func_append" description="Append function: Use append() as a read-only, query-time projection that adds a value after an array's elements without modifying the stored document" difficulty="advanced" >}} +> JSON.SET doc $ '{"arr":[1,2,3]}' +OK +> JSON.GET doc '$.arr.append(9)' +"[1,2,3,9]" +> JSON.SET doc $ '{"books":[{"t":"a","price":30},{"t":"b","price":5}]}' +OK +> JSON.GET doc '$.books[?(@.price >= 10)].append({"t":"X"})' +"[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]" +{{< /clients-example >}} + ### Update examples You can also use JSONPath queries when you want to update specific sections of a JSON document. 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 index 658d67f1e6..dccdb409e1 100644 --- 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 @@ -442,3 +442,509 @@ func ExampleClient_json_path_ops_filter_getkeys() { // ["obj","books"] // [] } + +func ExampleClient_json_path_ops_func_length() { + 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 func_length + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[[1,2,3],[1],"abcd","x"]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?length(@) > 2]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [[1,2,3],"abcd"] + // STEP_END + + // Output: + // OK + // [[1,2,3],"abcd"] +} + +func ExampleClient_json_path_ops_func_count() { + 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 func_count + res1, err := rdb.JSONSet(ctx, "doc", "$", + `[{"a":1,"b":2,"c":3},{"a":1}]`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$[?count(@.*) == 3]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"a":1,"b":2,"c":3}] + // STEP_END + + // Output: + // OK + // [{"a":1,"b":2,"c":3}] +} + +func ExampleClient_json_path_ops_func_value() { + 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 func_value + res1, err := rdb.JSONSet(ctx, "doc", "$", + `[{"a":1},{"a":2}]`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$[?value(@.a) == 1]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"a":1}] + // STEP_END + + // Output: + // OK + // [{"a":1}] +} + +func ExampleClient_json_path_ops_func_keys() { + 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 func_keys + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"obj":{"x":1,"y":2}}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.obj.keys()`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> ["x","y"] + + res3, err := rdb.JSONGet(ctx, "doc", `$.obj.keys().count()`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [2] + // STEP_END + + // Output: + // OK + // ["x","y"] + // [2] +} + +func ExampleClient_json_path_ops_func_match_search() { + 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 func_match_search + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":["abc","xabc","a","b"]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?match(@, "a.*")]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> ["abc","a"] + + res3, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":["abc","xyz","b"]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> OK + + res4, err := rdb.JSONGet(ctx, "doc", `$.a[?search(@, "b")]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> ["abc","b"] + // STEP_END + + // Output: + // OK + // ["abc","a"] + // OK + // ["abc","b"] +} + +func ExampleClient_json_path_ops_func_concat() { + 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 func_concat + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?concat(@.x, @.y) == "ab"]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"x":"a","y":"b"}] + // STEP_END + + // Output: + // OK + // [{"x":"a","y":"b"}] +} + +func ExampleClient_json_path_ops_func_math() { + 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 func_math + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[2.1,3.9,1.0]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?ceiling(@) == 3]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [2.1] + + res3, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[2.1,2.9,3.5]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> OK + + res4, err := rdb.JSONGet(ctx, "doc", `$.a[?floor(@) == 2]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [2.1,2.9] + + res5, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[{"n":-5},{"n":5},{"n":-3}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res5) // >>> OK + + res6, err := rdb.JSONGet(ctx, "doc", `$.a[?abs(@.n) == 5]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res6) // >>> [{"n":-5},{"n":5}] + // STEP_END + + // Output: + // OK + // [2.1] + // OK + // [2.1,2.9] + // OK + // [{"n":-5},{"n":5}] +} + +func ExampleClient_json_path_ops_func_array_access() { + 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 func_array_access + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[{"n":[1,2]},{"n":[9,8]}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?first(@.n) == 1]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"n":[1,2]}] + + res3, err := rdb.JSONGet(ctx, "doc", `$.a[?last(@.n) == 8]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [{"n":[9,8]}] + + res4, err := rdb.JSONGet(ctx, "doc", `$.a[?index(@.n, -1) == 2]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [{"n":[1,2]}] + // STEP_END + + // Output: + // OK + // [{"n":[1,2]}] + // [{"n":[9,8]}] + // [{"n":[1,2]}] +} + +func ExampleClient_json_path_ops_func_aggregate() { + 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 func_aggregate + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"a":[{"n":[3,1,2]},{"n":[5,6]}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.a[?sum(@.n) == 6]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [{"n":[3,1,2]}] + + res3, err := rdb.JSONGet(ctx, "doc", `$.a[?avg(@.n) == 2]`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [{"n":[3,1,2]}] + // STEP_END + + // Output: + // OK + // [{"n":[3,1,2]}] + // [{"n":[3,1,2]}] +} + +func ExampleClient_json_path_ops_func_append() { + 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 func_append + res1, err := rdb.JSONSet(ctx, "doc", "$", + `{"arr":[1,2,3]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.JSONGet(ctx, "doc", `$.arr.append(9)`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [1,2,3,9] + + res3, err := rdb.JSONSet(ctx, "doc", "$", + `{"books":[{"t":"a","price":30},{"t":"b","price":5}]}`, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> OK + + res4, err := rdb.JSONGet(ctx, "doc", `$.books[?(@.price >= 10)].append({"t":"X"})`).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [{"t":"a","price":30},{"t":"X"}] + // STEP_END + + // Output: + // OK + // [1,2,3,9] + // OK + // [{"t":"a","price":30},{"t":"X"}] +} diff --git a/local_examples/json_path_ops/jedis/JsonPathOpsExample.java b/local_examples/json_path_ops/jedis/JsonPathOpsExample.java index d99c364d81..6f35bc61ee 100644 --- a/local_examples/json_path_ops/jedis/JsonPathOpsExample.java +++ b/local_examples/json_path_ops/jedis/JsonPathOpsExample.java @@ -184,6 +184,170 @@ public void run() { jedis.del("doc"); // REMOVE_END + // STEP_START func_length + String res32 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[[1,2,3],[1],\"abcd\",\"x\"]}"); + System.out.println(res32); // >>> OK + + Object res33 = jedis.jsonGet("doc", new Path2("$.a[?length(@) > 2]")); + System.out.println(res33); // >>> [[1,2,3],"abcd"] + // STEP_END + // REMOVE_START + assertEquals("[[1,2,3],\"abcd\"]", res33.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_count + String res34 = jedis.jsonSet("doc", new Path2("$"), "[{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}]"); + System.out.println(res34); // >>> OK + + Object res35 = jedis.jsonGet("doc", new Path2("$[?count(@.*) == 3]")); + System.out.println(res35); // >>> [{"a":1,"b":2,"c":3}] + // STEP_END + // REMOVE_START + assertEquals("[{\"a\":1,\"b\":2,\"c\":3}]", res35.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_value + String res36 = jedis.jsonSet("doc", new Path2("$"), "[{\"a\":1},{\"a\":2}]"); + System.out.println(res36); // >>> OK + + Object res37 = jedis.jsonGet("doc", new Path2("$[?value(@.a) == 1]")); + System.out.println(res37); // >>> [{"a":1}] + // STEP_END + // REMOVE_START + assertEquals("[{\"a\":1}]", res37.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_keys + String res38 = jedis.jsonSet("doc", new Path2("$"), "{\"obj\":{\"x\":1,\"y\":2}}"); + System.out.println(res38); // >>> OK + + Object res39 = jedis.jsonGet("doc", new Path2("$.obj.keys()")); + System.out.println(res39); // >>> ["x","y"] + + Object res40 = jedis.jsonGet("doc", new Path2("$.obj.keys().count()")); + System.out.println(res40); // >>> [2] + // STEP_END + // REMOVE_START + assertEquals("[\"x\",\"y\"]", res39.toString()); + assertEquals("[2]", res40.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_match_search + String res41 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]}"); + System.out.println(res41); // >>> OK + + Object res42 = jedis.jsonGet("doc", new Path2("$.a[?match(@, \"a.*\")]")); + System.out.println(res42); // >>> ["abc","a"] + + String res43 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[\"abc\",\"xyz\",\"b\"]}"); + System.out.println(res43); // >>> OK + + Object res44 = jedis.jsonGet("doc", new Path2("$.a[?search(@, \"b\")]")); + System.out.println(res44); // >>> ["abc","b"] + // STEP_END + // REMOVE_START + assertEquals("[\"abc\",\"a\"]", res42.toString()); + assertEquals("[\"abc\",\"b\"]", res44.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_concat + String res45 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}"); + System.out.println(res45); // >>> OK + + Object res46 = jedis.jsonGet("doc", new Path2("$.a[?concat(@.x, @.y) == \"ab\"]")); + System.out.println(res46); // >>> [{"x":"a","y":"b"}] + // STEP_END + // REMOVE_START + assertEquals("[{\"x\":\"a\",\"y\":\"b\"}]", res46.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_math + String res47 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[2.1,3.9,1.0]}"); + System.out.println(res47); // >>> OK + + Object res48 = jedis.jsonGet("doc", new Path2("$.a[?ceiling(@) == 3]")); + System.out.println(res48); // >>> [2.1] + + String res49 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[2.1,2.9,3.5]}"); + System.out.println(res49); // >>> OK + + Object res50 = jedis.jsonGet("doc", new Path2("$.a[?floor(@) == 2]")); + System.out.println(res50); // >>> [2.1,2.9] + + String res51 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}"); + System.out.println(res51); // >>> OK + + Object res52 = jedis.jsonGet("doc", new Path2("$.a[?abs(@.n) == 5]")); + System.out.println(res52); // >>> [{"n":-5},{"n":5}] + // STEP_END + // REMOVE_START + assertEquals("[2.1]", res48.toString()); + assertEquals("[2.1,2.9]", res50.toString()); + assertEquals("[{\"n\":-5},{\"n\":5}]", res52.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_array_access + String res53 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}"); + System.out.println(res53); // >>> OK + + Object res54 = jedis.jsonGet("doc", new Path2("$.a[?first(@.n) == 1]")); + System.out.println(res54); // >>> [{"n":[1,2]}] + + Object res55 = jedis.jsonGet("doc", new Path2("$.a[?last(@.n) == 8]")); + System.out.println(res55); // >>> [{"n":[9,8]}] + + Object res56 = jedis.jsonGet("doc", new Path2("$.a[?index(@.n, -1) == 2]")); + System.out.println(res56); // >>> [{"n":[1,2]}] + // STEP_END + // REMOVE_START + assertEquals("[{\"n\":[1,2]}]", res54.toString()); + assertEquals("[{\"n\":[9,8]}]", res55.toString()); + assertEquals("[{\"n\":[1,2]}]", res56.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_aggregate + String res57 = jedis.jsonSet("doc", new Path2("$"), "{\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}"); + System.out.println(res57); // >>> OK + + Object res58 = jedis.jsonGet("doc", new Path2("$.a[?sum(@.n) == 6]")); + System.out.println(res58); // >>> [{"n":[3,1,2]}] + + Object res59 = jedis.jsonGet("doc", new Path2("$.a[?avg(@.n) == 2]")); + System.out.println(res59); // >>> [{"n":[3,1,2]}] + // STEP_END + // REMOVE_START + assertEquals("[{\"n\":[3,1,2]}]", res58.toString()); + assertEquals("[{\"n\":[3,1,2]}]", res59.toString()); + jedis.del("doc"); + // REMOVE_END + + // STEP_START func_append + String res60 = jedis.jsonSet("doc", new Path2("$"), "{\"arr\":[1,2,3]}"); + System.out.println(res60); // >>> OK + + Object res61 = jedis.jsonGet("doc", new Path2("$.arr.append(9)")); + System.out.println(res61); // >>> [1,2,3,9] + + String res62 = jedis.jsonSet("doc", new Path2("$"), "{\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}"); + System.out.println(res62); // >>> OK + + Object res63 = jedis.jsonGet("doc", new Path2("$.books[?(@.price >= 10)].append({\"t\":\"X\"})")); + System.out.println(res63); // >>> [{"t":"a","price":30},{"t":"X"}] + // STEP_END + // REMOVE_START + assertEquals("[1,2,3,9]", res61.toString()); + assertEquals("[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]", res63.toString()); + jedis.del("doc"); + // REMOVE_END + // HIDE_START jedis.close(); diff --git a/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java b/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java index e6655c35f3..a08a956303 100644 --- a/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java +++ b/local_examples/json_path_ops/lettuce-async/JsonPathOpsExample.java @@ -299,6 +299,310 @@ public void run() { asyncCommands.del("doc").toCompletableFuture().join(); // REMOVE_END + // STEP_START func_length + CompletableFuture funcLengthExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[1,2,3],[1],\"abcd\",\"x\"]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?length(@) > 2]")); + }).thenAccept(res2 -> { + System.out.println(res2); // >>> [[[1,2,3],"abcd"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[1,2,3],\"abcd\"]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcLengthExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_count + CompletableFuture funcCountExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("[{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}]")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?count(@.*) == 3]")); + }).thenAccept(res2 -> { + System.out.println(res2); // >>> [[{"a":1,"b":2,"c":3}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"a\":1,\"b\":2,\"c\":3}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcCountExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_value + CompletableFuture funcValueExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("[{\"a\":1},{\"a\":2}]")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$[?value(@.a) == 1]")); + }).thenAccept(res2 -> { + System.out.println(res2); // >>> [[{"a":1}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"a\":1}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcValueExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_keys + CompletableFuture funcKeysExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"obj\":{\"x\":1,\"y\":2}}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.obj.keys()")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [["x","y"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[\"x\",\"y\"]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.obj.keys().count()")); + }).thenAccept(res3 -> { + System.out.println(res3); // >>> [[2]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[2]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcKeysExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_match_search + CompletableFuture funcMatchSearchExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?match(@, \"a.*\")]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [["abc","a"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[\"abc\",\"a\"]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[\"abc\",\"xyz\",\"b\"]}")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?search(@, \"b\")]")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [["abc","b"]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[\"abc\",\"b\"]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcMatchSearchExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_concat + CompletableFuture funcConcatExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?concat(@.x, @.y) == \"ab\"]")); + }).thenAccept(res2 -> { + System.out.println(res2); // >>> [[{"x":"a","y":"b"}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"x\":\"a\",\"y\":\"b\"}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcConcatExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_math + CompletableFuture funcMathExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[2.1,3.9,1.0]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?ceiling(@) == 3]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[2.1]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[2.1]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[2.1,2.9,3.5]}")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?floor(@) == 2]")); + }).thenCompose(res4 -> { + System.out.println(res4); // >>> [[2.1,2.9]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[2.1,2.9]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}")); + }).thenCompose(res5 -> { + System.out.println(res5); // >>> OK + // REMOVE_START + assertThat(res5).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?abs(@.n) == 5]")); + }).thenAccept(res6 -> { + System.out.println(res6); // >>> [[{"n":-5},{"n":5}]] + // REMOVE_START + assertThat(res6.toString()).isEqualTo("[[{\"n\":-5},{\"n\":5}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcMathExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_array_access + CompletableFuture funcArrayAccessExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?first(@.n) == 1]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[{"n":[1,2]}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"n\":[1,2]}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?last(@.n) == 8]")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> [[{"n":[9,8]}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"n\":[9,8]}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?index(@.n, -1) == 2]")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[{"n":[1,2]}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"n\":[1,2]}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcArrayAccessExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_aggregate + CompletableFuture funcAggregateExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?sum(@.n) == 6]")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[{"n":[3,1,2]}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"n\":[3,1,2]}]]"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.a[?avg(@.n) == 2]")); + }).thenAccept(res3 -> { + System.out.println(res3); // >>> [[{"n":[3,1,2]}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"n\":[3,1,2]}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcAggregateExample.join(); + // REMOVE_START + asyncCommands.del("doc").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START func_append + CompletableFuture funcAppendExample = asyncCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"arr\":[1,2,3]}")) + .thenCompose(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", JsonPath.of("$.arr.append(9)")); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> [[1,2,3,9]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[1,2,3,9]]"); + // REMOVE_END + return asyncCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue( + "{\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}")); + }).thenCompose(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.jsonGet("doc", + JsonPath.of("$.books[?(@.price >= 10)].append({\"t\":\"X\"})")); + }).thenAccept(res4 -> { + System.out.println(res4); // >>> [[{"t":"a","price":30},{"t":"X"}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]]"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + + funcAppendExample.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 index 4c5d74ad52..bf017f949b 100644 --- a/local_examples/json_path_ops/lettuce-reactive/JsonPathOpsExample.java +++ b/local_examples/json_path_ops/lettuce-reactive/JsonPathOpsExample.java @@ -325,6 +325,341 @@ public void run() { reactiveCommands.del("doc").block(); // REMOVE_END + // STEP_START func_length + Mono funcLengthExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[[1,2,3],[1],\"abcd\",\"x\"]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?length(@) > 2]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[[1,2,3],"abcd"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[[1,2,3],\"abcd\"]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcLengthExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_count + Mono funcCountExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("[{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}]")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?count(@.*) == 3]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"a":1,"b":2,"c":3}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"a\":1,\"b\":2,\"c\":3}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcCountExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_value + Mono funcValueExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("[{\"a\":1},{\"a\":2}]")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$[?value(@.a) == 1]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"a":1}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"a\":1}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcValueExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_keys + Mono funcKeysExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"obj\":{\"x\":1,\"y\":2}}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.obj.keys()")).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("$.obj.keys().count()")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[2]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[2]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcKeysExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_match_search + Mono funcMatchSearchExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?match(@, \"a.*\")]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [["abc","a"]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[\"abc\",\"a\"]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[\"abc\",\"xyz\",\"b\"]}"))) + .doOnNext(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?search(@, \"b\")]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [["abc","b"]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[\"abc\",\"b\"]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcMatchSearchExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_concat + Mono funcConcatExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?concat(@.x, @.y) == \"ab\"]")) + .collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"x":"a","y":"b"}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"x\":\"a\",\"y\":\"b\"}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcConcatExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_math + Mono funcMathExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[2.1,3.9,1.0]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?ceiling(@) == 3]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[2.1]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[2.1]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[2.1,2.9,3.5]}"))) + .doOnNext(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?floor(@) == 2]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[2.1,2.9]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[2.1,2.9]]"); + // REMOVE_END + }) + .flatMap(res4 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}"))) + .doOnNext(res5 -> { + System.out.println(res5); // >>> OK + // REMOVE_START + assertThat(res5).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res5 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?abs(@.n) == 5]")).collectList()) + .doOnNext(res6 -> { + System.out.println(res6); // >>> [[{"n":-5},{"n":5}]] + // REMOVE_START + assertThat(res6.toString()).isEqualTo("[[{\"n\":-5},{\"n\":5}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcMathExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_array_access + Mono funcArrayAccessExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?first(@.n) == 1]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"n":[1,2]}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"n\":[1,2]}]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?last(@.n) == 8]")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[{"n":[9,8]}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"n\":[9,8]}]]"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?index(@.n, -1) == 2]")).collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[{"n":[1,2]}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"n\":[1,2]}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcArrayAccessExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_aggregate + Mono funcAggregateExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue("{\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?sum(@.n) == 6]")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[{"n":[3,1,2]}]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[{\"n\":[3,1,2]}]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.a[?avg(@.n) == 2]")).collectList()) + .doOnNext(res3 -> { + System.out.println(res3); // >>> [[{"n":[3,1,2]}]] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[[{\"n\":[3,1,2]}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcAggregateExample.block(); + // REMOVE_START + reactiveCommands.del("doc").block(); + // REMOVE_END + + // STEP_START func_append + Mono funcAppendExample = reactiveCommands + .jsonSet("doc", JsonPath.ROOT_PATH, parser.createJsonValue("{\"arr\":[1,2,3]}")) + .doOnNext(res1 -> { + System.out.println(res1); // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res1 -> reactiveCommands.jsonGet("doc", JsonPath.of("$.arr.append(9)")).collectList()) + .doOnNext(res2 -> { + System.out.println(res2); // >>> [[1,2,3,9]] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[[1,2,3,9]]"); + // REMOVE_END + }) + .flatMap(res2 -> reactiveCommands.jsonSet("doc", JsonPath.ROOT_PATH, + parser.createJsonValue( + "{\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}"))) + .doOnNext(res3 -> { + System.out.println(res3); // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + }) + .flatMap(res3 -> reactiveCommands + .jsonGet("doc", JsonPath.of("$.books[?(@.price >= 10)].append({\"t\":\"X\"})")) + .collectList()) + .doOnNext(res4 -> { + System.out.println(res4); // >>> [[{"t":"a","price":30},{"t":"X"}]] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]]"); + // REMOVE_END + }) + .then(); + // STEP_END + + funcAppendExample.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 index 30702f9c39..a98795481a 100644 --- a/local_examples/json_path_ops/node-redis/json-path-ops.js +++ b/local_examples/json_path_ops/node-redis/json-path-ops.js @@ -173,6 +173,182 @@ assert.deepEqual(res31, []); await client.del('doc'); // REMOVE_END +// STEP_START func_length +const res32 = await client.json.set('doc', '$', { a: [[1, 2, 3], [1], 'abcd', 'x'] }); +console.log(res32); // >>> OK + +const res33 = await client.json.get('doc', { path: '$.a[?length(@) > 2]' }); +console.log(res33); // >>> [ [ 1, 2, 3 ], 'abcd' ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res33, [[1, 2, 3], 'abcd']); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_count +const res34 = await client.json.set('doc', '$', [{ a: 1, b: 2, c: 3 }, { a: 1 }]); +console.log(res34); // >>> OK + +const res35 = await client.json.get('doc', { path: '$[?count(@.*) == 3]' }); +console.log(res35); // >>> [ { a: 1, b: 2, c: 3 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res35, [{ a: 1, b: 2, c: 3 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_value +const res36 = await client.json.set('doc', '$', [{ a: 1 }, { a: 2 }]); +console.log(res36); // >>> OK + +const res37 = await client.json.get('doc', { path: '$[?value(@.a) == 1]' }); +console.log(res37); // >>> [ { a: 1 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res37, [{ a: 1 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_keys +const res38 = await client.json.set('doc', '$', { obj: { x: 1, y: 2 } }); +console.log(res38); // >>> OK + +const res39 = await client.json.get('doc', { path: '$.obj.keys()' }); +console.log(res39); // >>> [ 'x', 'y' ] + +const res40 = await client.json.get('doc', { path: '$.obj.keys().count()' }); +console.log(res40); // >>> [ 2 ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res39, ['x', 'y']); +assert.deepEqual(res40, [2]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_match_search +const res41 = await client.json.set('doc', '$', { a: ['abc', 'xabc', 'a', 'b'] }); +console.log(res41); // >>> OK + +const res42 = await client.json.get('doc', { path: '$.a[?match(@, "a.*")]' }); +console.log(res42); // >>> [ 'abc', 'a' ] + +const res43 = await client.json.set('doc', '$', { a: ['abc', 'xyz', 'b'] }); +console.log(res43); // >>> OK + +const res44 = await client.json.get('doc', { path: '$.a[?search(@, "b")]' }); +console.log(res44); // >>> [ 'abc', 'b' ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res42, ['abc', 'a']); +assert.deepEqual(res44, ['abc', 'b']); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_concat +const res45 = await client.json.set('doc', '$', { a: [{ x: 'a', y: 'b' }, { x: 'a', y: 'c' }] }); +console.log(res45); // >>> OK + +const res46 = await client.json.get('doc', { path: '$.a[?concat(@.x, @.y) == "ab"]' }); +console.log(res46); // >>> [ { x: 'a', y: 'b' } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res46, [{ x: 'a', y: 'b' }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_math +const res47 = await client.json.set('doc', '$', { a: [2.1, 3.9, 1.0] }); +console.log(res47); // >>> OK + +const res48 = await client.json.get('doc', { path: '$.a[?ceiling(@) == 3]' }); +console.log(res48); // >>> [ 2.1 ] + +const res49 = await client.json.set('doc', '$', { a: [2.1, 2.9, 3.5] }); +console.log(res49); // >>> OK + +const res50 = await client.json.get('doc', { path: '$.a[?floor(@) == 2]' }); +console.log(res50); // >>> [ 2.1, 2.9 ] + +const res51 = await client.json.set('doc', '$', { a: [{ n: -5 }, { n: 5 }, { n: -3 }] }); +console.log(res51); // >>> OK + +const res52 = await client.json.get('doc', { path: '$.a[?abs(@.n) == 5]' }); +console.log(res52); // >>> [ { n: -5 }, { n: 5 } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res48, [2.1]); +assert.deepEqual(res50, [2.1, 2.9]); +assert.deepEqual(res52, [{ n: -5 }, { n: 5 }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_array_access +const res53 = await client.json.set('doc', '$', { a: [{ n: [1, 2] }, { n: [9, 8] }] }); +console.log(res53); // >>> OK + +const res54 = await client.json.get('doc', { path: '$.a[?first(@.n) == 1]' }); +console.log(res54); // >>> [ { n: [ 1, 2 ] } ] + +const res55 = await client.json.get('doc', { path: '$.a[?last(@.n) == 8]' }); +console.log(res55); // >>> [ { n: [ 9, 8 ] } ] + +const res56 = await client.json.get('doc', { path: '$.a[?index(@.n, -1) == 2]' }); +console.log(res56); // >>> [ { n: [ 1, 2 ] } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res54, [{ n: [1, 2] }]); +assert.deepEqual(res55, [{ n: [9, 8] }]); +assert.deepEqual(res56, [{ n: [1, 2] }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_aggregate +const res57 = await client.json.set('doc', '$', { a: [{ n: [3, 1, 2] }, { n: [5, 6] }] }); +console.log(res57); // >>> OK + +const res58 = await client.json.get('doc', { path: '$.a[?sum(@.n) == 6]' }); +console.log(res58); // >>> [ { n: [ 3, 1, 2 ] } ] + +const res59 = await client.json.get('doc', { path: '$.a[?avg(@.n) == 2]' }); +console.log(res59); // >>> [ { n: [ 3, 1, 2 ] } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res58, [{ n: [3, 1, 2] }]); +assert.deepEqual(res59, [{ n: [3, 1, 2] }]); +await client.del('doc'); +// REMOVE_END + +// STEP_START func_append +const res60 = await client.json.set('doc', '$', { arr: [1, 2, 3] }); +console.log(res60); // >>> OK + +const res61 = await client.json.get('doc', { path: '$.arr.append(9)' }); +console.log(res61); // >>> [ 1, 2, 3, 9 ] + +const res62 = await client.json.set('doc', '$', { + books: [{ t: 'a', price: 30 }, { t: 'b', price: 5 }] +}); +console.log(res62); // >>> OK + +const res63 = await client.json.get('doc', { path: '$.books[?(@.price >= 10)].append({"t":"X"})' }); +console.log(res63); // >>> [ { t: 'a', price: 30 }, { t: 'X' } ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res61, [1, 2, 3, 9]); +assert.deepEqual(res63, [{ t: 'a', price: 30 }, { t: 'X' }]); +await client.del('doc'); +// REMOVE_END + // HIDE_START await client.close(); diff --git a/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs b/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs index 6e19daeb46..1d5f404cae 100644 --- a/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs +++ b/local_examples/json_path_ops/nredisstack/JsonPathOpsExample.cs @@ -215,6 +215,194 @@ public void Run() db.KeyDelete("doc"); // REMOVE_END + // STEP_START func_length + bool res32 = db.JSON().Set("doc", "$", "{\"a\":[[1,2,3],[1],\"abcd\",\"x\"]}"); + Console.WriteLine(res32); // >>> True + + RedisResult res33 = db.JSON().Get("doc", path: "$.a[?length(@) > 2]"); + Console.WriteLine(res33); // >>> [[1,2,3],"abcd"] + // STEP_END + + // REMOVE_START + Assert.True(res32); + Assert.Equal("[[1,2,3],\"abcd\"]", (string?)res33); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_count + bool res34 = db.JSON().Set("doc", "$", "[{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}]"); + Console.WriteLine(res34); // >>> True + + RedisResult res35 = db.JSON().Get("doc", path: "$[?count(@.*) == 3]"); + Console.WriteLine(res35); // >>> [{"a":1,"b":2,"c":3}] + // STEP_END + + // REMOVE_START + Assert.True(res34); + Assert.Equal("[{\"a\":1,\"b\":2,\"c\":3}]", (string?)res35); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_value + bool res36 = db.JSON().Set("doc", "$", "[{\"a\":1},{\"a\":2}]"); + Console.WriteLine(res36); // >>> True + + RedisResult res37 = db.JSON().Get("doc", path: "$[?value(@.a) == 1]"); + Console.WriteLine(res37); // >>> [{"a":1}] + // STEP_END + + // REMOVE_START + Assert.True(res36); + Assert.Equal("[{\"a\":1}]", (string?)res37); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_keys + bool res38 = db.JSON().Set("doc", "$", "{\"obj\":{\"x\":1,\"y\":2}}"); + Console.WriteLine(res38); // >>> True + + RedisResult res39 = db.JSON().Get("doc", path: "$.obj.keys()"); + Console.WriteLine(res39); // >>> ["x","y"] + + RedisResult res40 = db.JSON().Get("doc", path: "$.obj.keys().count()"); + Console.WriteLine(res40); // >>> [2] + // STEP_END + + // REMOVE_START + Assert.True(res38); + Assert.Equal("[\"x\",\"y\"]", (string?)res39); + Assert.Equal("[2]", (string?)res40); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_match_search + bool res41 = db.JSON().Set("doc", "$", "{\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]}"); + Console.WriteLine(res41); // >>> True + + RedisResult res42 = db.JSON().Get("doc", path: "$.a[?match(@, \"a.*\")]"); + Console.WriteLine(res42); // >>> ["abc","a"] + + bool res43 = db.JSON().Set("doc", "$", "{\"a\":[\"abc\",\"xyz\",\"b\"]}"); + Console.WriteLine(res43); // >>> True + + RedisResult res44 = db.JSON().Get("doc", path: "$.a[?search(@, \"b\")]"); + Console.WriteLine(res44); // >>> ["abc","b"] + // STEP_END + + // REMOVE_START + Assert.True(res41); + Assert.Equal("[\"abc\",\"a\"]", (string?)res42); + Assert.True(res43); + Assert.Equal("[\"abc\",\"b\"]", (string?)res44); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_concat + bool res45 = db.JSON().Set("doc", "$", "{\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}"); + Console.WriteLine(res45); // >>> True + + RedisResult res46 = db.JSON().Get("doc", path: "$.a[?concat(@.x, @.y) == \"ab\"]"); + Console.WriteLine(res46); // >>> [{"x":"a","y":"b"}] + // STEP_END + + // REMOVE_START + Assert.True(res45); + Assert.Equal("[{\"x\":\"a\",\"y\":\"b\"}]", (string?)res46); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_math + bool res47 = db.JSON().Set("doc", "$", "{\"a\":[2.1,3.9,1.0]}"); + Console.WriteLine(res47); // >>> True + + RedisResult res48 = db.JSON().Get("doc", path: "$.a[?ceiling(@) == 3]"); + Console.WriteLine(res48); // >>> [2.1] + + bool res49 = db.JSON().Set("doc", "$", "{\"a\":[2.1,2.9,3.5]}"); + Console.WriteLine(res49); // >>> True + + RedisResult res50 = db.JSON().Get("doc", path: "$.a[?floor(@) == 2]"); + Console.WriteLine(res50); // >>> [2.1,2.9] + + bool res51 = db.JSON().Set("doc", "$", "{\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}"); + Console.WriteLine(res51); // >>> True + + RedisResult res52 = db.JSON().Get("doc", path: "$.a[?abs(@.n) == 5]"); + Console.WriteLine(res52); // >>> [{"n":-5},{"n":5}] + // STEP_END + + // REMOVE_START + Assert.True(res47); + Assert.Equal("[2.1]", (string?)res48); + Assert.True(res49); + Assert.Equal("[2.1,2.9]", (string?)res50); + Assert.True(res51); + Assert.Equal("[{\"n\":-5},{\"n\":5}]", (string?)res52); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_array_access + bool res53 = db.JSON().Set("doc", "$", "{\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}"); + Console.WriteLine(res53); // >>> True + + RedisResult res54 = db.JSON().Get("doc", path: "$.a[?first(@.n) == 1]"); + Console.WriteLine(res54); // >>> [{"n":[1,2]}] + + RedisResult res55 = db.JSON().Get("doc", path: "$.a[?last(@.n) == 8]"); + Console.WriteLine(res55); // >>> [{"n":[9,8]}] + + RedisResult res56 = db.JSON().Get("doc", path: "$.a[?index(@.n, -1) == 2]"); + Console.WriteLine(res56); // >>> [{"n":[1,2]}] + // STEP_END + + // REMOVE_START + Assert.True(res53); + Assert.Equal("[{\"n\":[1,2]}]", (string?)res54); + Assert.Equal("[{\"n\":[9,8]}]", (string?)res55); + Assert.Equal("[{\"n\":[1,2]}]", (string?)res56); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_aggregate + bool res57 = db.JSON().Set("doc", "$", "{\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}"); + Console.WriteLine(res57); // >>> True + + RedisResult res58 = db.JSON().Get("doc", path: "$.a[?sum(@.n) == 6]"); + Console.WriteLine(res58); // >>> [{"n":[3,1,2]}] + + RedisResult res59 = db.JSON().Get("doc", path: "$.a[?avg(@.n) == 2]"); + Console.WriteLine(res59); // >>> [{"n":[3,1,2]}] + // STEP_END + + // REMOVE_START + Assert.True(res57); + Assert.Equal("[{\"n\":[3,1,2]}]", (string?)res58); + Assert.Equal("[{\"n\":[3,1,2]}]", (string?)res59); + db.KeyDelete("doc"); + // REMOVE_END + + // STEP_START func_append + bool res60 = db.JSON().Set("doc", "$", "{\"arr\":[1,2,3]}"); + Console.WriteLine(res60); // >>> True + + RedisResult res61 = db.JSON().Get("doc", path: "$.arr.append(9)"); + Console.WriteLine(res61); // >>> [1,2,3,9] + + bool res62 = db.JSON().Set("doc", "$", "{\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}"); + Console.WriteLine(res62); // >>> True + + RedisResult res63 = db.JSON().Get("doc", path: "$.books[?(@.price >= 10)].append({\"t\":\"X\"})"); + Console.WriteLine(res63); // >>> [{"t":"a","price":30},{"t":"X"}] + // STEP_END + + // REMOVE_START + Assert.True(res60); + Assert.Equal("[1,2,3,9]", (string?)res61); + Assert.True(res62); + Assert.Equal("[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]", (string?)res63); + db.KeyDelete("doc"); + // REMOVE_END + // HIDE_START } diff --git a/local_examples/json_path_ops/predis/JsonPathOpsTest.php b/local_examples/json_path_ops/predis/JsonPathOpsTest.php index 1afecbc588..1d17071aa4 100644 --- a/local_examples/json_path_ops/predis/JsonPathOpsTest.php +++ b/local_examples/json_path_ops/predis/JsonPathOpsTest.php @@ -212,6 +212,222 @@ public function testJsonPathOps(): void $this->redis->del('doc'); // REMOVE_END + // STEP_START func_length + $res32 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [[1, 2, 3], [1], "abcd", "x"]] + )); + echo $res32 . PHP_EOL; // >>> OK + + $res33 = $this->redis->jsonget('doc', '', '', '', '$.a[?length(@) > 2]'); + echo $res33 . PHP_EOL; // >>> [[1,2,3],"abcd"] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res32); + $this->assertEquals('[[1,2,3],"abcd"]', $res33); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_count + $res34 = $this->redis->jsonset('doc', '$', json_encode( + [["a" => 1, "b" => 2, "c" => 3], ["a" => 1]] + )); + echo $res34 . PHP_EOL; // >>> OK + + $res35 = $this->redis->jsonget('doc', '', '', '', '$[?count(@.*) == 3]'); + echo $res35 . PHP_EOL; // >>> [{"a":1,"b":2,"c":3}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res34); + $this->assertEquals('[{"a":1,"b":2,"c":3}]', $res35); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_value + $res36 = $this->redis->jsonset('doc', '$', json_encode( + [["a" => 1], ["a" => 2]] + )); + echo $res36 . PHP_EOL; // >>> OK + + $res37 = $this->redis->jsonget('doc', '', '', '', '$[?value(@.a) == 1]'); + echo $res37 . PHP_EOL; // >>> [{"a":1}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res36); + $this->assertEquals('[{"a":1}]', $res37); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_keys + $res38 = $this->redis->jsonset('doc', '$', json_encode( + ["obj" => ["x" => 1, "y" => 2]] + )); + echo $res38 . PHP_EOL; // >>> OK + + $res39 = $this->redis->jsonget('doc', '', '', '', '$.obj.keys()'); + echo $res39 . PHP_EOL; // >>> ["x","y"] + + $res40 = $this->redis->jsonget('doc', '', '', '', '$.obj.keys().count()'); + echo $res40 . PHP_EOL; // >>> [2] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res38); + $this->assertEquals('["x","y"]', $res39); + $this->assertEquals('[2]', $res40); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_match_search + $res41 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => ["abc", "xabc", "a", "b"]] + )); + echo $res41 . PHP_EOL; // >>> OK + + $res42 = $this->redis->jsonget('doc', '', '', '', '$.a[?match(@, "a.*")]'); + echo $res42 . PHP_EOL; // >>> ["abc","a"] + + $res43 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => ["abc", "xyz", "b"]] + )); + echo $res43 . PHP_EOL; // >>> OK + + $res44 = $this->redis->jsonget('doc', '', '', '', '$.a[?search(@, "b")]'); + echo $res44 . PHP_EOL; // >>> ["abc","b"] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res41); + $this->assertEquals('["abc","a"]', $res42); + $this->assertEquals('OK', $res43); + $this->assertEquals('["abc","b"]', $res44); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_concat + $res45 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [["x" => "a", "y" => "b"], ["x" => "a", "y" => "c"]]] + )); + echo $res45 . PHP_EOL; // >>> OK + + $res46 = $this->redis->jsonget('doc', '', '', '', '$.a[?concat(@.x, @.y) == "ab"]'); + echo $res46 . PHP_EOL; // >>> [{"x":"a","y":"b"}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res45); + $this->assertEquals('[{"x":"a","y":"b"}]', $res46); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_math + $res47 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [2.1, 3.9, 1.0]] + )); + echo $res47 . PHP_EOL; // >>> OK + + $res48 = $this->redis->jsonget('doc', '', '', '', '$.a[?ceiling(@) == 3]'); + echo $res48 . PHP_EOL; // >>> [2.1] + + $res49 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [2.1, 2.9, 3.5]] + )); + echo $res49 . PHP_EOL; // >>> OK + + $res50 = $this->redis->jsonget('doc', '', '', '', '$.a[?floor(@) == 2]'); + echo $res50 . PHP_EOL; // >>> [2.1,2.9] + + $res51 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [["n" => -5], ["n" => 5], ["n" => -3]]] + )); + echo $res51 . PHP_EOL; // >>> OK + + $res52 = $this->redis->jsonget('doc', '', '', '', '$.a[?abs(@.n) == 5]'); + echo $res52 . PHP_EOL; // >>> [{"n":-5},{"n":5}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res47); + $this->assertEquals('[2.1]', $res48); + $this->assertEquals('OK', $res49); + $this->assertEquals('[2.1,2.9]', $res50); + $this->assertEquals('OK', $res51); + $this->assertEquals('[{"n":-5},{"n":5}]', $res52); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_array_access + $res53 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [["n" => [1, 2]], ["n" => [9, 8]]]] + )); + echo $res53 . PHP_EOL; // >>> OK + + $res54 = $this->redis->jsonget('doc', '', '', '', '$.a[?first(@.n) == 1]'); + echo $res54 . PHP_EOL; // >>> [{"n":[1,2]}] + + $res55 = $this->redis->jsonget('doc', '', '', '', '$.a[?last(@.n) == 8]'); + echo $res55 . PHP_EOL; // >>> [{"n":[9,8]}] + + $res56 = $this->redis->jsonget('doc', '', '', '', '$.a[?index(@.n, -1) == 2]'); + echo $res56 . PHP_EOL; // >>> [{"n":[1,2]}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res53); + $this->assertEquals('[{"n":[1,2]}]', $res54); + $this->assertEquals('[{"n":[9,8]}]', $res55); + $this->assertEquals('[{"n":[1,2]}]', $res56); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_aggregate + $res57 = $this->redis->jsonset('doc', '$', json_encode( + ["a" => [["n" => [3, 1, 2]], ["n" => [5, 6]]]] + )); + echo $res57 . PHP_EOL; // >>> OK + + $res58 = $this->redis->jsonget('doc', '', '', '', '$.a[?sum(@.n) == 6]'); + echo $res58 . PHP_EOL; // >>> [{"n":[3,1,2]}] + + $res59 = $this->redis->jsonget('doc', '', '', '', '$.a[?avg(@.n) == 2]'); + echo $res59 . PHP_EOL; // >>> [{"n":[3,1,2]}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res57); + $this->assertEquals('[{"n":[3,1,2]}]', $res58); + $this->assertEquals('[{"n":[3,1,2]}]', $res59); + $this->redis->del('doc'); + // REMOVE_END + + // STEP_START func_append + $res60 = $this->redis->jsonset('doc', '$', json_encode( + ["arr" => [1, 2, 3]] + )); + echo $res60 . PHP_EOL; // >>> OK + + $res61 = $this->redis->jsonget('doc', '', '', '', '$.arr.append(9)'); + echo $res61 . PHP_EOL; // >>> [1,2,3,9] + + $res62 = $this->redis->jsonset('doc', '$', json_encode( + ["books" => [["t" => "a", "price" => 30], ["t" => "b", "price" => 5]]] + )); + echo $res62 . PHP_EOL; // >>> OK + + $res63 = $this->redis->jsonget('doc', '', '', '', '$.books[?(@.price >= 10)].append({"t":"X"})'); + echo $res63 . PHP_EOL; // >>> [{"t":"a","price":30},{"t":"X"}] + // STEP_END + + // REMOVE_START + $this->assertEquals('OK', $res60); + $this->assertEquals('[1,2,3,9]', $res61); + $this->assertEquals('OK', $res62); + $this->assertEquals('[{"t":"a","price":30},{"t":"X"}]', $res63); + $this->redis->del('doc'); + // REMOVE_END + } protected function tearDown(): void 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 index 430ed2b265..d007ce31da 100644 --- a/local_examples/json_path_ops/redis-py/json_path_ops.py +++ b/local_examples/json_path_ops/redis-py/json_path_ops.py @@ -199,3 +199,209 @@ assert res4 == [] r.delete("doc") # REMOVE_END + +# STEP_START func_length +res1 = r.json().set("doc", "$", {"a": [[1, 2, 3], [1], "abcd", "x"]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?length(@) > 2]") +print(res2) +# >>> [[1, 2, 3], 'abcd'] +# STEP_END + +# REMOVE_START +assert res2 == [[1, 2, 3], "abcd"] +r.delete("doc") +# REMOVE_END + +# STEP_START func_count +res1 = r.json().set("doc", "$", [{"a": 1, "b": 2, "c": 3}, {"a": 1}]) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$[?count(@.*) == 3]") +print(res2) +# >>> [{'a': 1, 'b': 2, 'c': 3}] +# STEP_END + +# REMOVE_START +assert res2 == [{"a": 1, "b": 2, "c": 3}] +r.delete("doc") +# REMOVE_END + +# STEP_START func_value +res1 = r.json().set("doc", "$", [{"a": 1}, {"a": 2}]) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$[?value(@.a) == 1]") +print(res2) +# >>> [{'a': 1}] +# STEP_END + +# REMOVE_START +assert res2 == [{"a": 1}] +r.delete("doc") +# REMOVE_END + +# STEP_START func_keys +res1 = r.json().set("doc", "$", {"obj": {"x": 1, "y": 2}}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.obj.keys()") +print(res2) +# >>> ['x', 'y'] + +res3 = r.json().get("doc", "$.obj.keys().count()") +print(res3) +# >>> [2] +# STEP_END + +# REMOVE_START +assert res2 == ["x", "y"] +assert res3 == [2] +r.delete("doc") +# REMOVE_END + +# STEP_START func_match_search +res1 = r.json().set("doc", "$", {"a": ["abc", "xabc", "a", "b"]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", '$.a[?match(@, "a.*")]') +print(res2) +# >>> ['abc', 'a'] + +res3 = r.json().set("doc", "$", {"a": ["abc", "xyz", "b"]}) +print(res3) +# >>> True + +res4 = r.json().get("doc", '$.a[?search(@, "b")]') +print(res4) +# >>> ['abc', 'b'] +# STEP_END + +# REMOVE_START +assert res2 == ["abc", "a"] +assert res4 == ["abc", "b"] +r.delete("doc") +# REMOVE_END + +# STEP_START func_concat +res1 = r.json().set("doc", "$", {"a": [{"x": "a", "y": "b"}, {"x": "a", "y": "c"}]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", '$.a[?concat(@.x, @.y) == "ab"]') +print(res2) +# >>> [{'x': 'a', 'y': 'b'}] +# STEP_END + +# REMOVE_START +assert res2 == [{"x": "a", "y": "b"}] +r.delete("doc") +# REMOVE_END + +# STEP_START func_math +res1 = r.json().set("doc", "$", {"a": [2.1, 3.9, 1.0]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?ceiling(@) == 3]") +print(res2) +# >>> [2.1] + +res3 = r.json().set("doc", "$", {"a": [2.1, 2.9, 3.5]}) +print(res3) +# >>> True + +res4 = r.json().get("doc", "$.a[?floor(@) == 2]") +print(res4) +# >>> [2.1, 2.9] + +res5 = r.json().set("doc", "$", {"a": [{"n": -5}, {"n": 5}, {"n": -3}]}) +print(res5) +# >>> True + +res6 = r.json().get("doc", "$.a[?abs(@.n) == 5]") +print(res6) +# >>> [{'n': -5}, {'n': 5}] +# STEP_END + +# REMOVE_START +assert res2 == [2.1] +assert res4 == [2.1, 2.9] +assert res6 == [{"n": -5}, {"n": 5}] +r.delete("doc") +# REMOVE_END + +# STEP_START func_array_access +res1 = r.json().set("doc", "$", {"a": [{"n": [1, 2]}, {"n": [9, 8]}]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?first(@.n) == 1]") +print(res2) +# >>> [{'n': [1, 2]}] + +res3 = r.json().get("doc", "$.a[?last(@.n) == 8]") +print(res3) +# >>> [{'n': [9, 8]}] + +res4 = r.json().get("doc", "$.a[?index(@.n, -1) == 2]") +print(res4) +# >>> [{'n': [1, 2]}] +# STEP_END + +# REMOVE_START +assert res2 == [{"n": [1, 2]}] +assert res3 == [{"n": [9, 8]}] +assert res4 == [{"n": [1, 2]}] +r.delete("doc") +# REMOVE_END + +# STEP_START func_aggregate +res1 = r.json().set("doc", "$", {"a": [{"n": [3, 1, 2]}, {"n": [5, 6]}]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.a[?sum(@.n) == 6]") +print(res2) +# >>> [{'n': [3, 1, 2]}] + +res3 = r.json().get("doc", "$.a[?avg(@.n) == 2]") +print(res3) +# >>> [{'n': [3, 1, 2]}] +# STEP_END + +# REMOVE_START +assert res2 == [{"n": [3, 1, 2]}] +assert res3 == [{"n": [3, 1, 2]}] +r.delete("doc") +# REMOVE_END + +# STEP_START func_append +res1 = r.json().set("doc", "$", {"arr": [1, 2, 3]}) +print(res1) +# >>> True + +res2 = r.json().get("doc", "$.arr.append(9)") +print(res2) +# >>> [1, 2, 3, 9] + +res3 = r.json().set("doc", "$", {"books": [{"t": "a", "price": 30}, {"t": "b", "price": 5}]}) +print(res3) +# >>> True + +res4 = r.json().get("doc", '$.books[?(@.price >= 10)].append({"t":"X"})') +print(res4) +# >>> [{'t': 'a', 'price': 30}, {'t': 'X'}] +# STEP_END + +# REMOVE_START +assert res2 == [1, 2, 3, 9] +assert res4 == [{"t": "a", "price": 30}, {"t": "X"}] +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 index 717377fe3d..7b73d4b205 100644 --- a/local_examples/json_path_ops/ruby/json_path_ops.rb +++ b/local_examples/json_path_ops/ruby/json_path_ops.rb @@ -170,5 +170,179 @@ def assert_equal(expected, actual) assert_equal(%w[obj books], res3) assert_equal([], res4) r.del('doc') +# REMOVE_END + +# STEP_START func_length +res1 = r.json_set('doc', '$', { 'a' => [[1, 2, 3], [1], 'abcd', 'x'] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?length(@) > 2]') +p res2 # >>> [[1, 2, 3], "abcd"] +# STEP_END + +# REMOVE_START +assert_equal([[1, 2, 3], 'abcd'], res2) +r.del('doc') +# REMOVE_END + +# STEP_START func_count +res1 = r.json_set('doc', '$', [{ 'a' => 1, 'b' => 2, 'c' => 3 }, { 'a' => 1 }]) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$[?count(@.*) == 3]') +p res2 # >>> [{"a"=>1, "b"=>2, "c"=>3}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'a' => 1, 'b' => 2, 'c' => 3 }], res2) +r.del('doc') +# REMOVE_END + +# STEP_START func_value +res1 = r.json_set('doc', '$', [{ 'a' => 1 }, { 'a' => 2 }]) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$[?value(@.a) == 1]') +p res2 # >>> [{"a"=>1}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'a' => 1 }], res2) +r.del('doc') +# REMOVE_END + +# STEP_START func_keys +res1 = r.json_set('doc', '$', { 'obj' => { 'x' => 1, 'y' => 2 } }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.obj.keys()') +p res2 # >>> ["x", "y"] + +res3 = r.json_get('doc', '$.obj.keys().count()') +p res3 # >>> [2] +# STEP_END + +# REMOVE_START +assert_equal(%w[x y], res2) +assert_equal([2], res3) +r.del('doc') +# REMOVE_END + +# STEP_START func_match_search +res1 = r.json_set('doc', '$', { 'a' => ['abc', 'xabc', 'a', 'b'] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?match(@, "a.*")]') +p res2 # >>> ["abc", "a"] + +res3 = r.json_set('doc', '$', { 'a' => ['abc', 'xyz', 'b'] }) +puts res3 # >>> OK + +res4 = r.json_get('doc', '$.a[?search(@, "b")]') +p res4 # >>> ["abc", "b"] +# STEP_END + +# REMOVE_START +assert_equal(%w[abc a], res2) +assert_equal(%w[abc b], res4) +r.del('doc') +# REMOVE_END + +# STEP_START func_concat +res1 = r.json_set('doc', '$', { 'a' => [{ 'x' => 'a', 'y' => 'b' }, { 'x' => 'a', 'y' => 'c' }] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?concat(@.x, @.y) == "ab"]') +p res2 # >>> [{"x"=>"a", "y"=>"b"}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'x' => 'a', 'y' => 'b' }], res2) +r.del('doc') +# REMOVE_END + +# STEP_START func_math +res1 = r.json_set('doc', '$', { 'a' => [2.1, 3.9, 1.0] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?ceiling(@) == 3]') +p res2 # >>> [2.1] + +res3 = r.json_set('doc', '$', { 'a' => [2.1, 2.9, 3.5] }) +puts res3 # >>> OK + +res4 = r.json_get('doc', '$.a[?floor(@) == 2]') +p res4 # >>> [2.1, 2.9] + +res5 = r.json_set('doc', '$', { 'a' => [{ 'n' => -5 }, { 'n' => 5 }, { 'n' => -3 }] }) +puts res5 # >>> OK + +res6 = r.json_get('doc', '$.a[?abs(@.n) == 5]') +p res6 # >>> [{"n"=>-5}, {"n"=>5}] +# STEP_END + +# REMOVE_START +assert_equal([2.1], res2) +assert_equal([2.1, 2.9], res4) +assert_equal([{ 'n' => -5 }, { 'n' => 5 }], res6) +r.del('doc') +# REMOVE_END + +# STEP_START func_array_access +res1 = r.json_set('doc', '$', { 'a' => [{ 'n' => [1, 2] }, { 'n' => [9, 8] }] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?first(@.n) == 1]') +p res2 # >>> [{"n"=>[1, 2]}] + +res3 = r.json_get('doc', '$.a[?last(@.n) == 8]') +p res3 # >>> [{"n"=>[9, 8]}] + +res4 = r.json_get('doc', '$.a[?index(@.n, -1) == 2]') +p res4 # >>> [{"n"=>[1, 2]}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'n' => [1, 2] }], res2) +assert_equal([{ 'n' => [9, 8] }], res3) +assert_equal([{ 'n' => [1, 2] }], res4) +r.del('doc') +# REMOVE_END + +# STEP_START func_aggregate +res1 = r.json_set('doc', '$', { 'a' => [{ 'n' => [3, 1, 2] }, { 'n' => [5, 6] }] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.a[?sum(@.n) == 6]') +p res2 # >>> [{"n"=>[3, 1, 2]}] + +res3 = r.json_get('doc', '$.a[?avg(@.n) == 2]') +p res3 # >>> [{"n"=>[3, 1, 2]}] +# STEP_END + +# REMOVE_START +assert_equal([{ 'n' => [3, 1, 2] }], res2) +assert_equal([{ 'n' => [3, 1, 2] }], res3) +r.del('doc') +# REMOVE_END + +# STEP_START func_append +res1 = r.json_set('doc', '$', { 'arr' => [1, 2, 3] }) +puts res1 # >>> OK + +res2 = r.json_get('doc', '$.arr.append(9)') +p res2 # >>> [1, 2, 3, 9] + +res3 = r.json_set('doc', '$', { 'books' => [{ 't' => 'a', 'price' => 30 }, { 't' => 'b', 'price' => 5 }] }) +puts res3 # >>> OK + +res4 = r.json_get('doc', '$.books[?(@.price >= 10)].append({"t":"X"})') +p res4 # >>> [{"t"=>"a", "price"=>30}, {"t"=>"X"}] +# STEP_END + +# REMOVE_START +assert_equal([1, 2, 3, 9], res2) +assert_equal([{ 't' => 'a', 'price' => 30 }, { 't' => 'X' }], 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 index 92a637ab23..0a9ae19aa3 100644 --- a/local_examples/json_path_ops/rust-async/json_path_ops.rs +++ b/local_examples/json_path_ops/rust-async/json_path_ops.rs @@ -449,5 +449,449 @@ mod json_path_ops_tests { // REMOVE_END // STEP_END + // STEP_START func_length + let _: bool = match r.json_set("doc", "$", &json!({"a":[[1,2,3],[1],"abcd","x"]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?length(@) > 2]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [[1,2,3],"abcd"] + // REMOVE_START + assert_eq!(res1, r#"[[1,2,3],"abcd"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START func_count + let _: bool = match r.json_set("doc", "$", &json!([{"a":1,"b":2,"c":3},{"a":1}])).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$[?count(@.*) == 3]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [{"a":1,"b":2,"c":3}] + // REMOVE_START + assert_eq!(res1, r#"[{"a":1,"b":2,"c":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 func_value + let _: bool = match r.json_set("doc", "$", &json!([{"a":1},{"a":2}])).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$[?value(@.a) == 1]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [{"a":1}] + // REMOVE_START + assert_eq!(res1, r#"[{"a":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 func_keys + let _: bool = match r.json_set("doc", "$", &json!({"obj":{"x":1,"y":2}})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.obj.keys()").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> ["x","y"] + // REMOVE_START + assert_eq!(res1, r#"["x","y"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.obj.keys().count()").await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [2] + // REMOVE_START + assert_eq!(res2, "[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 func_match_search + let _: bool = match r.json_set("doc", "$", &json!({"a":["abc","xabc","a","b"]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", r#"$.a[?match(@, "a.*")]"#).await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> ["abc","a"] + // REMOVE_START + assert_eq!(res1, r#"["abc","a"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set("doc", "$", &json!({"a":["abc","xyz","b"]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", r#"$.a[?search(@, "b")]"#).await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> ["abc","b"] + // REMOVE_START + assert_eq!(res2, r#"["abc","b"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START func_concat + let _: bool = match r.json_set( + "doc", + "$", + &json!({"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", r#"$.a[?concat(@.x, @.y) == "ab"]"#).await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [{"x":"a","y":"b"}] + // REMOVE_START + assert_eq!(res1, r#"[{"x":"a","y":"b"}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc").await; + // REMOVE_END + // STEP_END + + // STEP_START func_math + let _: bool = match r.json_set("doc", "$", &json!({"a":[2.1,3.9,1.0]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?ceiling(@) == 3]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [2.1] + // REMOVE_START + assert_eq!(res1, "[2.1]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set("doc", "$", &json!({"a":[2.1,2.9,3.5]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?floor(@) == 2]").await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [2.1,2.9] + // REMOVE_START + assert_eq!(res2, "[2.1,2.9]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set( + "doc", + "$", + &json!({"a":[{"n":-5},{"n":5},{"n":-3}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?abs(@.n) == 5]").await { + Ok(res3) => { + let res3: String = res3; + println!("{res3}"); // >>> [{"n":-5},{"n":5}] + // REMOVE_START + assert_eq!(res3, r#"[{"n":-5},{"n":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 func_array_access + let _: bool = match r.json_set( + "doc", + "$", + &json!({"a":[{"n":[1,2]},{"n":[9,8]}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?first(@.n) == 1]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [{"n":[1,2]}] + // REMOVE_START + assert_eq!(res1, r#"[{"n":[1,2]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?last(@.n) == 8]").await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [{"n":[9,8]}] + // REMOVE_START + assert_eq!(res2, r#"[{"n":[9,8]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?index(@.n, -1) == 2]").await { + Ok(res3) => { + let res3: String = res3; + println!("{res3}"); // >>> [{"n":[1,2]}] + // REMOVE_START + assert_eq!(res3, r#"[{"n":[1,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 func_aggregate + let _: bool = match r.json_set( + "doc", + "$", + &json!({"a":[{"n":[3,1,2]},{"n":[5,6]}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.a[?sum(@.n) == 6]").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [{"n":[3,1,2]}] + // REMOVE_START + assert_eq!(res1, r#"[{"n":[3,1,2]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?avg(@.n) == 2]").await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [{"n":[3,1,2]}] + // REMOVE_START + assert_eq!(res2, r#"[{"n":[3,1,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 func_append + let _: bool = match r.json_set("doc", "$", &json!({"arr":[1,2,3]})).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", "$.arr.append(9)").await { + Ok(res1) => { + let res1: String = res1; + println!("{res1}"); // >>> [1,2,3,9] + // REMOVE_START + assert_eq!(res1, "[1,2,3,9]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + let _: bool = match r.json_set( + "doc", + "$", + &json!({"books":[{"t":"a","price":30},{"t":"b","price":5}]}), + ).await { + Ok(v) => v, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + }; + + match r.json_get("doc", r#"$.books[?(@.price >= 10)].append({"t":"X"})"#).await { + Ok(res2) => { + let res2: String = res2; + println!("{res2}"); // >>> [{"t":"a","price":30},{"t":"X"}] + // REMOVE_START + assert_eq!(res2, r#"[{"t":"a","price":30},{"t":"X"}]"#); + // 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 index b9e38ebdef..d1bded0e14 100644 --- a/local_examples/json_path_ops/rust-sync/json_path_ops.rs +++ b/local_examples/json_path_ops/rust-sync/json_path_ops.rs @@ -509,5 +509,513 @@ mod json_path_ops_tests { // REMOVE_END // STEP_END + // STEP_START func_length + match r.json_set("doc", "$", &json!({"a":[[1,2,3],[1],"abcd","x"]})) { + Ok(res32) => { + let res32: bool = res32; + println!("{}", if res32 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res32); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?length(@) > 2]") { + Ok(res33) => { + let res33: String = res33; + println!("{res33}"); // >>> [[1,2,3],"abcd"] + // REMOVE_START + assert_eq!(res33, r#"[[1,2,3],"abcd"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_count + match r.json_set("doc", "$", &json!([{"a":1,"b":2,"c":3},{"a":1}])) { + Ok(res34) => { + let res34: bool = res34; + println!("{}", if res34 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res34); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?count(@.*) == 3]") { + Ok(res35) => { + let res35: String = res35; + println!("{res35}"); // >>> [{"a":1,"b":2,"c":3}] + // REMOVE_START + assert_eq!(res35, r#"[{"a":1,"b":2,"c":3}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_value + match r.json_set("doc", "$", &json!([{"a":1},{"a":2}])) { + Ok(res36) => { + let res36: bool = res36; + println!("{}", if res36 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res36); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$[?value(@.a) == 1]") { + Ok(res37) => { + let res37: String = res37; + println!("{res37}"); // >>> [{"a":1}] + // REMOVE_START + assert_eq!(res37, r#"[{"a":1}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_keys + match r.json_set("doc", "$", &json!({"obj":{"x":1,"y":2}})) { + Ok(res38) => { + let res38: bool = res38; + println!("{}", if res38 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res38); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.obj.keys()") { + Ok(res39) => { + let res39: String = res39; + println!("{res39}"); // >>> ["x","y"] + // REMOVE_START + assert_eq!(res39, r#"["x","y"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.obj.keys().count()") { + Ok(res40) => { + let res40: String = res40; + println!("{res40}"); // >>> [2] + // REMOVE_START + assert_eq!(res40, "[2]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_match_search + match r.json_set("doc", "$", &json!({"a":["abc","xabc","a","b"]})) { + Ok(res41) => { + let res41: bool = res41; + println!("{}", if res41 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res41); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", r#"$.a[?match(@, "a.*")]"#) { + Ok(res42) => { + let res42: String = res42; + println!("{res42}"); // >>> ["abc","a"] + // REMOVE_START + assert_eq!(res42, r#"["abc","a"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"a":["abc","xyz","b"]})) { + Ok(res43) => { + let res43: bool = res43; + println!("{}", if res43 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res43); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", r#"$.a[?search(@, "b")]"#) { + Ok(res44) => { + let res44: String = res44; + println!("{res44}"); // >>> ["abc","b"] + // REMOVE_START + assert_eq!(res44, r#"["abc","b"]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_concat + match r.json_set("doc", "$", &json!({"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]})) { + Ok(res45) => { + let res45: bool = res45; + println!("{}", if res45 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res45); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", r#"$.a[?concat(@.x, @.y) == "ab"]"#) { + Ok(res46) => { + let res46: String = res46; + println!("{res46}"); // >>> [{"x":"a","y":"b"}] + // REMOVE_START + assert_eq!(res46, r#"[{"x":"a","y":"b"}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_math + match r.json_set("doc", "$", &json!({"a":[2.1,3.9,1.0]})) { + Ok(res47) => { + let res47: bool = res47; + println!("{}", if res47 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res47); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?ceiling(@) == 3]") { + Ok(res48) => { + let res48: String = res48; + println!("{res48}"); // >>> [2.1] + // REMOVE_START + assert_eq!(res48, "[2.1]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"a":[2.1,2.9,3.5]})) { + Ok(res49) => { + let res49: bool = res49; + println!("{}", if res49 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res49); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?floor(@) == 2]") { + Ok(res50) => { + let res50: String = res50; + println!("{res50}"); // >>> [2.1,2.9] + // REMOVE_START + assert_eq!(res50, "[2.1,2.9]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"a":[{"n":-5},{"n":5},{"n":-3}]})) { + Ok(res51) => { + let res51: bool = res51; + println!("{}", if res51 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res51); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?abs(@.n) == 5]") { + Ok(res52) => { + let res52: String = res52; + println!("{res52}"); // >>> [{"n":-5},{"n":5}] + // REMOVE_START + assert_eq!(res52, r#"[{"n":-5},{"n":5}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_array_access + match r.json_set("doc", "$", &json!({"a":[{"n":[1,2]},{"n":[9,8]}]})) { + Ok(res53) => { + let res53: bool = res53; + println!("{}", if res53 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res53); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?first(@.n) == 1]") { + Ok(res54) => { + let res54: String = res54; + println!("{res54}"); // >>> [{"n":[1,2]}] + // REMOVE_START + assert_eq!(res54, r#"[{"n":[1,2]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?last(@.n) == 8]") { + Ok(res55) => { + let res55: String = res55; + println!("{res55}"); // >>> [{"n":[9,8]}] + // REMOVE_START + assert_eq!(res55, r#"[{"n":[9,8]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?index(@.n, -1) == 2]") { + Ok(res56) => { + let res56: String = res56; + println!("{res56}"); // >>> [{"n":[1,2]}] + // REMOVE_START + assert_eq!(res56, r#"[{"n":[1,2]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_aggregate + match r.json_set("doc", "$", &json!({"a":[{"n":[3,1,2]},{"n":[5,6]}]})) { + Ok(res57) => { + let res57: bool = res57; + println!("{}", if res57 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res57); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?sum(@.n) == 6]") { + Ok(res58) => { + let res58: String = res58; + println!("{res58}"); // >>> [{"n":[3,1,2]}] + // REMOVE_START + assert_eq!(res58, r#"[{"n":[3,1,2]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.a[?avg(@.n) == 2]") { + Ok(res59) => { + let res59: String = res59; + println!("{res59}"); // >>> [{"n":[3,1,2]}] + // REMOVE_START + assert_eq!(res59, r#"[{"n":[3,1,2]}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + + // STEP_START func_append + match r.json_set("doc", "$", &json!({"arr":[1,2,3]})) { + Ok(res60) => { + let res60: bool = res60; + println!("{}", if res60 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res60); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", "$.arr.append(9)") { + Ok(res61) => { + let res61: String = res61; + println!("{res61}"); // >>> [1,2,3,9] + // REMOVE_START + assert_eq!(res61, "[1,2,3,9]"); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + match r.json_set("doc", "$", &json!({"books":[{"t":"a","price":30},{"t":"b","price":5}]})) { + Ok(res62) => { + let res62: bool = res62; + println!("{}", if res62 { "OK" } else { "(nil)" }); // >>> OK + // REMOVE_START + assert!(res62); + // REMOVE_END + }, + Err(e) => { + println!("Error setting doc: {e}"); + return; + } + } + + match r.json_get("doc", r#"$.books[?(@.price >= 10)].append({"t":"X"})"#) { + Ok(res63) => { + let res63: String = res63; + println!("{res63}"); // >>> [{"t":"a","price":30},{"t":"X"}] + // REMOVE_START + assert_eq!(res63, r#"[{"t":"a","price":30},{"t":"X"}]"#); + // REMOVE_END + }, + Err(e) => { + println!("Error getting doc: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("doc"); + // REMOVE_END + // STEP_END + } } From 254193a701c658e36e4a9d2d10938a07e838f96b Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 4 Sep 2026 14:43:03 +0100 Subject: [PATCH 2/2] DOC-7032: Fold function descriptions into their Filter examples entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove "## Functions" as a separate section — move each function's description to sit directly above its runnable example under Filter examples, matching the same restructuring already applied to the filter operators. Link each function from the "Beginning with Redis 8.10..." list under JSONPath syntax instead, and fold the general prefix/postfix-form note into that same list's framing text. Co-Authored-By: Claude Sonnet 5 --- content/develop/data-types/json/path.md | 88 +++++++++---------------- 1 file changed, 31 insertions(+), 57 deletions(-) diff --git a/content/develop/data-types/json/path.md b/content/develop/data-types/json/path.md index cf0c6de1fc..78e04c9ffc 100644 --- a/content/develop/data-types/json/path.md +++ b/content/develop/data-types/json/path.md @@ -71,18 +71,18 @@ Beginning with Redis 8.10, the JSON data type supports a richer JSONPath syntax, - [`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()` -- Strings concatenation with `concat()` -- Functions on array: `first()`, `last()`, `index()`, `append()` -- Aggregation functions on array: `min()`, `max()`, `avg()`, `sum()`, `stddev()` -- Function on object: `keys()` -- Function on nodelist: `count()` -- Function on nodelist with exactly one node: `value()` +- [`length()`](#length) function on array, object, and string +- Functions on number: [`abs()`, `ceiling()`, `floor()`](#abs-ceiling-and-floor) +- Functions on string: [`match()`, `search()`](#match-and-search) +- Strings concatenation with [`concat()`](#concat) +- Functions on array: [`first()`, `last()`, `index()`](#first-last-and-index), [`append()`](#append) +- Aggregation functions on array: [`min()`, `max()`, `avg()`, `sum()`, `stddev()`](#min-max-avg-sum-and-stddev) +- Function on object: [`keys()`](#keys) +- Function on nodelist: [`count()`](#count) +- Function on nodelist with exactly one node: [`value()`](#value) - Relations functions on array and nodelist: [`subsetof()`, `anyof()`, `noneof()`](#set-relations-subsetof-anyof-and-noneof) -These operators can be used within a filter expression (`?()`). +These operators can be used within a filter expression (`?()`). 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`. {{< warning >}} Beginning with Redis 8.10, two changes to path parsing may affect existing queries: @@ -101,52 +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. -## 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`. - -### `length()` - -Returns the number of characters in a string, elements in an array, or members in an object. Any other type produces *Nothing*. - -### `count()` - -Returns the number of nodes selected by a query. An absent path counts as `0`; a single node counts as `1`. - -### `value()` - -Returns the value of a query that selects exactly one node. A query that selects zero or more than one node produces *Nothing*. - -### `keys()` - -Returns the member names of an object as a list of strings, like the `~` operator. Unlike `~`, `keys()` is composable and can be chained with other functions. - -### `match()` and `search()` - -Both test a string against a regular expression pattern ([RFC 9485](https://datatracker.ietf.org/doc/rfc9485/) I-Regexp). `match()` requires the whole string to match (anchored), while `search()` matches any substring (the same behavior as the `=~` operator). An invalid pattern produces no match. - -### `concat()` - -Concatenates its string arguments into a single string. It requires at least one argument, and any non-string argument produces *Nothing*. - -### `abs()`, `ceiling()`, and `floor()` - -Operate on a number: `abs()` returns the absolute value, `ceiling()` rounds up to the nearest integer, and `floor()` rounds down. An integer argument stays an integer and a floating-point argument stays a float. A result that overflows the signed 64-bit integer range produces *Nothing*. - -### `first()`, `last()`, and `index()` - -`first(array)` and `last(array)` return the first and last element of an array. `index(array, n)` returns the element at index `n`; a negative `n` counts from the end, a fractional `n` is truncated toward zero, and an out-of-range index produces *Nothing*. - -### `min()`, `max()`, `avg()`, `sum()`, and `stddev()` - -These aggregation functions operate on an array of numbers. `stddev()` returns the *population* standard deviation (dividing by N). The functions are strict: an array that contains any non-numeric element, or an empty array, produces *Nothing* — elements are never silently skipped. - -### `append()` - -`append(value, ...)` returns the matched array with the given value or values added after its elements. It is a read-only query-time projection and does not modify the stored document — to mutate an array in place, use the [`JSON.ARRAPPEND`]({{< relref "commands/json.arrappend/" >}}) command instead. A multiple-value argument is added as a single element (it is not spread), and a *Nothing* argument makes the whole result *Nothing*. - -See [Filter examples](#filter-examples) for a runnable example of each function. - ## JSONPath examples The following examples use this JSON document, which stores details about items in a store's inventory: @@ -424,10 +378,12 @@ OK "[]" {{< /clients-example >}} -Beginning with Redis 8.10, JSONPath also supports the functions described in [Functions](#functions). The following examples demonstrate each one. +Functions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). Beginning with Redis 8.10, JSONPath supports the following functions. #### `length()` +Returns the number of characters in a string, elements in an array, or members in an object. Any other type produces *Nothing*. + {{< clients-example set="json_path_ops" step="func_length" description="Length function: Use length() to get the number of characters in a string, elements in an array, or members in an object" difficulty="advanced" >}} > JSON.SET doc $ '{"a":[[1,2,3],[1],"abcd","x"]}' OK @@ -437,6 +393,8 @@ OK #### `count()` +Returns the number of nodes selected by a query. An absent path counts as `0`; a single node counts as `1`. + {{< clients-example set="json_path_ops" step="func_count" description="Count function: Use count() to get the number of nodes selected by a query" difficulty="advanced" >}} > JSON.SET doc $ '[{"a":1,"b":2,"c":3},{"a":1}]' OK @@ -446,6 +404,8 @@ OK #### `value()` +Returns the value of a query that selects exactly one node. A query that selects zero or more than one node produces *Nothing*. + {{< clients-example set="json_path_ops" step="func_value" description="Value function: Use value() to get the value of a query that selects exactly one node" difficulty="advanced" >}} > JSON.SET doc $ '[{"a":1},{"a":2}]' OK @@ -455,6 +415,8 @@ OK #### `keys()` +Returns the member names of an object as a list of strings, like the `~` operator. Unlike `~`, `keys()` is composable and can be chained with other functions. + {{< clients-example set="json_path_ops" step="func_keys" description="Keys function: Use keys() to get an object's member names as a composable, chainable list of strings" difficulty="advanced" >}} > JSON.SET doc $ '{"obj":{"x":1,"y":2}}' OK @@ -466,6 +428,8 @@ OK #### `match()` and `search()` +Both test a string against a regular expression pattern ([RFC 9485](https://datatracker.ietf.org/doc/rfc9485/) I-Regexp). `match()` requires the whole string to match (anchored), while `search()` matches any substring (the same behavior as the `=~` operator). An invalid pattern produces no match. + {{< clients-example set="json_path_ops" step="func_match_search" description="Regex functions: Use match() for an anchored (whole-string) regular expression match and search() for a partial match" difficulty="advanced" >}} > JSON.SET doc $ '{"a":["abc","xabc","a","b"]}' OK @@ -479,6 +443,8 @@ OK #### `concat()` +Concatenates its string arguments into a single string. It requires at least one argument, and any non-string argument produces *Nothing*. + {{< clients-example set="json_path_ops" step="func_concat" description="Concat function: Use concat() to join string arguments into a single string for comparison in a filter" difficulty="advanced" >}} > JSON.SET doc $ '{"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}' OK @@ -488,6 +454,8 @@ OK #### `abs()`, `ceiling()`, and `floor()` +Operate on a number: `abs()` returns the absolute value, `ceiling()` rounds up to the nearest integer, and `floor()` rounds down. An integer argument stays an integer and a floating-point argument stays a float. A result that overflows the signed 64-bit integer range produces *Nothing*. + {{< clients-example set="json_path_ops" step="func_math" description="Numeric functions: Use abs(), ceiling(), and floor() to transform a number before comparing it in a filter" difficulty="advanced" >}} > JSON.SET doc $ '{"a":[2.1,3.9,1.0]}' OK @@ -505,6 +473,8 @@ OK #### `first()`, `last()`, and `index()` +`first(array)` and `last(array)` return the first and last element of an array. `index(array, n)` returns the element at index `n`; a negative `n` counts from the end, a fractional `n` is truncated toward zero, and an out-of-range index produces *Nothing*. + {{< clients-example set="json_path_ops" step="func_array_access" description="Array access functions: Use first(), last(), and index() to pick a single element out of an array before comparing it" difficulty="advanced" >}} > JSON.SET doc $ '{"a":[{"n":[1,2]},{"n":[9,8]}]}' OK @@ -518,6 +488,8 @@ OK #### `min()`, `max()`, `avg()`, `sum()`, and `stddev()` +These aggregation functions operate on an array of numbers. `stddev()` returns the *population* standard deviation (dividing by N). The functions are strict: an array that contains any non-numeric element, or an empty array, produces *Nothing* — elements are never silently skipped. + {{< clients-example set="json_path_ops" step="func_aggregate" description="Aggregation functions: Use sum(), avg(), min(), max(), or stddev() to reduce an array of numbers to a single value in a filter" difficulty="advanced" >}} > JSON.SET doc $ '{"a":[{"n":[3,1,2]},{"n":[5,6]}]}' OK @@ -529,6 +501,8 @@ OK #### `append()` +`append(value, ...)` returns the matched array with the given value or values added after its elements. It is a read-only query-time projection and does not modify the stored document — to mutate an array in place, use the [`JSON.ARRAPPEND`]({{< relref "commands/json.arrappend/" >}}) command instead. A multiple-value argument is added as a single element (it is not spread), and a *Nothing* argument makes the whole result *Nothing*. + {{< clients-example set="json_path_ops" step="func_append" description="Append function: Use append() as a read-only, query-time projection that adds a value after an array's elements without modifying the stored document" difficulty="advanced" >}} > JSON.SET doc $ '{"arr":[1,2,3]}' OK