Конвертируйте разделенные запятыми значения jsonb в объект json, используя скрипт psql - PullRequest
0 голосов
/ 02 мая 2019

У меня есть таблица в postgresql, которая имеет два столбца:

               Table "schemaname.tablename"
 Column |       Type        | Collation | Nullable | Default
--------+-------------------+-----------+----------+---------
 _key   | character varying |           | not null |
 value  | jsonb             |           |          |
Indexes:
    "tablename_pkey" PRIMARY KEY, btree (_key)

и я хотел бы преобразовать значение вложенного свойства jsonb, которое выглядит следующим образом:

{
    "somekey": "[k1=v1, k2=v2, k3=v2]",
}

в это:

{
    "somekey":  [
        "java.util.LinkedHashMap",
        {
            "k1": "v1",
            "k2": "v2",
            "k3": "v3"
        }
    ]
}

Мне удалось разобрать разделенную запятыми строку в массив строк, но кроме необходимости все еще применять другое разбиение на '=', я действительно не знаю, как сделать фактическое ОБНОВЛЕНИЕ для всех строк таблицы и сгенерировать правильное значение jsonb для ключа "somekey".

select regexp_split_to_array(RTRIM(LTRIM(value->>'somekey','['),']'),',') from schemaname.tablename;

Есть идеи?

1 Ответ

1 голос
/ 03 мая 2019

Попробуйте это (автономные тестовые данные):

WITH tablename (_key, value) AS (
    VALUES
        ('test', '{"somekey":"[k1=v1, k2=v2, k3=v2]"}'::jsonb),
        ('second', '{"somekey":"[no one=wants to, see=me, with garbage]"}'::jsonb),
        ('third', '{"somekey":"[some,key=with a = in it''s value, some=more here]"}'::jsonb)
    )
SELECT
    tab._key,
    jsonb_insert(
        '{"somekey":["java.util.LinkedHashMap"]}', -- basic JSON structure
        '{somekey,0}', -- path to insert after
        jsonb_object( -- create a JSONB object on-the-fly from the key-value array
            array_agg(key_values) -- aggregate all key-value rows into one array
        ),
        true -- we want to insert after the matching element, not before it
    ) AS json_transformed
FROM
    tablename AS tab,
    -- the following is an implicit LATERAL join (function based on eahc row for previous table)
    regexp_matches( -- produces multiple rows
        btrim(tab.value->>'somekey', '[]'), -- as you started with
        '(\w[^=]*)=([^,]*)', -- define regular expression groups for keys and values
        'g' -- we want all key-value sets
    ) AS key_values
GROUP BY 1
;

... в результате:

  _key  |                                           json_transformed                                            
--------+-------------------------------------------------------------------------------------------------------
 second | {"somekey": ["java.util.LinkedHashMap", {"see": "me", "no one": "wants to"}]}
 third  | {"somekey": ["java.util.LinkedHashMap", {"some": "more here", "some,key": "with a = in it's value"}]}
 test   | {"somekey": ["java.util.LinkedHashMap", {"k1": "v1", "k2": "v2", "k3": "v2"}]}
(3 rows)

Я надеюсь, что встроенные комментарии объясняют, как это работает достаточно подробно.

Без необходимости агрегирования / группировки по:

Следующее не требует группировки, поскольку нам не нужна функция агрегирования array_agg, но немного менее строгие в формате ключ-значениеи легко прервет запрос из-за некоторых данных (предыдущий вариант просто отбросит некоторое значение ключа):

WITH tablename (_key, value) AS (
    VALUES
        ('test', '{"somekey":"[k1=v1, k2=v2, k3=v2]"}'::jsonb),
        ('second', '{"somekey":"[no one=wants to, see=me, with garbage]"}'::jsonb)
    )
SELECT
    _key,
    jsonb_insert(
        '{"somekey":["java.util.LinkedHashMap"]}', -- basic JSON structure
        '{somekey,0}', -- path to insert after
        jsonb_object( -- create a JSONB object on-the-fly from the key-value array
            key_values -- take the keys + values as split using the function
        ),
        true -- we want to insert after the matching element, not before it
    ) AS json_transformed
FROM
    tablename AS tab,
    -- the following is an implicit LATERAL join (function based on eahc row for previous table)
    regexp_split_to_array( -- produces an array or keys and values: [k, v, k, v, ...]
        btrim(tab.value->>'somekey', '[]'), -- as you started with
        '(=|,\s*)' -- regex to match both separators
    ) AS key_values
;

... приводит к:

  _key  |                                json_transformed                                
--------+--------------------------------------------------------------------------------
 test   | {"somekey": ["java.util.LinkedHashMap", {"k1": "v1", "k2": "v2", "k3": "v2"}]}
 second | {"somekey": ["java.util.LinkedHashMap", {"see": "me", "no one": "wants to"}]}
(2 rows)

Фид с помощьюмусор (как во «второй» строке ранее) или с символом = в значении (как в «третьей» строке ранее) может привести к следующей ошибке здесь:

ERROR:  array must have even number of elements
...