PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como juntar elementos de array jsonb no Postgres?


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?


  1. Desaninhar o jsonb array, produzindo 1 linha por elemento do array:
    jsonb_array_elements(p.content) AS c(elem)
    

  2. Para cada elemento LEFT JOIN para images nas condições que
    a. A chave 'tipo' tem o valor 'imagem':c.elem->>'type' = 'image'
    b. O UUID em image_id correspondências:i.id = (elem->>'image_id')::uuid


  3. 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 como jsonb valor:
    elem - 'image_id' || jsonb_build_object('image', i)
    

    Caso contrário, mantenha o elemento original.

  4. Reagregar os elementos modificados em um novo content coluna com jsonb_agg() .


  5. Incondicionalmente LEFT JOIN LATERAL o resultado para posts e selecione todas as colunas, apenas substitua p.content com a substituição gerada c.content

  6. No SELECT externo , converta a linha inteira para jsonb com um simples to_jsonb() .


Todos os jsonb funções estão documentadas no manual aqui.