Python 更新Postgres中的json字段
通过json字段查询Postgres9.3真的很棒。 但是,我找不到一种正式的方法来更新json对象,为此,我使用了一个基于上一篇文章()的内部函数,该函数是用plpythonu编写的: 当我的json更新保持平坦和简单时,它工作得非常好。假设“chat”是“GO_SESSION”表中的json类型,包含{“a”:“1”,“b”:“2”},以下代码将更改'b'值,并将“chat”变为{“a”:“1”,“b”:“5”}Python 更新Postgres中的json字段,python,json,postgresql,Python,Json,Postgresql,通过json字段查询Postgres9.3真的很棒。 但是,我找不到一种正式的方法来更新json对象,为此,我使用了一个基于上一篇文章()的内部函数,该函数是用plpythonu编写的: 当我的json更新保持平坦和简单时,它工作得非常好。假设“chat”是“GO_SESSION”表中的json类型,包含{“a”:“1”,“b”:“2”},以下代码将更改'b'值,并将“chat”变为{“a”:“1”,“b”:“5”} update "GO_SESSION" set chat=json_updat
update "GO_SESSION" set chat=json_update(chat,'b','5') where id=3
问题是,当我试图分配另一个对象而不是一个简单的值时:
update "GO_SESSION" set chat=json_update(chat,'b','{"name":"steve"}') where id=3
数据库中的结果是'b'包含转义字符串,而不是真正的json对象:
{“a”:“1”,“b”:“{\“name\”:\“steve\”}”}
update "GO_SESSION" set chat=json_update(chat,'b','5') where id=3
我尝试了不同的方法来取消浏览或转储json,以保持“b”是一个对象,但找不到解决方案
谢谢解决了 上面的plpythonu函数的问题在于,无论它实际上是一个复杂的json对象,它都与作为字符串的“value”相关。解决此问题的关键是在值周围添加eval():
js[key] = eval(value)
这样,json字符串(在本例中称为“value”)将其外部的双引号“{…}”松开,并成为一个对象。No
eval
是必需的。您的问题是您没有将值解码为json对象
CREATE OR REPLACE FUNCTION json_update(data json, key text, value json)
RETURNS json AS
$BODY$
from json import loads, dumps
if key is None: return data
js = loads(data)
# you must decode 'value' with loads too:
js[key] = loads(value)
return dumps(js)
$BODY$
LANGUAGE plpythonu VOLATILE;
postgres=# SELECT json_update('{"a":1}', 'a', '{"innerkey":"innervalue"}');
json_update
-----------------------------------
{"a": {"innerkey": "innervalue"}}
(1 row)
不仅如此,使用eval
解码json
是危险和不可靠的。它是不可靠的,因为json
不是Python,它只是在很多时候计算起来有点像Python。这是不安全的,因为你永远不知道你在评估什么。在这种情况下,您基本上受到PostgreSQL的json解析器的保护:
postgres=# SELECT json_update(
postgres(# '{"a":1}',
postgres(# 'a',
postgres(# '__import__(''shutil'').rmtree(''/glad_this_is_not_just_root'')'
postgres(# );
ERROR: invalid input syntax for type json
LINE 4: '__import__(''shutil'').rmtree(''/glad_this_is_not_...
^
DETAIL: Token "__import__" is invalid.
CONTEXT: JSON data, line 1: __import__...
。。。但如果有人能够利用eval
漏洞逃过这一关,我一点也不会感到惊讶。因此,这里的教训是:对于想要plv8(可在Heroku等服务上使用的可信语言)的人,不要使用eval
。我经常需要迁移或更新json blob,直接在db上运行查询要比下载所有数据、转换数据然后发布更新快得多
CREATE EXTENSION plv8;
CREATE OR REPLACE FUNCTION json_replace_string(obj json, path text, value text, force boolean)
RETURNS json AS $$
if (value === null && !force) {
return obj;
}
var nestedRe = /(\.|\[)/;
var scrub = /]/g;
path = path.replace(scrub, '');
var pathBits = path.split(nestedRe);
var len = pathBits.length;
var layer = obj;
for (var i = 0; i < len; i += 2) {
if (layer === null || layer === undefined) return obj;
var key = pathBits[i];
if (key === '') continue;
if (i === len - 1) {
layer[key] = value;
} else {
if (force && typeof layer[key] === 'undefined') {
layer[key] = pathBits[i+1] === '.' ? {} : [];
}
layer = layer[key];
}
}
return obj;
$$ LANGUAGE plv8 IMMUTABLE;
force
参数具有两个功能—(1)用于设置null
值。如果您基于其他不存在的列动态生成值,例如blob->“不存在的值”
,则函数将输入null,您可能并不打算将该值设置为null。(2)目的是强制创建嵌套路径,如果它在您要修改的json对象中不存在。e、 g
json_replace(string('{"some_key": "some_val"}', 'other_key', 'new_val', true)
给予
您可以想象类似的函数来更新数字、删除键等。这基本上可以在postgres中的早期阶段启用类似mongo的功能,以实现快速原型化,并且随着模式的稳定,我们将内容分解为独立的列和表,以获得最佳性能 好奇地想知道…谁在编写教程来教用户创建这种疯狂的模式?这个问题是基于之前的一篇文章:可能是,但用这种方式更新json仍然值得怀疑,说得委婉一点。如果您需要更新json中存储的单个数据,那么这些数据几乎肯定是表中的实际字段。这种结构的效率在很大程度上取决于它的使用方式。例如,如果我当前的json字段被指定用于处理大量的“读取”请求,而不是以这种方式使用postgres json数据类型进行“写入”,那么就可以很好地实现这一目的。如果您所说的“读取”并不意味着“读取整个json数据,直到它从数据库中出来之前,都不知道里面有什么”,我想你会在接下来的路上遇到大惊喜。说到这里,从内部的角度来看,你可能会阅读应用程序中的整个字段,在其中进行编辑,然后更新整个字段……这就是Postgres在使用你的函数时所做的;唯一的区别是,您试图从PG内部松散地操纵json,这等于是重新发明了一个性能较差的
update
语句版本。。。。而不是,看我的答案。如果您发现自己在使用eval
,请思考“哎呀,这样做的安全方法是什么”eval()
几乎从来都不是正确的答案完美!谢谢lot@user3193043请接受答复。
json_replace(string('{"some_key": "some_val"}', 'other_key', 'new_val', true)
{"some_key": "some_val", "other_key": "new_val"}