# PATH
访问 JSON 文档中的特定元素
路径可帮助您访问 JSON 文档中的特定元素。由于不存在 JSON 路径语法的标准,因此 RedisJSON 实现了自己的标准。RedisJSON 的语法基于常见的最佳实践,并且有意类似于 JSONPath
。
RedisJSON 支持两种查询语法: JSONPath 语法
和 RedisJSON 第一个版本的 旧路径语法
。
RedisJSON 根据路径查询的第一个字符知道使用哪种语法。如果查询以字符 开头$
,则它使用 JSONPath 语法。否则,它默认为旧路径语法。
# JSONPath 支持
RedisJSON v2.0 引入了 JSONPath
支持。它遵循 Goessner 在他的文章中描述的语法。
JSONPath 查询可以解析到 JSON 文档中的多个位置。在这种情况下,JSON 命令将操作应用于每个可能的位置。这是对仅在第一条路径上运行的 遗留路径
查询的重大改进。
请注意,使用 JSONPath 时,命令响应的结构通常会有所不同。有关更多详细信息,请参阅 命令
页面。
新语法支持括号表示法,允许在键名中使用冒号“:”或空格等特殊字符。
如果要在查询中包含双引号,请将 JSONPath 括在单引号中。例如:
JSON.GET store '$.inventory["headphones"]'
# JSONPath 语法
以下 JSONPath 语法表改编自 Goessner 的 路径语法比较
。
语法元素 | 描述 |
---|---|
$ | 根(最外层的 JSON 元素)开始路径。 |
. 或者 [] | 选择一个子元素。 |
.. | 递归地遍历 JSON 文档。 |
* | 通配符,返回所有元素。 |
[] | 下标运算符,访问数组元素。 |
[,] | 联合,选择多个元素。 |
[开始:结束:步骤] | 数组切片,其中 start、end 和 step 是索引。 |
?() | 过滤 JSON 对象或数组。支持比较运算符(==、!=、<、<=、>、>=)和逻辑运算符(&&、||)。 |
() | 脚本表达式。 |
@ | 当前元素,用于过滤器或脚本表达式。 |
# JSONPath 示例
以下 JSONPath 示例使用此 JSON 文档,该文档存储有关商店库存中商品的详细信息:
{
"inventory": {
"headphones": [
{
"id": 12345,
"name": "Noise-cancelling Bluetooth headphones",
"description": "Wireless Bluetooth headphones with noise-cancelling technology",
"wireless": true,
"connection": "Bluetooth",
"price": 99.98,
"stock": 25,
"free-shipping": false,
"colors": ["black", "silver"]
},
{
"id": 12346,
"name": "Wireless earbuds",
"description": "Wireless Bluetooth in-ear headphones",
"wireless": true,
"connection": "Bluetooth",
"price": 64.99,
"stock": 17,
"free-shipping": false,
"colors": ["black", "white"]
},
{
"id": 12347,
"name": "Mic headset",
"description": "Headset with built-in microphone",
"wireless": false,
"connection": "USB",
"price": 35.01,
"stock": 28,
"free-shipping": false
}
],
"keyboards": [
{
"id": 22345,
"name": "Wireless keyboard",
"description": "Wireless Bluetooth keyboard",
"wireless": true,
"connection": "Bluetooth",
"price": 44.99,
"stock": 23,
"free-shipping": false,
"colors": ["black", "silver"]
},
{
"id": 22346,
"name": "USB-C keyboard",
"description": "Wired USB-C keyboard",
"wireless": false,
"connection": "USB-C",
"price": 29.99,
"stock": 30,
"free-shipping": false
}
]
}
}
首先,在您的数据库中创建 JSON 文档:
JSON.SET store $ '{"inventory":{"headphones":[{"id":12345,"name":"Noise-cancelling Bluetooth headphones","description":"Wireless Bluetooth headphones with noise-cancelling technology","wireless":true,"connection":"Bluetooth","price":99.98,"stock":25,"free-shipping":false,"colors":["black","silver"]},{"id":12346,"name":"Wireless earbuds","description":"Wireless Bluetooth in-ear headphones","wireless":true,"connection":"Bluetooth","price":64.99,"stock":17,"free-shipping":false,"colors":["black","white"]},{"id":12347,"name":"Mic headset","description":"Headset with built-in microphone","wireless":false,"connection":"USB","price":35.01,"stock":28,"free-shipping":false}],"keyboards":[{"id":22345,"name":"Wireless keyboard","description":"Wireless Bluetooth keyboard","wireless":true,"connection":"Bluetooth","price":44.99,"stock":23,"free-shipping":false,"colors":["black","silver"]},{"id":22346,"name":"USB-C keyboard","description":"Wired USB-C keyboard","wireless":false,"connection":"USB-C","price":29.99,"stock":30,"free-shipping":false}]}}'
# 访问 JSON 示例
以下示例使用该 JSON.GET
命令从 JSON 文档中的各种路径检索数据。
您可以使用通配符运算符*
返回库存中所有项目的列表:
127.0.0.1:6379> JSON.GET store $.inventory.*
"[[{\"id\":12345,\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"wireless\":true,\"connection\":\"Bluetooth\",\"price\":99.98,\"stock\":25,\"free-shipping\":false,\"colors\":[\"black\",\"silver\"]},{\"id\":12346,\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"wireless\":true,\"connection\":\"Bluetooth\",\"price\":64.99,\"stock\":17,\"free-shipping\":false,\"colors\":[\"black\",\"white\"]},{\"id\":12347,\"name\":\"Mic headset\",\"description\":\"Headset with built-in microphone\",\"wireless\":false,\"connection\":\"USB\",\"price\":35.01,\"stock\":28,\"free-shipping\":false}],[{\"id\":22345,\"name\":\"Wireless keyboard\",\"description\":\"Wireless Bluetooth keyboard\",\"wireless\":true,\"connection\":\"Bluetooth\",\"price\":44.99,\"stock\":23,\"free-shipping\":false,\"colors\":[\"black\",\"silver\"]},{\"id\":22346,\"name\":\"USB-C keyboard\",\"description\":\"Wired USB-C keyboard\",\"wireless\":false,\"connection\":\"USB-C\",\"price\":29.99,\"stock\":30,\"free-shipping\":false}]]"
对于某些查询,多个路径可以产生相同的结果。例如,以下路径返回所有耳机的名称:
127.0.0.1:6379> JSON.GET store $.inventory.headphones[*].name
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\",\"Mic headset\"]"
127.0.0.1:6379> JSON.GET store '$.inventory["headphones"][*].name'
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\",\"Mic headset\"]"
127.0.0.1:6379> JSON.GET store $..headphones[*].name
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\",\"Mic headset\"]"
递归下降运算符..
可以从 JSON 文档的多个部分检索字段。以下示例返回所有库存项目的名称:
127.0.0.1:6379> JSON.GET store $..name
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\",\"Mic headset\",\"Wireless keyboard\",\"USB-C keyboard\"]"
您可以使用数组切片从数组中选择一系列元素。此示例返回前两个耳机的名称:
127.0.0.1:6379> JSON.GET store $..headphones[0:2].name
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\"]"
过滤器表达式?()
允许您根据特定条件选择 JSON 元素。您可以在这些表达式中使用比较运算符(==、!=、<、<=、>、>=)和逻辑运算符(&&、||)。
例如,这个过滤器只返回价格低于 70 的无线耳机:
127.0.0.1:6379> JSON.GET store $..headphones[?(@.price<70&&@.wireless==true)]
"[{\"id\":12346,\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"wireless\":true,\"connection\":\"Bluetooth\",\"price\":64.99,\"stock\":17,\"free-shipping\":false,\"colors\":[\"black\",\"white\"]}]"
此示例筛选支持蓝牙连接的项目名称的清单:
127.0.0.1:6379> JSON.GET store '$.inventory.*[?(@.connection=="Bluetooth")].name'
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\",\"Wireless keyboard\"]"
# 更新 JSON 示例
当您想要更新 JSON 文档的特定部分时,还可以使用 JSONPath 查询。
例如,您可以将 JSONPath 传递给 JSON.SET
命令以更新特定字段。此示例更改耳机列表中第一项的价格:
127.0.0.1:6379> JSON.GET store $..headphones[0].price
"[99.98]"
127.0.0.1:6379> JSON.SET store $..headphones[0].price 78.99
"OK"
127.0.0.1:6379> JSON.GET store $..headphones[0].price
"[78.99]"
您可以使用过滤器表达式仅更新与特定条件匹配的 JSON 元素。对于价格大于 49 的任何商品,以下示例更改free-shipping
为:true
127.0.0.1:6379> JSON.SET store $.inventory.*[?(@.price>49)].free-shipping true
"OK"
127.0.0.1:6379> JSON.GET store $.inventory.*[?(@.free-shipping==true)].name
"[\"Noise-cancelling Bluetooth headphones\",\"Wireless earbuds\"]"
JSONPath 查询也适用于接受路径作为参数的其他 JSON 命令。例如,您可以为一组耳机添加新的颜色选项 JSON.ARRAPPEND
:
127.0.0.1:6379> JSON.GET store $..headphones[0].colors
"[[\"black\",\"silver\"]]"
127.0.0.1:6379> JSON.ARRAPPEND store $..headphones[0].colors '"pink"'
1) "3"
127.0.0.1:6379> JSON.GET store $..headphones[0].colors
"[[\"black\",\"silver\",\"pink\"]]"
# 旧路径语法
RedisJSON v1 具有以下路径实现。除了 JSONPath 之外,RedisJSON v2 仍然支持这个遗留路径。
路径总是从 RedisJSON 值的根开始。根由句点字符 ( .
) 表示。对于引用根的孩子的路径,可以选择在路径前加上根。
RedisJSON 支持对象键访问的点表示法和括号表示法。以下路径引用了耳机,它是根目录下的清单的子项:
.inventory.headphones
inventory["headphones"]
['inventory']["headphones"]
要访问数组元素,请将其索引括在一对方括号内。索引是从 0 开始的,0 是数组的第一个元素,1 是下一个元素,依此类推。您可以使用负偏移量来访问从数组末尾开始的元素。例如,-1 是数组中的最后一个元素,-2 是倒数第二个元素,依此类推。
# JSON 键名和路径兼容性
根据定义,JSON 键可以是任何有效的 JSON 字符串。另一方面,路径传统上基于 JavaScript(和 Java)的变量命名约定。
尽管 RedisJSON 可以存储包含任意键名的对象,但如果它们符合以下命名语法规则,则只能使用旧路径访问这些键:
- 名称必须以字母、美元符号 (
$
) 或下划线 (_
) 字符开头 - 名称可以包含字母、数字、美元符号和下划线
- 名称区分大小写
# 路径评估的时间复杂度
在路径中搜索(导航到)元素的时间复杂度计算如下:
- 子级别 - 路径上的每个级别都会添加额外的搜索
- 键搜索 - O(N) †,其中 N 是父对象中的键数
- 数组搜索 - O(1)
这意味着搜索路径的总体时间复杂度为O(N*M),其中 N 是深度,M 是父对象键的数量。
†虽然这对于 N 较小的对象是可以接受的,但可以针对较大的对象优化访问。