postgresql Postgres从多级Jsonb中删除项目

9gm1akwq  于 2023-02-22  发布在  PostgreSQL
关注(0)|答案(1)|浏览(164)

我将以下对象保存在Postgres数据库的jsonb列中。

{
  "method": "method",
  "drive": [
    {
      "name": "C_Drive",
      "key": [
        {
          "is_active": true,
          "created_at": "2023-01-01T00:00:00",
          "id": 3,
          "value": "value3"
        },
        {
          "is_active": false,
          "created_at": "2022-12-01T00:00:00",
          "id": 2,
          "value": "value2"
        },
        {
          "is_active": false,
          "created_at": "2022-11-01T00:00:00",
          "id": 1,
          "value": "value1"
        }]
    },
    {
      "name": "D_Drive",
      "key": [
        {
          "is_active": true,
          "created_at": "2023-01-01T00:00:00",
          "id": 4,
          "value": "value4"
        }
    }]
}

我想删除驱动器内的一个特定密钥。假设我想删除“id”= 3的密钥。我该怎么做?

ddrv8njm

ddrv8njm1#

回答您最初的问题:
我想查询一个驱动器的所有键,这些键是is_active = false,并且在基于created_at desc的驱动器的非活动键中排名〉1。
例如:在上面的示例中,我只希望结果中的密钥ID = 1。因为它处于非活动状态,并且created_at比C驱动器中其他非活动密钥(key2)的created_at更旧
前半部分直接转换为jsonb_path_query()中的jsonpath表达式,只剩下created_at的降序需要使用row_number()窗口函数或order byoffset 1处理,以跳过第一部分:online demo.

select k
from ( select jsonb_path_query(col1,'$.drive[*].key[*] ? (@.is_active==false)') as k
       from test_table ) as subquery
order by (k ->> 'created_at')::timestamp desc
offset 1;

编辑后,回答您的新问题:您可以首先使用custom function

CREATE OR REPLACE FUNCTION jsonb_paths (data jsonb, prefix text[] default '{}') 
  RETURNS SETOF text[] STABLE PARALLEL SAFE LANGUAGE plpgsql AS $function$
DECLARE
    key     text;
    value   jsonb;
    path    text[];
    counter integer := 0;
BEGIN
    IF jsonb_typeof(data) = 'object' THEN
        FOR key, value IN SELECT * FROM jsonb_each(data)
        LOOP
            RETURN NEXT array_append(prefix, key);
            IF jsonb_typeof(value) IN ('array', 'object') THEN
                RETURN QUERY SELECT * FROM jsonb_paths (value, array_append(prefix, key));
            END IF;
        END LOOP;
    ELSIF jsonb_typeof(data) = 'array' THEN
        FOR value IN SELECT * FROM jsonb_array_elements(data)
        LOOP
            RETURN NEXT array_append(prefix, counter::text);
            IF jsonb_typeof(value) IN ('array', 'object') THEN
                RETURN QUERY SELECT * FROM jsonb_paths (value, array_append(prefix, counter::text));
            END IF;
            counter := counter + 1;
        END LOOP;
    END IF;
END $function$;

然后根据您的条件选择一个并将其用于#-运算符:online demo

with paths(id,path_arr) as 
( select id, 
         path_arr
  from (select id, col1, jsonb_paths(col1) path_arr
        from test_table) c
  where path_arr[1]='drive'
    and path_arr[3]='key'
    and array_length(path_arr,1)=4
    and (col1 #> path_arr) @@ '$.id == 3'  )
update test_table a
set col1 = a.col1 #- b.path_arr
from paths as b
where a.id=b.id;

它非常灵活,但效率很低,尤其是对于大而复杂的jsonb值。

相关问题