ReJSON模块

JSON( http://json.org/ )作为一种非常常见的数据交换格式,在 Web领域得到了广泛的应用。Redis虽然可以存储文本和二进制数据, 但它并没有提供对JSON数据的原生支持,无法直接通过命令操作存储 在数据库中的JSON数据。用户只能先将数据取出,在客户端进行处 理,然后将处理后的数据重新传入数据库。这种做法不仅增加了编程 的复杂性,还在一定程度上带来了额外的性能损耗。

为了解决这个问题,RedisLabs公司开发出了ReJSON模块( https://github.com/RedisLabsModules/ReJSON )。这个模块通过 Redis的模块机制,在Redis之上实现了对JSON数据结构的原生支持: 有了这个模块,用户就可以直接在Redis数据库中存储、更新和获取 JSON数据,就像操作其他原生Redis数据类型一样。

本节将对ReJSON模块进行介绍,说明它的编译和载入方式、基本使用 方法,并在最后进一步介绍它的常用API。

编译和载入

ReJSON的官方文档( https://oss.redislabs.com/redisjson/#building-and-loadingthe-module )。详细地说明了在各个不同平台上编译和载入ReJSON的 方法。

具体来说,为了在MacOS上载入ReJSON模块,首先需要通过git clone 命令获取模块的源码:

$ git clone https://github.com/RedisLabsModules/rejson.git
正复制到'rejson'...
remote: Enumerating objects: 86, done.
remote: Counting objects: 100% (86/86), done.
remote: Compressing objects: 100% (85/85), done.
remote: Total 2397 (delta 40), reused 4 (delta 1), pack-reused 2311
接收对象中: 100% (2397/2397), 3.78 MiB | 1.13 MiB/s,完成.
处理delta中: 100% (1314/1314),完成.

接着进入rejson的文件夹,并输入Make命令编译源码:

$ cd rejson
$ make

最后通过MODULE LOAD命令载入编译完毕的模块即可:

redis> MODULE LOAD /Users/huangz/rejson/src/rejson.so
OK

Redis服务器在成功载入ReJSON模块之后,将在日志中打印以下输出:

48440:M 21 Apr 15:25:03.490 # <ReJSON> JSON data type for Redis v1.0.1 [encver 0]
48440:M 21 Apr 15:25:03.490 * Module 'ReJSON' loaded from /Users/huangz/rejson.so

使用示例

在详细地学习ReJSON的API之前,让我们先来看一些简单的ReJSON使用 示例。首先,以下代码展示了如何使用ReJSON去设置和获取JSON字符 串以及数字值:

# 创建JSON字符串
redis> JSON.SET module_name . '"ReJSON"'
OK
# 取值
redis> JSON.GET module_name
"\"ReJSON\""
# 查看该字符串的长度
redis> JSON.STRLEN module_name
(integer) 6
# 查看该JSON键的类型
redis> JSON.TYPE module_name
string
# 设置JSON数字值
redis> JSON.SET num . 10086
OK
# 取值
redis> JSON.GET num
"10086"
# 执行加法操作
redis> JSON.NUMINCRBY num . 1
"10087"

以下代码则展示了如何设置和获取JSON数组:

# 创建JSON数组
redis> JSON.SET arr . '["abc", "def", 123, 456, true, null]'
OK
# 取值
redis> JSON.GET arr
"[\"abc\",\"def\",123,456,true,null]"
# 获取数组在索引0上的元素
redis> JSON.GET arr [0]
"\"abc\""
# 获取数组在索引1上的元素
redis> JSON.GET arr [1]
"\"def\""
# 获取数组在索引-1上的元素
redis> JSON.GET arr [-1]
"null"

以下代码则展示了如何设置和获取JSON对象:

# 创建JSON对象
redis> JSON.SET doc . '{"name":"peter", "age": 35, "fav_foods": ["cake", "rice", "noodles"]}'
OK
# 获取对象包含的键
redis> JSON.OBJKEYS doc
1) "name"
2) "age"
3) "fav_foods"
# 取出整个对象
redis> JSON.GET doc
"{\"name\":\"peter\",\"age\":35,\"fav_foods\":[\"cake\",\"rice\",\"noodles\"]}"
# 获取指定键的值
redis> JSON.GET doc name
"\"peter\""
redis> JSON.GET doc age
"35"
redis> JSON.GET doc fav_foods
"[\"cake\",\"rice\",\"noodles\"]"

