Como obter um objeto específico da matriz jsonb no PostgreSQL?

15

Eu tenho um campo chamado 'usuário' que contém uma matriz json que mais ou menos se parece com isso:

"user":

[{ "_id" : "1", "count" : "4" }, { "_id" : "3", "count": "4"}]

Agora eu quero uma consulta como:

select count from tablename where id = "1"

Não consigo obter o campo específico countde uma matriz de objetos json no PostgreSQL 9.4.

Rabi C Shah
fonte

Respostas:

17

Seria muito mais eficiente armazenar seus valores em um esquema normalizado. Dito isto, você também pode fazê-lo funcionar com sua configuração atual.

Premissas

Assumindo esta definição de tabela:

CREATE TABLE tbl (tbl_id int, usr jsonb);

"usuário" é uma palavra reservada e requer que aspas duplas sejam usadas como nome da coluna. Não faça isso. Eu uso em usrvez disso.

Inquerir

A consulta não é tão trivial quanto os comentários (agora excluídos) fizeram parecer:

SELECT t.tbl_id, obj.val->>'count' AS count
FROM   tbl t
JOIN   LATERAL jsonb_array_elements(t.usr) obj(val) ON obj.val->>'_id' = '1'
WHERE  t.usr @> '[{"_id":"1"}]';

Existem 3 etapas básicas :

1. Identifique linhas qualificadas baratas

WHERE t.usr @> '[{"_id":"1"}]'identifica linhas com objeto correspondente na matriz JSON. A expressão pode usar um índice GIN genérico na jsonbcoluna ou um com a classe de operador mais especializada jsonb_path_ops:

CREATE INDEX tbl_usr_gin_idx ON tbl USING gin (usr jsonb_path_ops);

A WHEREcláusula adicionada é logicamente redundante , mas é necessária para usar o índice. A expressão na cláusula de junção impõe a mesma condição, mas somente após desinvestir a matriz em todas as linhas qualificadas até o momento. Com o suporte ao índice, o Postgres processa apenas linhas que contêm um objeto qualificado para começar. Não importa muito com tabelas pequenas, faz uma enorme diferença com tabelas grandes e com poucas linhas de qualificação.

Relacionado:

2. Identifique os objetos correspondentes na matriz

Desnaturar com jsonb_array_elements(). ( unnest()só é bom para os tipos de array do Postgres.) Como estamos interessados ​​apenas na correspondência de objetos, filtre a condição de junção imediatamente.

Relacionado:

3. Extrair valor para chave aninhada 'count'

Depois de objetos de qualificação foram extraídos, simplesmente: obj.val->>'count'.

Erwin Brandstetter
fonte
2
De onde obj(value)vem isso? Está no LATERAL JOIN, no jsonb_array_elementsou em outro lugar?
Tyler DeWitt
Parece que a formatação pode ter sido estragada. Estou lendo corretamente isso JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)e isso obj(value)é um alias de tabela e coluna? Neste exemplo, se objé um alias de tabela, para que é um alias? O conjunto retornou de jsonb_array_elements?
Tyler DeWitt
11
sim e sim. eu removi meu comentário embaralhado.
Erwin Brandstetter
É necessário usar o alias da coluna? Nos meus testes, JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'teve o mesmo efeito (depois de atualizar a instrução select para usar em valuevez de val). Parece que jsonb_array_elements(t.usr)retorna uma tabela com apenas uma coluna. O postgres é inteligente e percebe que obj ->>é o mesmo obj.val ->>?
Tyler DeWitt
Com apenas uma coluna, o Postgres usa um determinado alias como tabela e nome da coluna. Estou apenas sendo explícito, pois há muitas funções de retorno de conjunto que retornam mais de uma coluna.
Erwin Brandstetter