根据其他字段的值在 JSONB 列中插入新项目 - postgres

Insert new item in JSONB column based on value of other field - postgres

我有以下 jsonb 结构,其中有很多条目

[
   {
      "name":"test",
      "features":[
         {
            "name":"feature1",
            "granted":false
         },
         {
            "name":"feature2",
            "granted":true
         }
      ]
   }...
]

当父 name 元素的值为“test”且 feature1 granted 为“false”时,我想在 features 数组中添加一个新条目".

想法是写一个flyway脚本来迁移我的数据。 我一直在与 jsonb_insert 作斗争,但我无法弄清楚它的路径部分,因为我可能在那里有很多元素,我不能只添加给定的下标。

最终结果应该是:

[
   {
      "name":"test",
      "features":[
         {
            "name":"feature1",
            "granted":false
         },
         {
            "name":"feature2",
            "granted":true
         },
         {
            "name":"newFeature",
            "granted":false
         }
      ]
   }
]

EDIT1

到目前为止我已经尝试过:

UPDATE my_table SET modules =
    jsonb_insert(my_column, '{features, [0]}', '{"name": "newFeature", "granted": false}')
WHERE my_column ->> 'name' = 'test' AND my_column @> '{"features": [{"name":"feature1", "granted": false}]}';

语句执行但实际上没有更新。

EDIT2

我修改了查询只是为了测试

的路径
UPDATE my_table SET modules =
    jsonb_insert(my_column, '{0, features, 0}', '{"name": "newFeature", "granted": false}')
WHERE my_column ->> 'name' = 'test' AND my_column @> '{"features": [{"name":"feature1", "granted": false}]}';

然而这只会更新数组中的第一个条目,我需要更新的对象不能保证总是在这个位置

这些信息应该足以完成查询:

让我们创建模拟数据

create table a (id serial primary key , b jsonb);

insert into a (b)
values ('[
  {
    "name": "test",
    "features": [
      {
        "name": "feature1",
        "granted": false
      },
      {
        "name": "feature2",
        "granted": true
      }
    ]
  },
  {
    "name": "another-name",
    "features": [
      {
        "name": "feature1",
        "granted": false
      },
      {
        "name": "feature2",
        "granted": true
      }
    ]
  }
]');

现在使用 jsonb_array_elements 和序数分解数组以获得索引和 属性

select first_level.id, position, feature_position, feature
from (select a.id, arr.*
      from a,
           jsonb_array_elements(a.b) with ordinality arr (elem, position)
      where elem ->> 'name' = 'test') first_level,
     jsonb_array_elements(first_level.elem -> 'features') with ordinality features (feature, feature_position);

本次查询结果为:

1,1,1,"{""name"": ""feature1"", ""granted"": false}"
1,1,2,"{""name"": ""feature2"", ""granted"": true}"

这里有获取所需子元素所需的必要信息,以及查询所需的所有索引。

现在,到了最后的编辑阶段,您已经有了想要的查询:

UPDATE my_table SET modules =
    jsonb_insert(my_column, '{0, features, 0}', '{"name": "newFeature", "granted": false}')
WHERE my_column ->> 'name' = 'test' AND my_column @> '{"features": [{"name":"feature1", "granted": false}]}';

在您将使用 id 的地方,因为这些是您感兴趣的行,并且在您从查询中获得它们的索引中。所以:

UPDATE my_table SET modules =
    jsonb_insert(my_column, '{' || exploded_info.position::string || ', features, ' || exploded_info.feature_position || '}', '{"name": "newFeature", "granted": false}') from (/* previous query */) as exploded_info
WHERE exploded_info.id = my_table.id and exploded_info.feature -> 'granted' = false;

如您所见,这很容易变得非常讨厌。

我建议使用更 sql 的方法,也就是说,在 table 中而不是在 json 中具有功能,fk 将其链接到您的 table... 如果你真的需要使用 json,例如,因为域真的很复杂并且在应用程序级别定义并且非常灵活。然后我建议在应用程序代码中进行更新