ReJSON路径

ReJSON使用树状结构存储JSON数据,并允许用户通过ReJSON自行定义 的路径(path)语法来获取JSON数据的指定部分。为此,我们需要在 学习如何使用ReJSON的API处理JSON数据之前,先学习ReJSON路径的具 体语法。

一个ReJSON值总是从树根(root)开始,而ReJSON值包含的各个元素 则是这棵树上的子节点(child)。ReJSON值的树根可以用点号.来表 示,除此之外,子节点中的对象元素可以通过诸如foo.bar或者 foo["bar"]这样的语法进行访问,而子节点中的数组元素则可以通过 诸如arr[0]、matrix[2][5]这样的语法进行访问。

举个例子,对于单个字符串值或者数字值,比如"hello"和10086,我 们只需要向ReJSON提供树根作为路径,就可以取得整个值:

redis> JSON.SET word . '"hello"' --将ReJSON键word的值的树根设置为字符串"hello"
OK
redis> JSON.GET word . --获取树根(整个值)
"\"hello\""
redis> JSON.SET number . 10086 --将ReJSON键number的值的树根设置为数字值10086
OK
redis> JSON.GET number . --获取树根(整个值)
"10086"

对于更为复杂的、包含多个层次的ReJSON值,我们就需要用到对象访 问语法和数组访问语法。比如,对于以下数组:

redis> JSON.SET arr . '[1, 2, [3, [4]]]'
OK

我们可以通过将树根.设置为路径来获取整个数组:

redis> JSON.GET arr .
"[1,2,[3,[4]]]"

但如果我们想要分别获取数组中的单个元素,就需要向ReJSON提供相 应数组元素的下标作为路径:

redis> JSON.GET arr [0]
"1"
redis> JSON.GET arr [1]
"2"
redis> JSON.GET arr [2]
"[3,[4]]"
redis> JSON.GET arr [2][0]
"3"
redis> JSON.GET arr [2][1][0]
"4"

与此类似,在访问以下对象的时候:

redis> JSON.SET doc . '{"name": "peter", "age": 35, "fav_fruits": ["apple", "banana", "cherry"]}
'
OK

我们可以通过将树根设置为路径来获取整个对象:

redis> JSON.GET doc .
"{\"name\":\"peter\",\"age\":35,\"fav_fruits\":[\"apple\",\"banana\",\"cherry\"]}"

也可以通过给定对象中的某个键来获取该键的值:

redis> JSON.GET doc "name" --获取name键的值
"\"peter\""
redis> JSON.GET doc "age" --获取age键的值
"35"
redis> JSON.GET doc "fav_fruits" --获取fav_fruits键的值(一个数组)
"[\"apple\",\"banana\",\"cherry\"]"

或者通过键加下标的复合语法来获取指定数组中的某个元素:

redis> JSON.GET doc fav_fruits[0] --获取fav_fruits数组中下标为0的元素
"\"apple\""

API简介

