我有一个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”
这样做的最快方法是什么?
如果你已经在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安装中实际工作,您需要扩展plpython3u
和jsonb_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)
...对于数组中的每个项目,我们检查过滤条件是否为null
(entry_filters is None
=匹配任何条目)或条目是否“包含”提供的示例,包括键和值(entry_filters.items() <= item.items()
)。
如果条目匹配,则使用提供的替换覆盖/添加内容。
我希望你能找到你想要的方向。
看看与JSON修改相关的PostgreSQL的当前功能,它将非常复杂(如果不复杂)并且引入了大量开销来对纯SQL执行相同操作。
如果您还没有版本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)
$$;