使用 jsonb_set() 进行条件更新

Conditional update with jsonb_set()

我在 Postgres 11.3 数据库中有一个 table,其中有一个 jsonb 列。

正在尝试更新嵌套数组名称中的所有对象 "iProps"

如果路径 {iProps -> value -> rules -> ao -> sc} 是对象,则路径应从对象更新为具有值的字符串 {iProps -> value -> rules -> ao -> sc -> name}

如果路径 {iProps -> value -> rules -> ao -> sc} 不存在,则对象应保持不变。

使用查询测试设置:Fiddle link

想要的结果:

{
    "iProps": [
        {
            "value": {
                "rules": [
                    {
                        "ao": {
                            "set": "get"
                        },
                        "name": "PRule"
                    },
                    {
                        "ao": {
                            "sc":  "name1"
                            
                        }
                    },
                    {
                        "ao": {
                            "sc": "name2"
                            
                        }
                    },
                    {
                        "ao": {
                            "sc":  "name3"                            
                        }
                    }
                ]
            }
        }
    ]
}

我已经修改了 fiddle 中的查询和链接。有没有人看一下对不对?

一个简单的 CASE 应该有所作为。

UPDATE table_ t
SET    value_ = jsonb_set(value_, '{iProps}', sub2.new_prop, false)
FROM  (
   SELECT id
        , jsonb_agg(jsonb_set(prop, '{value, rules}', new_rules, false)
                    ORDER BY idx1) AS new_prop
   FROM  (
      SELECT t.id, arr1.prop, arr1.idx1
           , jsonb_agg(<b>CASE WHEN jsonb_typeof(rule #> '{ao,sc}') = 'object'
                            THEN jsonb_set(rule, '{ao,sc}', rule #> '{ao,sc,name}', false)
                            ELSE rule
                       END</b>
                       ORDER BY idx2) AS new_rules
      FROM table_ t
         , jsonb_array_elements(value_->'iProps')       WITH ORDINALITY arr1(prop,idx1)
         , jsonb_array_elements(prop->'value'->'rules') WITH ORDINALITY arr2(rule,idx2)
      GROUP  BY t.id, arr1.prop, arr1.idx1
      ) sub1
   GROUP  BY id
   ) sub2
WHERE t.id = sub2.id;

db<>fiddle here(Postgres 11!)

为了满足您在更新中添加的第二个过滤器(必须是 对象),请检查 jsonb_typeof()

your fiddle 中的查询似乎不必要地复杂 (tl;dr)。此外,它不会保留 数组元素的原始顺序 。如果这实际上无关紧要,请省略 WITH ORDINALITYORDER BY 并进一步简化:

UPDATE table_ t
SET    value_ = jsonb_set(value_, '{iProps}', sub2.new_prop, false)
FROM  (
   SELECT id
        , jsonb_agg(jsonb_set(prop, '{value, rules}', new_rules, false)) AS new_prop
   FROM  (
      SELECT t.id, prop
           , jsonb_agg(CASE WHEN jsonb_typeof(rule #> '{ao,sc}') = 'object'
                            THEN jsonb_set(rule, '{ao,sc}', rule #> '{ao,sc,name}', false)
                            ELSE rule
                       END) AS new_rules
      FROM table_ t
         , jsonb_array_elements(value_->'iProps')       prop
         , jsonb_array_elements(prop->'value'->'rules') rule
      GROUP  BY t.id, prop
      ) sub1
   GROUP  BY id
   ) sub2
WHERE t.id = sub2.id;

db<>fiddle here

这通常仍会保留数组元素的顺序(与您的原始顺序不同)。只是不能保证两级聚合。

参见:

  • PostgreSQL unnest() with element number

我对您之前的相关问题的回答中有更多建议: