Assumindo pelo menos o Postgres 9.5, isso fará o trabalho:
SELECT jsonb_pretty(to_jsonb(p)) AS post_row_as_json
FROM (
SELECT id, title, author_id, c.content
FROM posts p
LEFT JOIN LATERAL (
SELECT jsonb_agg(
CASE WHEN c.elem->>'type' = 'image' AND i.id IS NOT NULL
THEN elem - 'image_id' || jsonb_build_object('image', i)
ELSE c.elem END) AS content
FROM jsonb_array_elements(p.content) AS c(elem)
LEFT JOIN images i ON c.elem->>'type' = 'image'
AND i.id = (elem->>'image_id')::uuid
) c ON true
) p;
Como?
-
Desaninhar ojsonb
array, produzindo 1 linha por elemento do array:
jsonb_array_elements(p.content) AS c(elem)
-
Para cada elementoLEFT JOIN
paraimages
nas condições que
a. A chave 'tipo' tem o valor 'imagem':c.elem->>'type' = 'image'
b. O UUID emimage_id
correspondências:i.id = (elem->>'image_id')::uuid
-
Para tipos de imagem, onde uma imagem correspondente foi encontrada
c.elem->>'type' = 'image' AND i.id IS NOT NULL
remova a chave 'image_id' e adicione a linha de imagem relacionada comojsonb
valor:
elem - 'image_id' || jsonb_build_object('image', i)
Caso contrário, mantenha o elemento original.
-
Reagregar os elementos modificados em um novocontent
coluna comjsonb_agg()
.
-
IncondicionalmenteLEFT JOIN LATERAL
o resultado paraposts
e selecione todas as colunas, apenas substituap.content
com a substituição geradac.content
-
NoSELECT
externo , converta a linha inteira parajsonb
com um simplesto_jsonb()
.
Todos os
jsonb
funções estão documentadas no manual aqui.