[英]Updating a jsonb array value with constraints in Postgres
我有一個帶有數組的jsonb
類型的表,我正在嘗試在該數組中專門散列/匿名化遵循某些要求的電子郵件(通過 md5 或 sha1)。 使用下面的虛擬數據,我很難嘗試專門針對沒有@google.com
域或admin
的任何電子郵件的owners
數組。
{
"blah": blah,
"owners": [
"admin"
],
},
{
"blah": blah,
"owners": [
"john@google.com"
],
},
{
"blah": blah,
"owners": [
"jason@facebook.com", "fred@facebook.com"
],
},
{
"blah": blah,
"owners": [
"zuck@facebook.com", "bill@google.com"
],
}
理想情況下,最終結果應該是
{
"blah": blah,
"owners": [
"admin"
],
},
{
"blah": blah,
"owners": [
"john@google.com"
],
},
{
"blah": blah,
"owners": [
"dskfjld@hashed.com", "dshfjkdhsfjk@hashed.com"
],
},
{
"blah": blah,
"owners": [
"hashfsdflksdjf@hashed.com", "bill@google.com"
],
}
這里的邏輯可能是遍歷列中的每個owners
數組,但不確定查詢的具體外觀。
(a) 您的jsonb
數據樣本內部有一些錯誤。 對於這個答案,我將改用:
[{
"blah": "blah",
"owners": [
"admin"
]},
{
"blah": "blah",
"owners": [
"john@google.com"
]},
{
"blah": "blah",
"owners": [
"jason@facebook.com", "fred@facebook.com"
]},
{
"blah": "blah",
"owners": [
"zuck@facebook.com", "bill@google.com"
]}]
(b) 使用 postgres 在jsonb
數據中進行多次更新並不容易。 以下解決方案在使用新的 email 值更新 jsonb 數據結構時跟蹤路徑信息時分解 jsonb 數據結構。
(c) 該解決方案基於jsonb_set()
標准 function,它只能對給定的 jsonb 數據應用一次更新。 所以第一步是創建 function jsonb_set_agg()
作為基於jsonb_set
的aggregate
,它能夠在迭代一組記錄時對相同的 jsonb 數據執行多次更新:
CREATE OR REPLACE FUNCTION jsonb_set(x jsonb, y jsonb, p text[], z jsonb, c boolean)
RETURNS jsonb LANGUAGE sql AS
$$ SELECT jsonb_set(COALESCE(x,y), p, z, c) ; $$ ;
CREATE OR REPLACE AGGREGATE jsonb_set_agg(x jsonb,p text[], z jsonb, c boolean)
(SFUNC = jsonb_set, STYPE = jsonb) ;
(d) 最后,以下查詢分解 jsonb 數據( FROM
子句),過濾必須更新的電子郵件( WHERE
子句),然后根據基於ORDINALITY
的路徑,用新的 email 值更新 jsonb 數據信息:
SELECT jsonb_set_agg(j.json_data, array[(b.id - 1) :: text, 'owners', (c.id - 1) :: text], to_jsonb('insert_here_the_new_value' :: text), true)
FROM (VALUES ('[{
"blah": "blah",
"owners": [
"admin"
]},
{
"blah": "blah",
"owners": [
"john@google.com"
]},
{
"blah": "blah",
"owners": [
"jason@facebook.com", "fred@facebook.com"
]},
{
"blah": "blah",
"owners": [
"zuck@facebook.com", "bill@google.com"
]}]' :: jsonb)) AS j(json_data)
CROSS JOIN LATERAL jsonb_array_elements(j.json_data) WITH ORDINALITY AS b(json_data, id)
CROSS JOIN LATERAL jsonb_array_elements_text(b.json_data->'owners') WITH ORDINALITY AS c(json_data, id)
WHERE c.json_data <> 'admin'
AND NOT c.json_data ~ '@google.com$'
結果是:
[ {"blah": "blah", "owners": ["admin"]}
, {"blah": "blah", "owners": ["john@google.com"]}
, {"blah": "blah", "owners": ["insert_here_the_new_value","insert_here_the_new_value"]}
, {"blah": "blah", "owners": ["insert_here_the_new_value", "bill@google.com"]}
]
dbfiddle中的所有詳細信息。
您可以使用帶有json_agg
的子查詢:
select jsonb_set(t.data, '{owners}',
(select jsonb_agg(case when v.value::text = '"admin"' or v.value::text ~ 'google\.com"' then v.value::text else md5(substring(v.value::text from '^"(\w+)@'))||'@'||md5(substring(v.value::text from '@(\w+\.\w+)"$')) end)
from jsonb_array_elements(t.data -> 'owners') v))
from vals t
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.