如何在PostgreSQL中合并两个json_arrays

时间:2019-05-30 09:37:50

标签: json postgresql

我需要MERGE两个JSONB_ARRAYS

我的表格列jsonb中有类似以下内容的项目:

[
   {"fav": 1, "is_active": true, "date": "1999-00-00 11:07:05.710000"},
   {"fav": 2, "is_active": true, "date": "1998-00-00 11:07:05.710000"}
]

其中fav的值是唯一编号。

我有传入的数据,其中可能是表中也存在的相同项目,还有新项目,合并结果后必须是这样,即只需添加新项目但我需要更新的现有项目

因此合并后的结果必须如下所示:

merge:
[
   {"fav": 1, "is_active": true, "date": "1999-00-00 11:07:05.710000"},
   {"fav": 2, "is_active": true, "date": "1998-00-00 11:07:05.710000"}
]::jsonb ||

[
   {"fav": 3, "is_active": true, "date": "2019-00-00 11:07:05.710000"},
   {"fav": 1, "is_active": false, "date": "2020-00-00 11:07:05.710000"}
]::jsonb

------------------------------------------------------------------------
result:

[
   {"fav": 1, "is_active": false, "date": "2020-00-00 11:07:05.710000"},
   {"fav": 2, "is_active": true, "date": "1998-00-00 11:07:05.710000"},
   {"fav": 3, "is_active": true, "date": "2019-00-00 11:07:05.710000"}
]

如预期的那样,"fav": 1->已更新,并添加了"fav": 3-> 也许我需要json的一些修饰结构,也许还有其他东西? 也许如果我将json检索到Collection并使用对象并在所有操作之后将其保存回去会更好?

更新1

我尝试编写自定义函数:

CREATE OR REPLACE FUNCTION public.json_array_merge(data1 jsonb, merge_data jsonb)
    RETURNS jsonb
    IMMUTABLE
    LANGUAGE sql
AS $$
SELECT jsonb_agg(expression)::jsonb
FROM (
         WITH to_merge AS (
             SELECT * FROM jsonb_each(jsonb_array_elements(merge_data))
         )
         SELECT *
         FROM json_each(jsonb_array_elements(data1))
         WHERE value NOT IN (SELECT value FROM to_merge)
         UNION ALL
         SELECT * FROM to_merge
     ) expression;
$$;

但现在不起作用(

1 个答案:

答案 0 :(得分:0)

您可能想编写一个自定义函数来处理此问题。默认行为是附加每个值,因为它无法知道您希望fav是唯一的。

如果您的数据使用fav作为关键字,例如

{
    "fav1": {"date": "2020-00-00 11:07:05.710000", "is_active": false},
    "fav2": {"date": "1998-00-00 11:07:05.710000", "is_active": true},
    "fav3": {"date": "2019-00-00 11:07:05.710000", "is_active": true}
}

这将很容易管理,但是由于您使用的是数组,因此需要创建一个自定义函数来迭代并检查每个值。

编辑您将需要使用plpgsql运行一些循环,使用plv8可以更有效地实现这一点

CREATE OR REPLACE FUNCTION public.json_array_merge(
    data_new jsonb,
    data_old jsonb,
    key_val text
)
    RETURNS jsonb
AS $$
DECLARE
    ret jsonb := '[]'::jsonb;
    cur text;
    add boolean := true;

    i integer := 0;
    ic integer := jsonb_array_length(data_old);
    j integer := 0;
    jc integer := jsonb_array_length(data_new);
BEGIN
    IF ic > 0 AND jc > 0 THEN

        -- populate or replace the records that are already there
        WHILE i < ic LOOP
            cur := null;
            j   := 0;

            -- loop new array
            WHILE j < jc LOOP
                IF data_old->i->>key_val = data_new->j->>key_val THEN
                    cur := data_new->>j;
                    add := false;
                END IF;
                j := j + 1;
            END LOOP;

            -- add or replace
            IF add THEN
                ret := ret || format('[%s]', data_old->>i)::jsonb;
            ELSE
                ret := ret || format('[%s]', cur)::jsonb;
            END IF;

            add := true;
            i   := i + 1;
        END LOOP;

        -- loop through the new data again and add any values not in ret
        ic := jsonb_array_length(ret);
        j  := 0;
        WHILE j < jc LOOP
            i   := 0;
            add := true;

            WHILE i < ic LOOP
                IF ret->i->>key_val = data_new->j->>key_val THEN
                    add := false;
                END IF;

                i := i + 1;
            END LOOP;

            IF add THEN
                ret := ret || format('[%s]', data_new->>j)::jsonb;
            END IF;

            j := j + 1;
        END LOOP;
    ELSE
        ret := data_new;
    END IF;

    RETURN ret;
END
$$
LANGUAGE plpgsql IMMUTABLE;

运行此命令应该会给您想要的结果

SELECT json_array_merge(
    '[{"fav": 3, "is_active": true, "date": "2019-00-00 11:07:05.710000"},{"fav": 1, "is_active": false, "date": "2020-00-00 11:07:05.710000"}]',
    '[{"fav": 1, "is_active": true, "date": "1999-00-00 11:07:05.710000"},{"fav": 2, "is_active": true, "date": "1998-00-00 11:07:05.710000"}]',
    'fav'
)