路径
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/json/path/
路径允许您访问 JSON 文档中的特定元素。由于 JSON 路径语法没有标准,Redis JSON 实现了自己的标准。JSON 的语法基于常见的实践,并有意类似于 JSONPath。
JSON 支持两种查询语法:JSONPath 语法和来自 JSON 第一版的传统路径语法。
JSON 根据路径查询的第一个字符来判断使用哪种语法。如果查询以字符 $ 开头,则使用 JSONPath 语法。否则,默认使用传统路径语法。
返回值是一个 JSON 字符串,顶层是一个 JSON 序列化字符串的数组。 如果使用了多路径,则返回值是一个 JSON 字符串,顶层是一个对象,其值是序列化 JSON 值的数组。
JSONPath 支持
RedisJSON v2.0 引入了 JSONPath 支持。它遵循 Goessner 在其文章中描述的语法。
一个 JSONPath 查询可以解析到 JSON 文档中的多个位置。在这种情况下,JSON 命令将操作应用于每个可能的位置。这是对传统路径查询的一个重大改进,传统路径查询仅作用于第一个路径。
注意,使用 JSONPath 时命令响应的结构通常会有所不同。有关更多详细信息,请参阅命令页面。
新语法支持括号表示法,允许在键名中使用冒号 ":" 或空格等特殊字符。
如果要在 CLI 的查询中包含双引号,请将 JSONPath 用单引号括起来。例如:
JSON.GET store '$.inventory["mountain_bikes"]'JSONPath 语法
以下 JSONPath 语法表改编自 Goessner 的路径语法比较。
| 语法元素 | 描述 |
|---|---|
| $ | 根(最外层的 JSON 元素),路径从此开始。 |
| . 或 [] | 选择子元素。 |
| .. | 递归遍历 JSON 文档。 |
| * | 通配符,返回所有元素。 |
| [] | 下标运算符,访问数组元素。 |
| [,] | 并集,选择多个元素。 |
| [start:end:step] | 数组切片,其中 start、end 和 step 是索引值。您可以从切片中省略值(例如 [3:]、[:8:2])以使用默认值:start 默认为第一个索引,end 默认为最后一个索引,step 默认为 1。使用 [*] 或 [:] 选择所有元素。 |
| ?() | 过滤 JSON 对象或数组。支持比较运算符 、逻辑运算符 和括号 。 |
| () | 脚本表达式。 |
| @ | 当前元素,用于过滤或脚本表达式。 |
JSONPath 示例
以下 JSONPath 示例使用此 JSON 文档,该文档存储了商店库存中商品的详细信息。为了简洁,此处省略完整 JSON,但您可以在实际文档中查看完整结构。
首先,在数据库中创建 JSON 文档:
准备:创建一个包含嵌套对象和数组的复杂 JSON 文档,用于 JSONPath 示例
命令: JSON.SET
复杂度: O(M+N)
Java 示例:
Java(异步 - Lettuce)
String inventory_json_str = "{" + " \"inventory\": {" + " \"mountain_bikes\": [" + " {"
+ " \"id\": \"bike:1\"," + " \"model\": \"Phoebe\","
+ " \"description\": \"This is a mid-travel trail slayer that is a "
+ "fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset "
+ "gives plenty of gear range to tackle hills and there\u2019s room for mudguards "
+ "and a rack too. This is the bike for the rider who wants trail manners with " + "low fuss ownership.\","
+ " \"price\": 1920,"
+ " \"specs\": {\"material\": \"carbon\", \"weight\": 13.1},"
+ " \"colors\": [\"black\", \"silver\"]" + " }," + " {"
+ " \"id\": \"bike:2\"," + " \"model\": \"Quaoar\","
+ " \"description\": \"Redesigned for the 2020 model year, this "
+ "bike impressed our testers and is the best all-around trail bike we've ever "
+ "tested. The Shimano gear system effectively does away with an external cassette, "
+ "so is super low maintenance in terms of wear and tear. All in all it's an "
+ "impressive package for the price, making it very competitive.\"," + " \"price\": 2072,"
+ " \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9},"
+ " \"colors\": [\"black\", \"white\"]" + " }," + " {"
+ " \"id\": \"bike:3\"," + " \"model\": \"Weywot\","
+ " \"description\": \"This bike gives kids aged six years and older "
+ "a durable and uberlight mountain bike for their first experience on tracks and easy "
+ "cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes "
+ "provide ample stopping ability. If you're after a budget option, this is one of the "
+ "best bikes you could get.\"," + " \"price\": 3264,"
+ " \"specs\": {\"material\": \"alloy\", \"weight\": 13.8}" + " }" + " ],"
+ " \"commuter_bikes\": [" + " {" + " \"id\": \"bike:4\","
+ " \"model\": \"Salacia\","
+ " \"description\": \"This bike is a great option for anyone who just "
+ "wants a bike to get about on With a slick-shifting Claris gears from Shimano\u2019s, "
+ "this is a bike which doesn\u2019t break the bank and delivers craved performance. "
+ "It\u2019s for the rider who wants both efficiency and capability.\","
+ " \"price\": 1475,"
+ " \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6},"
+ " \"colors\": [\"black\", \"silver\"]" + " }," + " {"
+ " \"id\": \"bike:5\"," + " \"model\": \"Mimas\","
+ " \"description\": \"A real joy to ride, this bike got very high scores "
+ "in last years Bike of the year report. The carefully crafted 50-34 tooth chainset "
+ "and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the "
+ "high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step "
+ "frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb "
+ "throttle. Put it all together and you get a bike that helps redefine what can be "
+ "done for this price.\"," + " \"price\": 3941,"
+ " \"specs\": {\"material\": \"alloy\", \"weight\": 11.6}" + " }" + " ]"
+ " }" + "}";
Charset charset = Charset.forName("UTF-8");
ByteBuffer inventory_json = charset.encode(inventory_json_str);
CompletableFuture<Void> setBikes = asyncCommands
.jsonSet("bikes:inventory", JsonPath.ROOT_PATH, parser.loadJsonValue(inventory_json))
.thenAccept(System.out::println) // >>> OK
.toCompletableFuture();Java(响应式 - Lettuce)
String inventory_json_str = "{" + " \"inventory\": {" + " \"mountain_bikes\": [" + " {"
+ " \"id\": \"bike:1\"," + " \"model\": \"Phoebe\","
+ " \"description\": \"This is a mid-travel trail slayer that is a "
+ "fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset "
+ "gives plenty of gear range to tackle hills and there\u2019s room for mudguards "
+ "and a rack too. This is the bike for the rider who wants trail manners with " + "low fuss ownership.\","
+ " \"price\": 1920,"
+ " \"specs\": {\"material\": \"carbon\", \"weight\": 13.1},"
+ " \"colors\": [\"black\", \"silver\"]" + " }," + " {"
+ " \"id\": \"bike:2\"," + " \"model\": \"Quaoar\","
+ " \"description\": \"Redesigned for the 2020 model year, this "
+ "bike impressed our testers and is the best all-around trail bike we've ever "
+ "tested. The Shimano gear system effectively does away with an external cassette, "
+ "so is super low maintenance in terms of wear and tear. All in all it's an "
+ "impressive package for the price, making it very competitive.\"," + " \"price\": 2072,"
+ " \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9},"
+ " \"colors\": [\"black\", \"white\"]" + " }," + " {"
+ " \"id\": \"bike:3\"," + " \"model\": \"Weywot\","
+ " \"description\": \"This bike gives kids aged six years and older "
+ "a durable and uberlight mountain bike for their first experience on tracks and easy "
+ "cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes "
+ "provide ample stopping ability. If you're after a budget option, this is one of the "
+ "best bikes you could get.\"," + " \"price\": 3264,"
+ " \"specs\": {\"material\": \"alloy\", \"weight\": 13.8}" + " }" + " ],"
+ " \"commuter_bikes\": [" + " {" + " \"id\": \"bike:4\","
+ " \"model\": \"Salacia\","
+ " \"description\": \"This bike is a great option for anyone who just "
+ "wants a bike to get about on With a slick-shifting Claris gears from Shimano\u2019s, "
+ "this is a bike which doesn\u2019t break the bank and delivers craved performance. "
+ "It\u2019s for the rider who wants both efficiency and capability.\","
+ " \"price\": 1475,"
+ " \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6},"
+ " \"colors\": [\"black\", \"silver\"]" + " }," + " {"
+ " \"id\": \"bike:5\"," + " \"model\": \"Mimas\","
+ " \"description\": \"A real joy to ride, this bike got very high scores "
+ "in last years Bike of the year report. The carefully crafted 50-34 tooth chainset "
+ "and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the "
+ "high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step "
+ "frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb "
+ "throttle. Put it all together and you get a bike that helps redefine what can be "
+ "done for this price.\"," + " \"price\": 3941,"
+ " \"specs\": {\"material\": \"alloy\", \"weight\": 11.6}" + " }" + " ]"
+ " }" + "}";
Charset charset = Charset.forName("UTF-8");
ByteBuffer inventory_json = charset.encode(inventory_json_str);
Mono<Void> setBikes = reactiveCommands
.jsonSet("bikes:inventory", JsonPath.ROOT_PATH, parser.loadJsonValue(inventory_json))
.doOnNext(System.out::println) // >>> OK
.then();Java(同步 - Jedis)
String inventory_json = "{"
+ " \"inventory\": {"
+ " \"mountain_bikes\": ["
+ " {"
+ " \"id\": \"bike:1\","
+ " \"model\": \"Phoebe\","
+ " \"description\": \"This is a mid-travel trail slayer that is a "
+ "fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset "
+ "gives plenty of gear range to tackle hills and there\u2019s room for mudguards "
+ "and a rack too. This is the bike for the rider who wants trail manners with "
+ "low fuss ownership.\","
+ " \"price\": 1920,"
+ " \"specs\": {\"material\": \"carbon\", \"weight\": 13.1},"
+ " \"colors\": [\"black\", \"silver\"]"
+ " },"
+ " {"
+ " \"id\": \"bike:2\","
+ " \"model\": \"Quaoar\","
+ " \"description\": \"Redesigned for the 2020 model year, this "
+ "bike impressed our testers and is the best all-around trail bike we've ever "
+ "tested. The Shimano gear system effectively does away with an external cassette, "
+ "so is super low maintenance in terms of wear and tear. All in all it's an "
+ "impressive package for the price, making it very competitive.\","
+ " \"price\": 2072,"
+ " \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9},"
+ " \"colors\": [\"black\", \"white\"]"
+ " },"
+ " {"
+ " \"id\": \"bike:3\","
+ " \"model\": \"Weywot\","
+ " \"description\": \"This bike gives kids aged six years and older "
+ "a durable and uberlight mountain bike for their first experience on tracks and easy "
+ "cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes "
+ "provide ample stopping ability. If you're after a budget option, this is one of the "
+ "best bikes you could get.\","
+ " \"price\": 3264,"
+ " \"specs\": {\"material\": \"alloy\", \"weight\": 13.8}"
+ " }"
+ " ],"
+ " \"commuter_bikes\": ["
+ " {"
+ " \"id\": \"bike:4\","
+ " \"model\": \"Salacia\","
+ " \"description\": \"This bike is a great option for anyone who just "
+ "wants a bike to get about on With a slick-shifting Claris gears from Shimano\u2019s, "
+ "this is a bike which doesn\u2019t break the bank and delivers craved performance. "
+ "It\u2019s for the rider who wants both efficiency and capability.\","
+ " \"price\": 1475,"
+ " \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6},"
+ " \"colors\": [\"black\", \"silver\"]"
+ " },"
+ " {"
+ " \"id\": \"bike:5\","
+ " \"model\": \"Mimas\","
+ " \"description\": \"A real joy to ride, this bike got very high scores "
+ "in last years Bike of the year report. The carefully crafted 50-34 tooth chainset "
+ "and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the "
+ "high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step "
+ "frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb "
+ "throttle. Put it all together and you get a bike that helps redefine what can be "
+ "done for this price.\","
+ " \"price\": 3941,"
+ " \"specs\": {\"material\": \"alloy\", \"weight\": 11.6}"
+ " }"
+ " ]"
+ " }"
+ "}";
String res28 = jedis.jsonSet("bikes:inventory", new Path2("$"), inventory_json);
System.out.println(res28); // >>> OK访问示例
以下示例使用 JSON.GET 命令从 JSON 文档中的各种路径检索数据。
您可以使用通配符运算符 * 返回库存中所有项目的列表:
通配符查询:使用 * 运算符检索集合中的所有项目,以访问特定层级的所有元素
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> getBikes = asyncCommands.jsonGet("bikes:inventory", JsonPath.of("$.inventory.*"))
.thenAccept(System.out::println)
// >>> [[[{"id":"bike:1","model":"Phoebe",...
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> getBikes = reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$.inventory.*")).collectList()
.doOnNext(System.out::println) // >>> [[[{"id":"bike:1","model":"Phoebe",...
.then();Java(同步 - Jedis)
Object res29 = jedis.jsonGet("bikes:inventory", new Path2("$.inventory.*"));
System.out.println(res29);
// >>> [[{"specs":{"material":"carbon","weight":13.1},"price":1920, ...对于某些查询,多个路径可以产生相同的结果。例如,以下路径返回所有山地自行车的名称:
数组元素访问:使用括号表示法和数组下标访问特定数组元素,或使用 [*] 访问所有元素,以从数组中检索值
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> getMtnBikes = asyncCommands
.jsonGet("bikes:inventory", JsonPath.of("$.inventory.mountain_bikes[*].model")).thenCompose(res19 -> {
System.out.println(res19); // >>> [["Phoebe","Quaoar","Weywot"]]
return asyncCommands.jsonGet("bikes:inventory",
JsonPath.of("$.inventory[\"mountain_bikes\"][*].model"));
}).thenCompose(res20 -> {
System.out.println(res20); // >>> [["Phoebe","Quaoar","Weywot"]]
return asyncCommands.jsonGet("bikes:inventory", JsonPath.of("$..mountain_bikes[*].model"));
})
.thenAccept(System.out::println)
// >>> [["Phoebe","Quaoar","Weywot"]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> getMtnBikes = reactiveCommands
.jsonGet("bikes:inventory", JsonPath.of("$.inventory.mountain_bikes[*].model")).collectList()
.doOnNext(res19 -> {
System.out.println(res19); // >>> [["Phoebe","Quaoar","Weywot"]]
})
.flatMap(res19 -> reactiveCommands
.jsonGet("bikes:inventory", JsonPath.of("$.inventory[\"mountain_bikes\"][*].model")).collectList())
.doOnNext(res20 -> {
System.out.println(res20); // >>> [["Phoebe","Quaoar","Weywot"]]
})
.flatMap(res20 -> reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$..mountain_bikes[*].model"))
.collectList())
.doOnNext(System.out::println) // >>> [["Phoebe","Quaoar","Weywot"]]
.then();Java(同步 - Jedis)
Object res30 = jedis.jsonGet(
"bikes:inventory", new Path2("$.inventory.mountain_bikes[*].model")
);
System.out.println(res30); // >>> ["Phoebe","Quaoar","Weywot"]
Object res31 = jedis.jsonGet(
"bikes:inventory", new Path2("$.inventory[\"mountain_bikes\"][*].model")
);
System.out.println(res31); // >>> ["Phoebe","Quaoar","Weywot"]
Object res32 = jedis.jsonGet(
"bikes:inventory", new Path2("$..mountain_bikes[*].model")
);
System.out.println(res32); // >>> ["Phoebe","Quaoar","Weywot"]递归下降运算符 .. 可以从 JSON 文档的多个部分检索字段。以下示例返回所有库存项目的名称:
递归下降:使用 .. 运算符在 JSON 文档中搜索任意深度的字段,以跨多个嵌套层级查找值
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> getModels = asyncCommands.jsonGet("bikes:inventory", JsonPath.of("$..model"))
.thenAccept(System.out::println)
// >>> [["Phoebe","Quaoar","Weywot","Salacia","Mimas"]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> getModels = reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$..model")).collectList()
.doOnNext(System.out::println) // >>> [["Phoebe","Quaoar","Weywot","Salacia","Mimas"]]
.then();Java(同步 - Jedis)
Object res33 = jedis.jsonGet("bikes:inventory", new Path2("$..model"));
System.out.println(res33);
// >>> ["Phoebe","Quaoar","Weywot","Salacia","Mimas"]您可以使用数组切片从数组中选择元素范围。此示例返回前 2 辆山地自行车的名称:
数组切片:使用 [start:end] 语法选择数组元素范围,以从数组中检索子集
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> get2MtnBikes = asyncCommands
.jsonGet("bikes:inventory", JsonPath.of("$..mountain_bikes[0:2].model"))
.thenAccept(System.out::println)
// >>> [["Phoebe","Quaoar"]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> get2MtnBikes = reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$..mountain_bikes[0:2].model"))
.collectList()
.doOnNext(System.out::println) // >>> [["Phoebe","Quaoar"]]
.then();Java(同步 - Jedis)
Object res34 = jedis.jsonGet(
"bikes:inventory", new Path2("$..mountain_bikes[0:2].model")
);
System.out.println(res34); // >>> ["Phoebe","Quaoar"]过滤示例
过滤表达式 ?() 允许您基于特定条件选择 JSON 元素。您可以在这些表达式中使用比较运算符(==、!=、<、<=、>、>=,以及从 v2.4.2 开始增加的 =~)、逻辑运算符(&&、||)和括号((、))。过滤表达式可以应用于数组或对象,遍历数组中的所有元素或对象中的所有值,仅检索匹配过滤条件的元素。
过滤条件中的路径使用点表示法,可以使用 @ 表示当前数组元素或当前对象值,或使用 $ 表示顶层元素。例如,使用 @.key_name 引用嵌套值,使用 $.top_level_key_name 引用顶层值。
从 v2.4.2 版本开始,您可以使用比较运算符 =~ 将左侧字符串值路径与右侧正则表达式模式进行匹配。有关更多信息,请参见支持的正则表达式语法文档。
非字符串值不匹配。只有当左侧是字符串值的路径,且右侧是硬编码字符串或字符串值的路径时,才能匹配。
正则表达式匹配是部分匹配,意味着像 "foo" 这样的模式可以匹配 "barefoots" 这样的字符串。要使匹配精确,请使用 "^foo$" 模式。
其他 JSONPath 引擎可能在斜杠之间使用正则表达式模式(例如 /foo/),并且它们的匹配是精确的。它们可以使用诸如 /.*foo.*/ 的模式执行部分匹配。
在以下示例中,过滤器仅返回价格低于 3000 且重量低于 10 的山地自行车:
过滤表达式:使用 ?() 结合比较和逻辑运算符选择匹配特定条件的元素,以根据多个条件进行查询
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> filter1 = asyncCommands
.jsonGet("bikes:inventory", JsonPath.of("$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]"))
.thenAccept(System.out::println)
// >>> [[{"id":"bike:2","model":"Quaoar","description":...
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> filter1 = reactiveCommands
.jsonGet("bikes:inventory", JsonPath.of("$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]"))
.collectList()
.doOnNext(System.out::println) // >>> [[{"id":"bike:2","model":"Quaoar","description":...
.then();Java(同步 - Jedis)
Object res35 = jedis.jsonGet(
"bikes:inventory",
new Path2("$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]")
);
System.out.println(res35);
// >>> [{"specs":{"material":"aluminium","weight":7.9},"price":2072,...此示例过滤库存中由合金制成的自行车型号名称:
相等过滤:在过滤表达式中使用 == 运算符选择具有特定字段值的元素,以查找匹配精确条件的项目
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> filter2 = asyncCommands
.jsonGet("bikes:inventory", JsonPath.of("$..[?(@.specs.material == 'alloy')].model"))
.thenAccept(System.out::println)
// >>> [["Weywot","Mimas"]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> filter2 = reactiveCommands
.jsonGet("bikes:inventory", JsonPath.of("$..[?(@.specs.material == 'alloy')].model")).collectList()
.doOnNext(System.out::println) // >>> [["Weywot","Mimas"]]
.then();Java(同步 - Jedis)
Object res36 = jedis.jsonGet(
"bikes:inventory", new Path2("$..[?(@.specs.material == 'alloy')].model")
);
System.out.println(res36); // >>> ["Weywot","Mimas"]此示例从 v2.4.2 版本开始有效,使用正则表达式匹配仅筛选材料以 "al-" 开头的自行车。注意,由于正则表达式模式中的前缀 (?i),此匹配不区分大小写:
正则表达式过滤:在过滤表达式中使用 =~ 运算符和正则表达式匹配字符串值中的模式,以实现灵活的基于模式的过滤
命令: JSON.GET
复杂度: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> filter3 = asyncCommands
.jsonGet("bikes:inventory", JsonPath.of("$..[?(@.specs.material =~ '(?i)al')].model"))
.thenAccept(System.out::println)
// >>> [["Quaoar","Weywot","Salacia","Mimas"]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> filter3 = reactiveCommands
.jsonGet("bikes:inventory", JsonPath.of("$..[?(@.specs.material =~ '(?i)al')].model")).collectList()
.doOnNext(System.out::println) // >>> [["Quaoar","Weywot","Salacia","Mimas"]]
.then();Java(同步 - Jedis)
Object res37 = jedis.jsonGet(
"bikes:inventory", new Path2("$..[?(@.specs.material =~ '(?i)al')].model")
);
System.out.println(res37);
// >>> ["Quaoar","Weywot","Salacia","Mimas"]您也可以使用 JSON 对象本身的属性来指定正则表达式模式。例如,我们可以向每辆山地自行车添加一个名为 regex_pat 的字符串属性,其值为 "(?i)al" 以匹配材料,如上一个示例所示。然后我们可以将 regex_pat 与自行车的材料进行匹配:
动态正则表达式过滤:使用存储在 JSON 属性中的正则表达式模式过滤元素,以应用文档内部定义的模式
命令: JSON.SET, JSON.GET
复杂度: JSON.SET: O(M+N), JSON.GET: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> filter4 = asyncCommands.jsonSet("bikes:inventory",
JsonPath.of("$.inventory.mountain_bikes[0].regex_pat"), parser.createJsonValue("\"(?i)al\""))
.thenCompose(r -> {
System.out.println(r); // >>> OK
return asyncCommands.jsonSet("bikes:inventory", JsonPath.of("$.inventory.mountain_bikes[1].regex_pat"),
parser.createJsonValue("\"(?i)al\""));
}).thenCompose(r -> {
System.out.println(r); // >>> OK
return asyncCommands.jsonSet("bikes:inventory", JsonPath.of("$.inventory.mountain_bikes[2].regex_pat"),
parser.createJsonValue("\"(?i)al\""));
}).thenCompose(res22 -> {
System.out.println(res22); // >>> OK
return asyncCommands.jsonGet("bikes:inventory",
JsonPath.of("$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model"));
})
.thenAccept(System.out::println)
// >>> [["Quaoar","Weywot"]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> filter4 = reactiveCommands.jsonSet("bikes:inventory",
JsonPath.of("$.inventory.mountain_bikes[0].regex_pat"), parser.createJsonValue("\"(?i)al\""))
.doOnNext(r -> {
System.out.println(r); // >>> OK
})
.flatMap(r -> reactiveCommands.jsonSet("bikes:inventory",
JsonPath.of("$.inventory.mountain_bikes[1].regex_pat"), parser.createJsonValue("\"(?i)al\"")))
.doOnNext(r -> {
System.out.println(r); // >>> OK
})
.flatMap(r -> reactiveCommands.jsonSet("bikes:inventory",
JsonPath.of("$.inventory.mountain_bikes[2].regex_pat"), parser.createJsonValue("\"(?i)al\"")))
.doOnNext(res22 -> {
System.out.println(res22); // >>> OK
})
.flatMap(res22 -> reactiveCommands
.jsonGet("bikes:inventory",
JsonPath.of("$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model"))
.collectList())
.doOnNext(System.out::println) // >>> [["Quaoar","Weywot"]]
.then();Java(同步 - Jedis)
jedis.jsonSet(
"bikes:inventory", new Path2("$.inventory.mountain_bikes[0].regex_pat"),
"\"(?i)al\""
);
jedis.jsonSet(
"bikes:inventory", new Path2("$.inventory.mountain_bikes[1].regex_pat"),
"\"(?i)al\""
);
jedis.jsonSet(
"bikes:inventory", new Path2("$.inventory.mountain_bikes[2].regex_pat"),
"\"(?i)al\""
);
Object res38 = jedis.jsonGet(
"bikes:inventory",
new Path2("$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model")
);
System.out.println(res38); // >>> ["Quaoar","Weywot"]更新示例
当您想要更新 JSON 文档的特定部分时,也可以使用 JSONPath 查询。
例如,您可以将 JSONPath 传递给 JSON.SET 命令以更新特定字段。此示例更改所有自行车价格,先减后加:
批量更新:将 JSONPath 与 JSON.NUMINCRBY 结合使用,更新文档中的多个数值,以对多个字段应用算术运算
命令: JSON.GET, JSON.NUMINCRBY
复杂度: JSON.GET: O(N), JSON.NUMINCRBY: O(1)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> updateBikes = asyncCommands.jsonGet("bikes:inventory", JsonPath.of("$..price"))
.thenCompose(r -> {
System.out.println(r); // >>> [[1920,2072,3264,1475,3941]]
return asyncCommands.jsonNumincrby("bikes:inventory", JsonPath.of("$..price"), -100);
}).thenCompose(res23 -> {
System.out.println(res23); // >>> [1820, 1972, 3164, 1375, 3841]
return asyncCommands.jsonNumincrby("bikes:inventory", JsonPath.of("$..price"), 100);
})
.thenAccept(System.out::println)
// >>> [1920, 2072, 3264, 1475, 3941]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> updateBikes = reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$..price")).collectList()
.doOnNext(r -> {
System.out.println(r); // >>> [[1920,2072,3264,1475,3941]]
})
.flatMap(
r -> reactiveCommands.jsonNumincrby("bikes:inventory", JsonPath.of("$..price"), -100).collectList())
.doOnNext(res23 -> {
System.out.println(res23); // >>> [1820, 1972, 3164, 1375, 3841]
})
.flatMap(res23 -> reactiveCommands.jsonNumincrby("bikes:inventory", JsonPath.of("$..price"), 100)
.collectList())
.doOnNext(System.out::println) // >>> [1920, 2072, 3264, 1475, 3941]
.then();Java(同步 - Jedis)
Object res39 = jedis.jsonGet("bikes:inventory", new Path2("$..price"));
System.out.println(res39);
// >>> [1920,2072,3264,1475,3941]
Object res40 = jedis.jsonNumIncrBy("bikes:inventory", new Path2("$..price"), -100);
System.out.println(res40); // >>> [1820,1972,3164,1375,3841]
Object res41 = jedis.jsonNumIncrBy("bikes:inventory", new Path2("$..price"), 100);
System.out.println(res41); // >>> [1920,2072,3264,1475,3941]您可以使用过滤表达式仅更新匹配特定条件的 JSON 元素。以下示例将价格低于 2000 的自行车的价格设置为 1500:
条件更新:将过滤表达式与 JSON.SET 结合,仅更新匹配特定条件的元素,以实现选择性修改
命令: JSON.SET, JSON.GET
复杂度: JSON.SET: O(M+N), JSON.GET: O(N)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> updateFilters1 = asyncCommands.jsonSet("bikes:inventory",
JsonPath.of("$.inventory.*[?(@.price<2000)].price"), parser.createJsonValue("1500")).thenCompose(r -> {
System.out.println(r); // >>> OK
return asyncCommands.jsonGet("bikes:inventory", JsonPath.of("$..price"));
})
.thenAccept(System.out::println)
// >>> [[1500,2072,3264,1500,3941]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> updateFilters1 = reactiveCommands.jsonSet("bikes:inventory",
JsonPath.of("$.inventory.*[?(@.price<2000)].price"), parser.createJsonValue("1500")).doOnNext(r -> {
System.out.println(r); // >>> OK
}).flatMap(r -> reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$..price")).collectList())
.doOnNext(System.out::println) // >>> [[1500,2072,3264,1500,3941]]
.then();Java(同步 - Jedis)
jedis.jsonSet("bikes:inventory", new Path2("$.inventory.*[?(@.price<2000)].price"), 1500);
Object res42 = jedis.jsonGet("bikes:inventory", new Path2("$..price"));
System.out.println(res42); // >>> [1500,2072,3264,1500,3941]JSONPath 查询也适用于其他接受路径作为参数的 JSON 命令。例如,您可以使用 JSON.ARRAPPEND 为一组符合条件的自行车添加新的颜色选项:
带过滤器的数组更新:将 JSON.ARRAPPEND 与过滤表达式结合,向匹配条件的数组添加元素,以选择性修改集合
命令: JSON.ARRAPPEND
复杂度: O(1)
Java 示例:
Java(异步 - Lettuce)
CompletableFuture<Void> updateFilters2 = asyncCommands.jsonArrappend("bikes:inventory",
JsonPath.of("$.inventory.*[?(@.price<2000)].colors"), parser.createJsonValue("\"pink\"")).thenCompose(r -> {
System.out.println(r); // >>> [3, 3]
return asyncCommands.jsonGet("bikes:inventory", JsonPath.of("$..[*].colors"));
})
.thenAccept(System.out::println)
// >>> [[["black","silver","pink"],["black","white"],["black","silver","pink"]]]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> updateFilters2 = reactiveCommands.jsonArrappend("bikes:inventory",
JsonPath.of("$.inventory.*[?(@.price<2000)].colors"), parser.createJsonValue("\"pink\"")).collectList()
.doOnNext(r -> {
System.out.println(r); // >>> [3, 3]
}).flatMap(r -> reactiveCommands.jsonGet("bikes:inventory", JsonPath.of("$..[*].colors")).collectList())
.doOnNext(System.out::println) // >>>
// [[["black","silver","pink"],["black","white"],["black","silver","pink"]]]
.then();Java(同步 - Jedis)
List<Long> res43 = jedis.jsonArrAppendWithEscape(
"bikes:inventory", new Path2("$.inventory.*[?(@.price<2000)].colors"),
"\"pink\""
);
System.out.println(res43); // >>> [3, 3]
Object res44 = jedis.jsonGet("bikes:inventory", new Path2("$..[*].colors"));
System.out.println(res44);
// >>> [["black","silver","\"pink\""],["black","white"],["black","silver","\"pink\""]]传统路径语法
RedisJSON v1 具有以下路径实现。JSON v2 除了 JSONPath 之外,仍然支持这种传统路径。
路径始终从 Redis JSON 值的根开始。根用句点字符(.)表示。对于引用根的子级的路径,可以选择是否在路径前加上根。
Redis JSON 支持对象键访问的点表示法和括号表示法。以下路径引用 headphones,它是根下 inventory 的子级:
.inventory.headphonesinventory["headphones"]['inventory']["headphones"]
要访问数组元素,请将其索引括在一对方括号中。索引从 0 开始,0 表示数组的第一个元素,1 表示下一个元素,依此类推。您可以使用负偏移量从数组末尾开始访问元素。例如,-1 是数组中的最后一个元素,-2 是倒数第二个元素,依此类推。
JSON 键名与路径兼容性
根据定义,JSON 键可以是任何有效的 JSON 字符串。另一方面,路径传统上基于 JavaScript(和 Java)的变量命名约定。
尽管 JSON 可以存储包含任意键名的对象,但只有当这些键符合以下命名语法规则时,您才能使用传统路径访问它们:
- 名称必须以字母、美元符号(
$)或下划线(_)字符开头 - 名称可以包含字母、数字、美元符号和下划线
- 名称区分大小写
路径评估的时间复杂度
搜索(导航到)路径中元素的时间复杂度由以下因素计算:
- 子级层级 - 路径中的每一层都会增加一次额外的搜索
- 键搜索 - O(N)†,其中 N 是父对象中的键数
- 数组搜索 - O(1)
这意味着搜索路径的整体时间复杂度为 O(N*M),其中 N 是深度,M 是父对象键的数量。
† 虽然这对 N 较小的对象是可以接受的,但对于较大的对象,访问可以优化。