本节将会详细地介绍ReJSON中最常见的JSON.SET、JSON.GET、 JSON.DEL、JSON.TYPE等命令,至于ReJSON的完整API信息则可以在 ReJSON官方文档页面 https://oss.redislabs.com/rejson/commands/ 查看到。

  1. JSON.SET

    JSON.SET命令接受一个Redis键、一个路径以及一个JSON值作为参数, 并将指定路径上的JSON数据设置为给定的值:

    JSON.SET <key> <path> <json>

    在使用JSON.SET命令创建新的Redis键时,路径必须设置为树根。当路 径指定的元素已经存在时,JSON.SET命令将使用用户给定的新值去代 替已有的旧值。

    在以下代码示例中,我们先使用一条JSON.SET命令创建了数组arr,然 后使用第二条JSON.SET命令,将数组的第一个元素从1修改成了 10086:

    redis> JSON.SET arr . '[1, 2, 3, 4]' --使用树根作为路径,创建新数组
    OK
    redis> JSON.GET arr
    "[1,2,3,4]"
    redis> JSON.SET arr [0] 10086 --使用下标[0]为路径,对元素进行更新
    OK
    redis> JSON.GET arr
    "[10086,2,3,4]"

    与Redis内置的SET命令一样,JSON.SET命令也支持可选的NX选项和XX选项:

    JSON.SET <key> <path> <json> [NX | XX]

    前者只会在给定Redis键不存在的情况下进行设置,而后者则只会在给 定Redis键已经存在的情况下进行设置。

    JSON.SET命令在设置成功时返回OK作为结果,并在命令因为NX、XX选 项而导致设置失败时返回nil作为结果。

    JSON.SET命令从ReJSON 1.0.0版本开始可用,它的时间复杂度为 O(M+N),其中M为已有值的体积,而N则是新值的体积。

  2. JSON.GET

    JSON.GET命令接受一个Redis键以及任意多个路径作为参数,然后从键 中获取指定路径存储的JSON数据:

    JSON.GET <key> [path path ...]

    如果用户在执行命令时没有给定任何路径,那么命令将使用树根作为 默认路径并返回键中存储的所有JSON数据。换句话说,以下两条命令 是完全等价的:

    JSON.GET <key> .
    JSON.GET <key>

    以下是一些简单的JSON.GET命令执行示例:

    redis> JSON.SET doc . '{"name": "peter", "age": 35}'
    OK
    redis> JSON.GET doc
    "{\"name\":\"peter\",\"age\":35}"
    redis> JSON.GET doc "name"
    "\"peter\""
    redis> JSON.GET not_exists_key
    (nil)

    JSON.GET命令在执行之后将返回经过序列化格式的JSON字符串作为结果。 JSON.GET命令从ReJSON 1.0.0版本开始可用,其时间复杂度为 O(N),其中N为命令返回的值数量。

  3. JSON.DEL

    JSON.DEL命令接受一个或两个参数作为输入。当用户只给定Redis键作 为参数时,ReJSON将直接删除该键及其包含的所有JSON数据:

    JSON.DEL <key>

    以这种形式调用的JSON.DEL命令总是会返回0作为结果。

    如果用户在执行JSON.DEL命令时给定了可选的路径参数,那么ReJSON 将只删除JSON数据中路径指定的部分:

    JSON.DEL <key> [path]

    接收了两个参数的JSON.DEL命令在执行之后将返回被删除值的数量作为结果。

    以下是一些简单的JSON.DEL命令执行示例:

    redis> JSON.SET arr . '["a", "b", "c"]'
    OK
    redis> JSON.GET arr
    "[\"a\",\"b\",\"c\"]"
    redis> JSON.DEL arr [-1] --删除数组的最后一个元素
    (integer) 1
    redis> JSON.GET arr
    "[\"a\",\"b\"]"
    redis> JSON.DEL arr --删除整个数组
    (integer) 0
    redis> JSON.GET arr
    (nil)

    JSON.DEL命令将自动忽略不存在的Redis键或路径。

    JSON.DEL命令从ReJSON 1.0.0版本开始可用,它的时间复杂度为 O(N),其中N为被删除值的数量。

  4. JSON.MGET

    JSON.MGET命令接受任意多个Redis键以及一个路径作为参数,然后从 给定键中获取位于指定路径上的JSON数据:

    JSON.MGET <key> [key ...] <path>

    JSON.MGET命令在执行之后将返回多个值作为结果。如果给定的Redis 键或者路径不存在,那么JSON.MGET将在结果对应的位置返回nil。

    以下是一些简单的JSON.MGET命令执行示例:

    redis> JSON.SET k1 . '"hello"'
    OK
    redis> JSON.SET k2 . 123
    OK
    redis> JSON.SET k3 . '["a", "b", "c"]'
    OK
    redis> JSON.MGET k1 k2 k3 not_exists_key .
    1) "\"hello\""
    2) "123"
    3) "[\"a\",\"b\",\"c\"]"
    4) (nil)

    JSON.MGET命令从ReJSON 1.0.0版本开始可用,它的时间复杂度为 O(M*N),其中M为给定键的数量,而N则是被返回值的体积。

  5. JSON.TYPE

    JSON.TYPE命令接受一个键以及一个可选的路径作为参数,然后返回给 定路径上的值的类型作为结果:

    JSON.TYPE <key> [path]

    如果用户没有显式地指定路径,那么命令默认将使用树根作为路径。

    以下是一些简单的JSON.TYPE命令执行示例:

    redis> JSON.TYPE message
    string
    redis> JSON.TYPE arr
    array
    redis> JSON.TYPE doc
    object

    JSON.TYPE命令从ReJSON 1.0.0版本开始可用,它的时间复杂度为 O(1)。