jsonb_insert
может быть тем, что вы ищете.
Из документации
jsonb_insert(target jsonb, path text[], new_value jsonb [, insert_after boolean])
Возвращает цель со вставленным new_value. Если целевой раздел, обозначенный путем, находится в массиве JSONB, new_value будет вставлено до цели или после, если insert_after равен true (по умолчанию false). Если целевой раздел, обозначенный путем, находится в объекте JSONB, new_value будет вставлено, только если целевой объект не существует. Как и в случае с ориентированными на путь операторами, отрицательные целые числа, которые появляются в пути, считаются с конца JSON массивов.
Этот запрос вставляет в массив genres
новый элемент с именем Comedy
в начале массива - проверено на PostgreSQL 10.10:
SELECT jsonb_insert('{"title": "new abc",
"genres": ["Fiction", "Thriller", "Horror"],
"published": false}','{genres,0}','"Comedy"',false);
jsonb_insert
-------------------------------------------------------------------------------------------------
{"title": "new abc", "genres": ["Comedy", "Fiction", "Thriller", "Horror"], "published": false}
(1 Zeile)
Редактировать : проверка наличия элемента внутри массива перед его вставкой - см. комментарии:
Пример данных
CREATE TEMPORARY TABLE t (f jsonb);
INSERT INTO t VALUES ('{"title": "new abc",
"genres": ["Fiction", "Thriller", "Horror"],
"published": false}');
Запрос - несуществующий элемент
SELECT jsonb_insert(f,'{genres,0}','"Comedy"',false)
FROM t
WHERE '"Comedy"' NOT IN
(SELECT * FROM jsonb_array_elements(f#>'{genres}'));
-------------------------------------------------------------------------------------------------
{"title": "new abc", "genres": ["Comedy", "Fiction", "Thriller", "Horror"], "published": false}
(1 Zeile)
Запрос - существующий элемент
SELECT jsonb_insert(f,'{genres,0}','"Fiction"',false)
FROM t
WHERE '"Fiction"' NOT IN
(SELECT * FROM jsonb_array_elements(f#>'{genres}'));
jsonb_insert
--------------
(0 Zeilen)