如何使用 PostgreSQL JSONB_SET() 创建新的深层对象元素

use*_*645 6 postgresql jsonb postgresql-9.6

我一定是遗漏了一些东西......似乎JSONB_SET()不像宣传的那样工作?

SELECT JSONB_SET(
    '{"k1": {"value": "v1"}}',
    '{k2,value}',
    '"v2"',
    TRUE
);
Run Code Online (Sandbox Code Playgroud)

结果是:

----+------------------------
    | jsonb_set
    | jsonb
----+------------------------
  1 | {"k1": {"value": "v1"}}
----+------------------------
Run Code Online (Sandbox Code Playgroud)

我期待{"k1": {"value": "v1"}, "k2": {"value": "v2"}} 我也尝试FALSE作为第四个参数,以防它被逆转或其他什么。

我正在使用 PostgreSQL 9.6.4

小智 6

该文件说:

jsonb_set(目标 jsonb,路径文本 [],新值 jsonb[,create_missing 布尔值])

在您的示例中,第二个参数 -'{k2,value}'是搜索路径,但由于第一个路径k2不存在,因此可以添加/替换之前 为 NULL 的结果value

简单来说 - jsonb_set 并不是按照您尝试使用搜索路径的方式构建整个 JSON 文档,而是添加或替换单个键/值。

如果您想添加/替换全新的 JSON 集,您可以使用||(concatenate) 运算符代替:

-- Add example:
SELECT $${"k1": {"value": "v1"}}$$::jsonb || $${ "k2": { "value": "v2"}}$$::jsonb;
                    ?column?
------------------------------------------------
 {"k1": {"value": "v1"}, "k2": {"value": "v2"}}
(1 row)

-- Replace example
SELECT $${"k1": {"value": "v1"}}$$::jsonb || $${ "k1": { "value": "v2"}}$$::jsonb;
        ?column?
-------------------------
 {"k1": {"value": "v2"}}
(1 row) 
Run Code Online (Sandbox Code Playgroud)

或者

你可以这样使用jsonb_set()

SELECT JSONB_SET(
    '{"k1": {"value": "v1"}}',
    '{k2}',
    '{"value": "v2"}',
    TRUE
);
                   jsonb_set
------------------------------------------------
 {"k1": {"value": "v1"}, "k2": {"value": "v2"}}
(1 row)
Run Code Online (Sandbox Code Playgroud)

  • 如果除了 `k2.value` 之外还有其他值,`||` 就不会真正起作用,也就是说,假设我们想要修改嵌套对象中的值,可能存在也可能不存在。如果它存在并创建其他值,我们将使用这种方法删除它们 (2认同)

小智 5

我刚刚遇到了同样的问题并为其创建了一个简单的 postgres 函数。它可能不是最快的解决方案,对于深度插入可能特别慢,但到目前为止对我来说效果很好!

CREATE OR REPLACE FUNCTION jsonb_deep_set(curjson jsonb, globalpath text[], newval jsonb) RETURNS jsonb AS
$$
  BEGIN
    IF curjson is null THEN
      curjson := '{}'::jsonb;
    END IF;
    FOR index IN 1..ARRAY_LENGTH(globalpath, 1) LOOP
      IF curjson #> globalpath[1:index] is null THEN
        curjson := jsonb_set(curjson, globalpath[1:index], '{}');
      END IF;
    END LOOP;
    curjson := jsonb_set(curjson, globalpath, newval);
    RETURN curjson;
  END;
$$
LANGUAGE 'plpgsql';
Run Code Online (Sandbox Code Playgroud)

替换jsonbjson使其也适用于 json 对象。