¿Cómo obtener un objeto particular de la matriz jsonb en PostgreSQL?

15

Tengo un campo llamado 'usuario' que contiene una matriz json que se ve más o menos así:

"user":

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

Ahora quiero una consulta como:

select count from tablename where id = "1"

No puedo obtener el campo particular countde una matriz de objetos json en PostgreSQL 9.4.

Rabi C Shah
fuente

Respuestas:

17

Sería mucho más eficiente almacenar sus valores en un esquema normalizado. Dicho esto, también puedes hacer que funcione con tu configuración actual.

Supuestos

Suponiendo esta definición de tabla:

CREATE TABLE tbl (tbl_id int, usr jsonb);

"usuario" es una palabra reservada y requeriría comillas dobles para usar como nombre de columna. No hagas eso. Yo uso usren su lugar.

Consulta

La consulta no es tan trivial como los comentarios (ahora eliminados) lo hicieron 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"}]';

Hay 3 pasos básicos :

1. Identificar filas calificadas a bajo costo

WHERE t.usr @> '[{"_id":"1"}]'identifica filas con objetos coincidentes en la matriz JSON. La expresión puede usar un índice GIN genérico en la jsonbcolumna, o uno con la clase de operador más especializada jsonb_path_ops:

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

La WHEREcláusula agregada es lógicamente redundante , pero se requiere para usar el índice. La expresión en la cláusula de unión impone la misma condición pero solo después de anular la matriz en cada fila que califica hasta ahora. Con el soporte de índice, Postgres solo procesa filas que contienen un objeto calificado para empezar. No importa mucho con tablas pequeñas, hace una gran diferencia con tablas grandes y solo unas pocas filas calificadas.

Relacionado:

2. Identifique los objetos coincidentes en la matriz

Unnest con jsonb_array_elements(). ( unnest()solo es bueno para los tipos de matriz de Postgres). Dado que solo estamos interesados ​​en la coincidencia de objetos, filtre la condición de unión de inmediato.

Relacionado:

3. Extraer valor para clave anidada 'count'

Después de los objetos que califican han sido extraídos, simplemente: obj.val->>'count'.

Erwin Brandstetter
fuente
2
¿De dónde obj(value)viene el? ¿Está en el LATERAL JOIN, el jsonb_array_elementso en otro lugar?
Tyler DeWitt
Parece que el formato podría haberse estropeado. ¿Lo estoy leyendo correctamente JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)y ese obj(value)es un alias de tabla y columna? En este ejemplo, si objes un alias de tabla, ¿para qué es un alias? El conjunto regresó de jsonb_array_elements?
Tyler DeWitt
1
si y si. Eliminé mi comentario revuelto.
Erwin Brandstetter
¿Es necesario usar el alias de columna? En mis pruebas, JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'tuvo el mismo efecto (una vez que actualiza la instrucción de selección para usar en valuelugar de val) Parece que jsonb_array_elements(t.usr)devuelve una tabla con solo una columna. ¿Postgres es inteligente y se da cuenta de que obj ->>es lo mismo obj.val ->>?
Tyler DeWitt
Con solo una columna, Postgres usa un alias dado como nombre de tabla y columna. Solo estoy siendo explícito ya que hay muchas funciones de devolución de conjuntos que devuelven más de una columna.
Erwin Brandstetter