Artigo original: How to update objects inside JSONB arrays with PostgreSQL

Escrito por: Leandro Cesquini Pereira

Como atualizar um valor específico em um array JSONB

Digamos que você decidiu armazenar dados em um banco de dados como json ou jsonb e descobriu que acaba de criar novos problemas para si mesmo que antes não existiam. Você não está sozinho.

O JSONB é uma ferramenta poderosa, mas tem um custo, pois você precisa adaptar a maneira como faz consultas (em inglês, queries) e manipula os dados.

Não é raro carregar todo o objeto JSONB na memória, transformá-lo usando sua linguagem de programação preferida e salvar o objeto novamente no banco de dados. Você, no entanto, acaba de criar um problema de gargalos de desempenho e desperdício de recursos.

Neste artigo, vamos ver como atualizar um valor específico de um objeto dentro de um array com uma consulta.

Versão resumida: a query final está no fim do artigo. Você pode conferir um exemplo no DB Fiddle para copiar, colar e testar por sua conta.

Suponha que você esteja implementando tela de clientes o armazenamento de contatos dinâmicos para cada cliente. Então, você tem a ideia de armazenar os contatos em uma coluna em JSONB, pois eles são dinâmicos. Assim, usar uma estrutura de dados não relacional faz sentido.

Em seguida, você cria uma tabela de clientes com uma coluna de contatos em JSONB e insere alguns dados nela:

gfE87Cd6J1Jg8NG-SoiqfVGz1J8JN8rC6Fnj

Bem fácil, não é? Como, no entanto, podemos atualizar um contato específico para um cliente específico? Como mudar o e-mail de Jimi ou o telefone de Janis, por exemplo?

Felizmente, o PostgreSQL é seu amigo e fornece a função jsonb_set:

jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean])

Dada uma coluna de jsonb, você pode definir um novo valor no caminho especificado:

NsMb3UL3fPutaYFB4ebdOGq7rFVCqjJmiJPd
Referência: funções de JSON do PostgreSQL

Os selects (seleções) acima retornarão:

[{"type": "phone", "value": "+1–202–555–0105"}, {"type": "email", "value": "jimi.hendrix@gmail.com"}]

[{"type": "email", "value": "janis.joplin@gmail.com"}]

Para alterar o e-mail de Jimi na lista de contatos, informe o caminho "1, value", que significa o segundo objeto do array (começando em 0) e a chave value (valor). Esse é o path (caminho). O mesmo se aplica para alterar o e-mail de Janis, mas seu objeto de e-mail está no índice 0.

Você deve estar pensando: eu só preciso usar jsonb_set em um statement (instrução) de atualização e pronto? Essa é a ideia, mas ainda não é o suficiente.

O problema com dados não relacionais é que eles são dinâmicos. Bem, esse é um dos motivos para se usar JSONB, mas gera um problema: veja que o objeto de e-mail de Jimi está no índice 1 e o objeto de e-mail de Janis está no índice 0 do array. Outro cliente poderia ter um array muito diferente com índices diferentes. Então, como descobrir o índice de cada tipo de contato?

A resposta é ordenar os elementos do array e obter seu índice:

KoTlVXRd73a57XhyrIfVj0QnZfqRK8KguWoZ

A query retorna 1, que é o índice do objeto email (de tipo email) dentro do array de contatos do cliente Jimi.

Agora, temos todas as peças do quebra-cabeça: sabemos como atualizar um valor em jsonb e como descobrir o índice de um objeto a ser atualizado.

O único passo que resta é a própria atualização. Juntando tudo, temos:

ysGG240RTX8t9rLeuMv4KdubaDVpzVMfjnEb

A parte mais importante dessa query é o bloco with. É um recurso poderoso, mas, para este exemplo, você pode pensar nele como um "modo de armazenar uma variável", que é o path(caminho) do contato que é necessário atualizar, o qual será dinâmico, dependendo do registro.

Deixe-me explicar um pouco sobre esta parte:

(‘{‘||index-1||’,value}’)::text[] as path

Essa parte apenas constrói path como '{1, value}', mas precisamos converter para text[], pois esse é o tipo esperado em uma função jsonb_path.

Resumindo

O JSONB é uma ferramenta excelente e valiosa para resolver muitos problemas. Tenha em mente, porém, que você também precisa consultar e atualizar esse tipo de dado. Isso traz um custo que você deve considerar ao decidir quais ferramentas escolher para usar.

Observação: essa solução surgiu de uma sessão de programação com Lucas Cegatti.

Está procurando por uma empresa criativa para implementar sua próxima ideia? Confira a LNA Systems.