【问题标题】:JSONB Array Update for Specific Element特定元素的 JSONB 数组更新
【发布时间】:2019-03-07 16:21:39
【问题描述】:

我有一个 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”且instance为“1.1.4”时,将值更新为“18”

最快的方法是什么?

【问题讨论】:

  • 您目前(缓慢)在此处进行更新的方式是什么?因此,无论其他方法是否真的更快,我们都可以以此作为参考。

标签: postgresql


【解决方案1】:

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() &lt;= 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)
$$;

【讨论】:

  • 感谢 Ancoron 非常详细的回答。不幸的是,我使用的是 Postgres 9.6,目前无法升级。
  • 没问题,我会想出一个 9.6 的版本然后... :)
猜你喜欢
  • 2016-12-24
  • 2016-11-14
  • 2016-05-26
  • 2012-04-16
  • 2018-12-14
  • 2012-03-04
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多