特定元素的JSONB数组更新

时间:2019-03-07 16:21:39

标签: postgresql

我有一个jsonb数组,其中包含大约1000个结构为“ oid:aaa,instance:bbb,value:ccc”的元素。

{"_id": 37637070
, "data": [{"oid": "11.5.15.1.4", "value": "1", "instance": "1.1.4"}
         , {"oid": "11.5.15.1.9", "value": "17", "instance": "1.1.4"}
         , {"oid": "12.5.15.1.5", "value": "0.0.0.0", "instance": "0"}]}

我在此字段上有一个索引

CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);

搜索非常快,但是我没有找到进行更新的快速方法。我想为特定的oid和实例组合更新一个值。

例如,当oid为“ 11.5.15.1.9”且实例为“ 1.1.4”时,将值更新为“ 18”

最快的方法是什么?

1 个答案:

答案 0 :(得分:0)

PostgreSQL 11 +

如果您已经在PostgreSQL v。11中(由于new JSONB type conversion support),那么最好的选择可能是用Perl或Python编写的自定义函数。

我喜欢Python 3,下面是一个功能示例:

CREATE OR REPLACE FUNCTION jsonb_replace_in_array (val jsonb, path_to_array text[], replacement jsonb, entry_filters jsonb)
    RETURNS jsonb
    TRANSFORM FOR TYPE jsonb
    LANGUAGE plpython3u
AS $$
v_new = val
tmp = v_new
for e in path_to_array:
    tmp = tmp[e]

for item in tmp:
    if (entry_filters is None or entry_filters.items() <= item.items()):
        item.update(replacement)

return v_new
$$;

...然后可以按以下方式使用:

UPDATE configuration
SET
  config = jsonb_replace_in_array(
    config,
    '{data}',
    '{"value":"changed"}'::jsonb,
    '{"oid":"11.5.15.1.4","instance":"1.1.4"}'::jsonb
  )
WHERE config->'data' @> '[{"oid":"11.5.15.1.4","instance":"1.1.4"}]';

是的,条件是重复的,但仅是为了限制要触摸的行数。

要在纯PostgreSQL 11上实际安装,需要扩展plpython3ujsonb_plpython3u

CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;

Python逻辑说明:

for e in path_to_array:
    tmp = tmp[e]

...使我们进入需要查看的条目数组。

for item in tmp:
    if (entry_filters is None or entry_filters.items() <= item.items()):
        item.update(replacement)

...对于数组中的每个项目,我们检查过滤条件是否为nullentry_filters is None =匹配任何条目),或者该条目是否“包含”所提供的示例,包括键和值(entry_filters.items() <= item.items()

如果en条目匹配,则用提供的替换内容覆盖/添加内容。

我希望这能引导您寻找方向。

看看PostgreSQL当前与JSON修改相关的功能,这将非常复杂(如果不复杂的话),并且引入了大量的开销来使用纯SQL进行相同的操作。


PostgreSQL 9.6 +

如果您还没有版本11,则以下功能将执行相同的功能,但要自己处理类型转换,但要使其完全与API兼容,因此,一旦升级,您唯一要做的就是要做的就是替换该函数(无需更改任何使用此函数的语句):

CREATE OR REPLACE FUNCTION jsonb_replace_in_array (val jsonb, path_to_array text[], replacement jsonb, entry_filters jsonb)
    RETURNS jsonb
    LANGUAGE plpython3u
AS $$
import json

v_new = json.loads(val)
t_replace = json.loads(replacement)
t_filters = json.loads(entry_filters)
tmp = v_new
for e in path_to_array:
    tmp = tmp[e]

for item in tmp:
    if (entry_filters is None or t_filters.items() <= item.items()):
        item.update(t_replace)

return json.dumps(v_new)
$$;