使用 PostgreSQL 查询更新一个复杂的 JSONB 对象以删除一个 sub-sub-属性

Update a complex JSONB object to remove a sub-sub-property with a PostgreSQL query

假设我在列 my_jsonb PostgreSQL 数据库 table 中有这样的 JSONB 数据,具有以下模式:

[
  {
    "foo": {
      "bar": [
        {
          "baz": 1,
          "qux": 2
        }
      ]
    }
  },
  {
    "foo": {
      "bar": [
        {
          "baz": 3,
          "qux": 4
        },
        {
          "baz": 5,
          "qux": 6
        }
      ]
    }
  }
]

换句话说,每一列 my_jsonb 都有一个 foo 的列表,其中有一个 属性 bar 包含具有两个属性的对象数组 bazqux.

我现在认为 baz 属性 已经过时了,我想删除它以得到这个:

[
  {
    "foo": {
      "bar": [
        {
          "qux": 2
        }
      ]
    }
  },
  {
    "foo": {
      "bar": [
        {
          "qux": 4
        },
        {
          "qux": 6
        }
      ]
    }
  }
]

我可以使用脚本在每个 bar 项目中一个接一个地删除这些 baz 属性,但它会非常长。有没有一种方法可以更新复杂的 JSONB 对象以删除 sub-sub-属性 而无需使用脚本执行某些操作?

一行一行的做就可以了(我的实际只有100k行table,所以我认为每一行都迭代是安全的,我唯一想要的是一次删除一行中的所有 baz 个属性).

有可能吗?

在 json 结构僵硬的情况下,您可以通过以下方式重新创建 json 对象:

select id, jsonb_agg(new_foos order by fo)
from (
    select 
        id, fo, 
        jsonb_build_object(
            'foo', 
            jsonb_build_object(
                'bar', 
                jsonb_agg(bars- 'baz' order by bo))
        ) as new_foos
    from my_table
    cross join 
        jsonb_array_elements(json_data) 
        with ordinality as foos(foos, fo)
    cross join 
        jsonb_array_elements(foos->'foo'->'bar') 
        with ordinality as bars(bars, bo)
    group by id, fo
    ) s
group by id

请注意,我们将聚合与 with ordinality 获得的顺序功能结合使用,以保留数组元素的原始顺序。

Db<>Fiddle.