postgresql:更新一个 jsonb 对象中的多个值

postgresql: update multiple values within one jsonb object

我最近遇到了一个关于我的 Postgresql 数据库中 JSONB 数据类型的问题。

我的专栏有一个相当复杂的结构(假设 table 称为 RATING,列名称 FOOD_VALUE - 组成它),例如,如下所示:

{
  "food": {
            "type": {
                     "id": 1,
                      "name": "good"
                    },
            "category": {
                          "id": 2,
                          "name": "Vegetables"
                       },
            "others": {
                       "descr": "It's all good"
                     }
         }
}

这是一个示例,实际可能有数百个值(当然是嵌套的)。

我想更新几个值(在这个例子中是两个 -> food.type.name + food.category.id)而不修改结构本身。

这是期望的输出:

{
  "food": {
            "type": {
                     "id": 1,
                      "name": "not bad"
                    },
            "category": {
                          "id": 1,
                          "name": "Vegetables"
                       },
            "others": {
                       "descr": "It's all good"
                     }
         }
}

使用 JSONB_SET 我只能在同一列中操作一条记录 -> RATING.FOOD_VALUE ...我知道我也可以嵌套它们,但考虑到我需要更新 40 多个值,这可能会让人难以阅读(因此难以维护)

所以这个:

UPDATE rating 
SET food_value = jsonb_set(
      jsonb_set(food_value,'{food, type, name}', '"not bad"')::jsonb
      ,'{food, category, id}','"1"'::jsonb)
WHERE ....

绝对不行..

实现此目的的另一种方法是使用 JSON_BUILD_OBJECT:

UPDATE rating 
SET food_value = food_value 
   || jsonb_build_object('food', jsonb_build_object('type', jsonb_build_object('name', 'not bad')))
   || jsonb_build_object('food', jsonb_build_object('category', jsonb_build_object('id', 1)))
   ... and so on
WHERE ....

这在维护、可读性等方面看起来更有前途。但我在这里遇到的问题是它改变了 JSON 文件格式的结构:(所以上面的查询将结果

{
  "food": {
            "type": {
                      "name": "not bad"
                    },
            "category": {
                          "id": 1
                       }
         }
}

知道如何摆脱这种困境吗?我需要找到一种方法来更新复杂 JSON 结构中的数十个字段而不修改结构本身(只是值)

create type t_json_val as (path text[], val jsonb);

create or replace function jsonb_mset(a jsonb, variadic b t_json_val[])
    returns jsonb
    immutable
    language plpgsql
as $$
-- Set multiple jsonb values at once
declare
    bb t_json_val;
begin
    foreach bb in array b loop
        a := jsonb_set(a, bb.path, bb.val);
    end loop;
    return a;
end $$;
select jsonb_pretty(jsonb_mset('{
  "food": {
            "type": {
                     "id": 1,
                      "name": "good"
                    },
            "category": {
                          "id": 2,
                          "name": "Vegetables"
                       },
            "others": {
                       "descr": "It''s all good"
                     }
         }
}',
('{food, type, name}', '"not bad"'),
('{food, category, id}', '"1"')));

┌──────────────────────────────────────┐
│             jsonb_pretty             │
├──────────────────────────────────────┤
│ {                                   ↵│
│     "food": {                       ↵│
│         "type": {                   ↵│
│             "id": 1,                ↵│
│             "name": "not bad"       ↵│
│         },                          ↵│
│         "others": {                 ↵│
│             "descr": "It's all good"↵│
│         },                          ↵│
│         "category": {               ↵│
│             "id": "1",              ↵│
│             "name": "Vegetables"    ↵│
│         }                           ↵│
│     }                               ↵│
│ }                                    │
└──────────────────────────────────────┘

demo