Los tipos de datos JSON sirven para almacenar datos JSON (JavaScript Object Notation),
tal como se especifica en la RFC
7159. Estos datos también se pueden almacenar como text, pero
los tipos de datos JSON tienen la ventaja de garantizar que cada
valor almacenado sea válido según las reglas de JSON. También hay
varias funciones y operadores específicos de JSON disponibles para los datos almacenados
en estos tipos de datos; consulta la Section 9.16.
PostgreSQL ofrece dos tipos para almacenar datos
JSON: json y jsonb. Para implementar mecanismos de consulta
eficientes para estos tipos de datos, PostgreSQL
también proporciona el tipo de datos jsonpath descrito en
la Section 8.14.7.
Los tipos de datos json y jsonb
aceptan conjuntos de valores casi idénticos como
entrada. La principal diferencia práctica es la eficiencia. El
tipo de datos json almacena una copia exacta del texto de entrada,
que las funciones de procesamiento deben volver a analizar en cada ejecución; mientras
que los datos jsonb se almacenan en un formato binario descompuesto que
hace que su entrada sea ligeramente más lenta debido a la sobrecarga de conversión
adicional, pero significativamente más rápida de procesar, ya que no se necesita volver
a analizar. jsonb también admite indexación, lo que puede ser una
ventaja importante.
Dado que el tipo json almacena una copia exacta del texto de entrada,
conservará los espacios en blanco semánticamente insignificantes entre tokens, así
como el orden de las claves dentro de los objetos JSON. Además, si un objeto JSON
dentro del valor contiene la misma clave más de una vez, se conservan todos los pares
clave/valor. (Las funciones de procesamiento consideran el último valor como el
operativo). Por el contrario, jsonb no conserva los espacios en
blanco, no conserva el orden de las claves de los objetos y no conserva las
claves de objeto duplicadas. Si se especifican claves duplicadas en la entrada,
solo se conserva el último valor.
En general, la mayoría de las aplicaciones deberían preferir almacenar datos JSON como
jsonb, a menos que existan necesidades muy especializadas, como
suposiciones heredadas sobre el orden de las claves de los objetos.
La RFC 7159 especifica que las cadenas JSON deben codificarse en UTF8. Por lo tanto, no es posible que los tipos JSON se ajusten estrictamente a la especificación JSON a menos que la codificación de la base de datos sea UTF8. Los intentos de incluir directamente caracteres que no se pueden representar en la codificación de la base de datos fallarán; por el contrario, se permitirán los caracteres que se pueden representar en la codificación de la base de datos pero no en UTF8.
La RFC 7159 permite que las cadenas JSON contengan secuencias de escape Unicode
denotadas por \u. En la función
de entrada para el tipo XXXXjson, se permiten los escapes Unicode
independientemente de la codificación de la base de datos, y solo se verifica su corrección
sintáctica (es decir, que sigan cuatro dígitos hexadecimales a \u).
Sin embargo, la función de entrada para jsonb es más estricta: no permite
escapes Unicode para caracteres que no se pueden representar en la codificación de la base
de datos. El tipo jsonb también
rechaza \u0000 (porque no se puede representar en el tipo
text de PostgreSQL), e insiste en
que cualquier uso de pares subrogados de Unicode para designar caracteres fuera
del Plano Multilingüe Básico de Unicode sea correcto. Los escapes Unicode válidos
se convierten al carácter único equivalente para su almacenamiento;
esto incluye la conversión de pares subrogados en un solo carácter.
Muchas de las funciones de procesamiento de JSON descritas
en la Section 9.16 convertirán los escapes Unicode en
caracteres normales y, por lo tanto, arrojarán los mismos tipos de errores
que se acaban de describir, incluso si su entrada es de tipo json
y no jsonb. El hecho de que la función de entrada json no
realice estas comprobaciones puede considerarse un artefacto histórico, aunque
permite el almacenamiento simple (sin procesamiento) de escapes Unicode JSON
en una codificación de base de datos que no admite los caracteres representados.
Al convertir la entrada JSON textual a jsonb, los tipos primitivos
descritos por la RFC 7159 se asignan eficazmente a tipos
nativos de PostgreSQL, como se muestra
en la Table 8.23.
Por lo tanto, existen algunas limitaciones adicionales menores sobre lo que
constituye datos jsonb válidos que no se aplican al
tipo json, ni a JSON en abstracto, correspondientes
a los límites de lo que puede representar el tipo de datos subyacente.
En particular, jsonb rechazará números que estén fuera del
rango del tipo de datos numeric de PostgreSQL,
mientras que json no lo hará. Estas restricciones definidas por la
implementación están permitidas por la RFC 7159. Sin embargo, en
la práctica, es mucho más probable que tales problemas ocurran en otras
implementaciones, ya que es común representar el tipo primitivo number de
JSON como punto flotante de doble precisión IEEE 754 (lo que la
RFC 7159 prevé y permite explícitamente).
Al usar JSON como formato de intercambio con tales sistemas, se debe considerar el
peligro de perder precisión numérica en comparación con los datos almacenados
originalmente por PostgreSQL.
Por el contrario, como se indica en la tabla, existen algunas restricciones menores en el formato de entrada de los tipos primitivos JSON que no se aplican a los tipos correspondientes de PostgreSQL.
Table 8.23. Tipos primitivos JSON y tipos correspondientes de PostgreSQL
| Tipo primitivo JSON | Tipo de PostgreSQL | Notas |
|---|---|---|
string | text | No se permite \u0000, al igual que los escapes Unicode
que representan caracteres no disponibles en la codificación de la base de datos |
number | numeric | No se permiten los valores NaN e infinity |
boolean | boolean | Solo se aceptan las formas en minúsculas de true y false |
null | (ninguno) | El valor SQL NULL es un concepto diferente |
La sintaxis de entrada/salida para los tipos de datos JSON es la especificada en la RFC 7159.
Las siguientes son todas expresiones válidas de tipo json (o jsonb):
-- Valor escalar/primitivo simple
-- Los valores primitivos pueden ser números, cadenas entre comillas, true, false o null
SELECT '5'::json;
-- Array de cero o más elementos (no es necesario que los elementos sean del mismo tipo)
SELECT '[1, 2, "foo", null]'::json;
-- Objeto que contiene pares de claves y valores
-- Ten en cuenta que las claves de los objetos siempre deben ser cadenas entre comillas
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;
-- Los arrays y objetos se pueden anidar arbitrariamente
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;
Como se indicó anteriormente, cuando se ingresa un valor JSON y luego se imprime sin
ningún procesamiento adicional, json devuelve el mismo texto que se
ingresó, mientras que jsonb no conserva los detalles semánticamente
insignificantes como los espacios en blanco. Por ejemplo, observa las diferencias aquí:
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)
Un detalle semánticamente insignificante que vale la pena señalar es que
en jsonb, los números se imprimirán de acuerdo con el comportamiento del
tipo numeric subyacente. En la práctica, esto significa que los números
introducidos con la notación E se imprimirán sin ella, por
ejemplo:
SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb;
json | jsonb
-----------------------+-------------------------
{"reading": 1.230e-5} | {"reading": 0.00001230}
(1 row)
Sin embargo, jsonb conservará los ceros fraccionarios finales, como se ve
en este ejemplo, aunque sean semánticamente insignificantes para
propósitos como las comprobaciones de igualdad.
Para obtener la lista de funciones y operadores integrados disponibles para construir y procesar valores JSON, consulta la Section 9.16.
Representar datos como JSON puede ser considerablemente más flexible que el modelo de datos relacional tradicional, lo que resulta atractivo en entornos donde los requisitos son cambiantes. Es muy posible que ambos enfoques coexistan y se complementen entre sí dentro de la misma aplicación. Sin embargo, incluso para aplicaciones donde se desea la máxima flexibilidad, se recomienda que los documentos JSON tengan una estructura un poco fija. La estructura normalmente no se impone (aunque es posible imponer algunas reglas de negocio de forma declarativa), pero tener una estructura predecible facilita la escritura de consultas que resuman de manera útil un conjunto de “documentos” (datos) en una tabla.
Los datos JSON están sujetos a las mismas consideraciones de control de concurrencia que cualquier otro tipo de datos cuando se almacenan en una tabla. Aunque almacenar documentos grandes es factible, ten en cuenta que cualquier actualización adquiere un bloqueo a nivel de fila en toda la fila. Considera limitar los documentos JSON a un tamaño manejable para disminuir la contención de bloqueo entre las transacciones que realizan actualizaciones. Idealmente, cada documento JSON debería representar un dato atómico que las reglas de negocio dicten que no se puede subdividir razonablemente en datos más pequeños que pudieran modificarse de forma independiente.
jsonb #
Probar la contención es una capacidad importante de
jsonb. No existe un conjunto de facilidades paralelo para el
tipo json. La contención prueba si
un documento jsonb contiene a otro.
Estos ejemplos devuelven true excepto donde se indique:
-- Los valores escalares/primitivos simples contienen solo el valor idéntico:
SELECT '"foo"'::jsonb @> '"foo"'::jsonb;
-- El array del lado derecho está contenido en el del lado izquierdo:
SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb;
-- El orden de los elementos del array no es significativo, por lo que esto también es cierto:
SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb;
-- Los elementos duplicados del array tampoco importan:
SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb;
-- El objeto con un solo par en el lado derecho está contenido
-- dentro del objeto en el lado izquierdo:
SELECT '{"product": "PostgreSQL", "version": 9.4, "jsonb": true}'::jsonb @> '{"version": 9.4}'::jsonb;
-- El array del lado derecho no se considera contenido dentro del
-- array de la izquierda, a pesar de que un array similar está anidado dentro de él:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- devuelve false
-- Pero con un nivel de anidamiento, sí está contenido:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb;
-- De manera similar, aquí no se reporta contención:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb; -- devuelve false
-- Se contiene una clave de nivel superior y un objeto vacío:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb;
El principio general es que el objeto contenido debe coincidir con el objeto contenedor en cuanto a estructura y contenido de datos, posiblemente después de descartar algunos elementos de array no coincidentes o pares clave/valor de objeto del objeto contenedor. Pero recuerda que el orden de los elementos del array no es significativo al hacer una coincidencia de contención, y los elementos duplicados del array se consideran efectivamente solo una vez.
Como excepción especial al principio general de que las estructuras deben coincidir, un array puede contener un valor primitivo:
-- Este array contiene el valor de cadena primitivo: SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb; -- Esta excepción no es recíproca -- aquí se reporta la no contención: SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- devuelve false
jsonb también tiene un operador de existencia, que es
una variación del tema de la contención: prueba si una cadena
(dada como un valor de tipo text) aparece como una clave de objeto o elemento
de array en el nivel superior del valor jsonb.
Estos ejemplos devuelven true excepto donde se indique:
-- La cadena existe como elemento de array:
SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar';
-- La cadena existe como clave de objeto:
SELECT '{"foo": "bar"}'::jsonb ? 'foo';
-- Los valores de los objetos no se consideran:
SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- devuelve false
-- Al igual que con la contención, la existencia debe coincidir en el nivel superior:
SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- devuelve false
-- Se considera que existe una cadena si coincide con una cadena JSON primitiva:
SELECT '"foo"'::jsonb ? 'foo';
Los objetos JSON son más adecuados que los arrays para probar la contención o existencia cuando hay muchas claves o elementos involucrados, porque a diferencia de los arrays, están optimizados internamente para búsquedas y no necesitan buscarse linealmente.
Dado que la contención de JSON es anidada, una consulta adecuada puede omitir
la selección explícita de subobjetos. Como ejemplo, supongamos que tenemos
una columna doc que contiene objetos en el nivel superior, y
la mayoría de los objetos contienen campos tags que contienen arrays de
subobjetos. Esta consulta busca entradas en las que aparecen subobjetos que contienen
tanto "term":"paris" como "term":"food",
al tiempo que ignora cualquier clave de este tipo fuera del array tags:
SELECT doc->'site_name' FROM websites
WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}';
Uno podría lograr lo mismo con, por ejemplo,
SELECT doc->'site_name' FROM websites
WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]';
pero ese enfoque es menos flexible y, a menudo, también menos eficiente.
Por otro lado, el operador de existencia de JSON no es anidado: solo buscará la clave o el elemento de array especificado en el nivel superior del valor JSON.
Los diversos operadores de contención y existencia, junto con todos los demás operadores y funciones de JSON, están documentados en la Section 9.16.
jsonb #
Se pueden utilizar índices GIN para buscar eficientemente
claves o pares clave/valor que ocurran dentro de una gran cantidad de
documentos (datos) de tipo jsonb.
Se proporcionan dos “clases de operadores” GIN, que ofrecen diferentes
compensaciones de rendimiento y flexibilidad.
La clase de operador GIN predeterminada para jsonb admite consultas con
los operadores de existencia de clave ?, ?|
y ?&, el operador de contención
@>, y los operadores de coincidencia
de jsonpath @? y @@.
(Para obtener detalles sobre la semántica que implementan estos operadores,
consulta la Table 9.48).
Un ejemplo de creación de un índice con esta clase de operador es:
CREATE INDEX idxgin ON api USING GIN (jdoc);
La clase de operador GIN no predeterminada jsonb_path_ops
no admite los operadores de existencia de clave, pero sí admite
@>, @? y @@.
Un ejemplo de creación de un índice con esta clase de operador es:
CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);
Considera el ejemplo de una tabla que almacena documentos JSON recuperados de un servicio web de terceros, con una definición de esquema documentada. Un documento típico es:
{
"guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
"name": "Angela Barton",
"is_active": true,
"company": "Magnafone",
"address": "178 Howard Place, Gulf, Washington, 702",
"registered": "2009-11-07T08:53:22 +08:00",
"latitude": 19.793713,
"longitude": 86.513373,
"tags": [
"enim",
"aliquip",
"qui"
]
}
Almacenamos estos documentos en una tabla llamada api,
en una columna de tipo jsonb llamada jdoc.
Si se crea un índice GIN en esta columna,
las consultas como la siguiente pueden hacer uso del índice:
-- Buscar documentos en los que la clave "company" tenga el valor "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';
Sin embargo, el índice no se podría utilizar para consultas como la
siguiente, porque aunque el operador ? es indexable,
no se aplica directamente a la columna indexada jdoc:
-- Buscar documentos en los que la clave "tags" contenga la clave o elemento de array "qui" SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui';
Aún así, con el uso adecuado de índices de expresión, la consulta anterior
puede usar un índice. Si la búsqueda de elementos particulares dentro de
la clave "tags" es común, puede valer la pena definir un índice como este:
CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags'));
Ahora, la cláusula WHERE jdoc -> 'tags' ? 'qui'
se reconocerá como una aplicación del operador indexable
? a la expresión indexada jdoc -> 'tags'.
(Se puede encontrar más información sobre índices de expresión en la Section 11.7).
Otro enfoque para realizar consultas es explotar la contención, por ejemplo:
-- Buscar documentos en los que la clave "tags" contenga el elemento de array "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}';
Un índice GIN simple en la columna jdoc puede admitir esta
consulta. Pero ten en cuenta que dicho índice almacenará copias de cada clave y
valor en la columna jdoc, mientras que el índice de expresión
del ejemplo anterior almacena solo los datos encontrados bajo
la clave tags. Si bien el enfoque de índice simple es mucho más
flexible (ya que admite consultas sobre cualquier clave), los índices de expresión
específicos probablemente sean más pequeños y rápidos de buscar que un índice simple.
Los índices GIN también admiten los operadores @?
y @@, que realizan la coincidencia de jsonpath.
Los ejemplos son:
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @? '$.tags[*] ? (@ == "qui")';
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @@ '$.tags[*] == "qui"';
Para estos operadores, un índice GIN extrae cláusulas de la forma
fuera del
patrón accessors_chain
== constantjsonpath, y realiza la búsqueda en el índice basándose en
las claves y valores mencionados en estas cláusulas. La cadena de accesores
puede incluir accesores .,
key[*] y [.
La clase de operador index]jsonb_ops también
admite accesores .* y .**,
but the jsonb_path_ops operator class does not.
Aunque la clase de operador jsonb_path_ops admite
solo consultas con los operadores @>, @?
y @@, tiene notables
ventajas de rendimiento sobre la clase de operador
predeterminada jsonb_ops. Un índice jsonb_path_ops
suele ser mucho más pequeño que un índice jsonb_ops
sobre los mismos datos, y la especificidad de las búsquedas es mejor,
particularmente cuando las consultas contienen claves que aparecen con frecuencia en los
datos. Por lo tanto, las operaciones de búsqueda suelen tener un mejor rendimiento
que con la clase de operador predeterminada.
La diferencia técnica entre un índice GIN jsonb_ops
y uno jsonb_path_ops es que el primero
crea elementos de índice independientes para cada clave y valor en los datos,
mientras que el segundo crea elementos de índice solo para cada valor en los
datos.
[7]
Básicamente, cada elemento de índice jsonb_path_ops es
un hash del valor y las claves que conducen a él; por ejemplo, para indexar
{"foo": {"bar": "baz"}}, se crearía un único elemento de índice
que incorporaría los tres valores foo, bar
y baz en el valor hash. Por lo tanto, una consulta de contención
que busque esta estructura daría como resultado una búsqueda de índice extremadamente
específica; pero no hay forma alguna de averiguar si foo
aparece como una clave. Por otro lado, un índice jsonb_ops
crearía tres elementos de índice que representarían foo,
bar y baz por separado; luego, para realizar la
consulta de contención, buscaría las filas que contuvieran estos tres
elementos. Si bien los índices GIN pueden realizar una búsqueda AND de este tipo de manera
bastante eficiente, seguirá siendo menos específica y más lenta que la búsqueda
equivalente de jsonb_path_ops, especialmente si
hay una cantidad muy grande de filas que contienen uno solo de los tres elementos de índice.
Una desventaja del enfoque jsonb_path_ops es
que no produce entradas de índice para estructuras JSON que no contengan
ningún valor, como {"a": {}}. Si se solicita una búsqueda de
documentos que contengan dicha estructura, se requerirá un escaneo completo del
índice, lo cual es bastante lento. Por lo tanto, jsonb_path_ops no
es adecuado para aplicaciones que realizan tales búsquedas con frecuencia.
jsonb también admite índices btree y hash.
Estos suelen ser útiles solo si es importante comprobar la
igualdad de documentos JSON completos.
El ordenamiento btree para los datos jsonb rara vez
es de gran interés, pero para completitud es el siguiente:
Objeto>Array>Booleano>Número>Cadena>nullObjeto con n pares>objeto con n - 1 paresArray con n elementos>array con n - 1 elementos
con la excepción de que (por razones históricas) un array vacío de nivel superior se ordena antes que null.
Los objetos con el mismo número de pares se comparan en el orden:
clave-1,valor-1,clave-2...
Ten en cuenta que las claves de los objetos se comparan en su orden de almacenamiento; en particular, dado que las claves más cortas se almacenan antes que las claves más largas, esto puede conducir a resultados que podrían ser poco intuitivos, como:
{ "aa": 1, "c": 1} > {"b": 1, "d": 1}
Del mismo modo, los arrays con el mismo número de elementos se comparan en el orden:
elemento-1,elemento-2...
Los valores JSON primitivos se comparan utilizando las mismas reglas de comparación que para el tipo de datos subyacente de PostgreSQL. Las cadenas se comparan utilizando la colación predeterminada de la base de datos.
jsonb #
El tipo de datos jsonb admite expresiones de subindizado de estilo array
para extraer y modificar elementos. Los valores anidados se pueden indicar encadenando
expresiones de subindizado, siguiendo las mismas reglas que el argumento path
en la función jsonb_set. Si un valor jsonb
es un array, los subíndices numéricos comienzan en cero y los enteros negativos cuentan
hacia atrás desde el último elemento del array. No se admiten expresiones de rebanado.
El resultado de una expresión de subindizado siempre es del tipo de datos jsonb.
Las sentencias UPDATE pueden usar subindizado en la
cláusula SET para modificar valores de tipo jsonb. Las rutas
de subíndices deben ser transitables para todos los valores afectados en la medida en que existan. Por
ejemplo, la ruta val['a']['b']['c'] se puede transitar por completo
hasta c si cada uno de los elementos val,
val['a'] y val['a']['b'] es un
objeto. Si algún elemento val['a'] o val['a']['b']
no está definido, se creará como un objeto vacío y se rellenará según sea
necesario. Sin embargo, si el propio val o uno de los
valores intermedios se define como un no objeto, como una cadena, un número o un
jsonb null, el recorrido no puede continuar, por lo
que se produce un error y se aborta la transacción.
Un ejemplo de sintaxis de subindizado:
-- Extraer valor de objeto por clave
SELECT ('{"a": 1}'::jsonb)['a'];
-- Extraer valor de objeto anidado por ruta de clave
SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
-- Extraer elemento de array por índice
SELECT ('[1, "2", null]'::jsonb)[1];
-- Actualizar valor de objeto por clave. Ten en cuenta las comillas alrededor de '1': el valor
-- asignado también debe ser del tipo jsonb
UPDATE table_name SET jsonb_field['key'] = '1';
-- Esto producirá un error si el elemento jsonb_field['a']['b'] de algún registro es algo
-- distinto de un objeto. Por ejemplo, el valor {"a": 1} tiene un valor numérico
-- en la clave 'a'.
UPDATE table_name SET jsonb_field['a']['b']['c'] = '1';
-- Filtrar registros mediante una cláusula WHERE con subindizado. Como el resultado del
-- subindizado es jsonb, el valor con el que lo comparamos también debe ser jsonb.
-- Las comillas dobles hacen que "value" también sea una cadena jsonb válida.
SELECT * FROM table_name WHERE jsonb_field['key'] = '"value"';
La asignación a jsonb mediante subindizado maneja algunos casos límite de manera
diferente a jsonb_set. Cuando un valor jsonb de origen
es NULL, la asignación mediante subindizado continuará
como si fuera un valor JSON vacío del tipo (objeto o array) implícito en la
clave del subíndice:
-- Donde jsonb_field era NULL, ahora es {"a": 1}
UPDATE table_name SET jsonb_field['a'] = '1';
-- Donde jsonb_field era NULL, ahora es [1]
UPDATE table_name SET jsonb_field[0] = '1';
Si se especifica un índice para un array que contiene muy pocos elementos,
se agregarán elementos NULL hasta que se pueda alcanzar el índice
y establecer el valor.
-- Donde jsonb_field era [], ahora es [null, null, 2]; -- donde jsonb_field era [0], ahora es [0, null, 2] UPDATE table_name SET jsonb_field[2] = '2';
Un valor jsonb aceptará asignaciones a rutas de subíndice inexistentes
siempre que el último elemento existente a transitar sea un objeto o un
array, según lo implícito por el subíndice correspondiente (el elemento indicado por
el último subíndice en la ruta no se transita y puede ser cualquier cosa). Se crearán
estructuras de arrays y objetos anidados y, en el primer caso,
se rellenarán con null, según lo especificado por la ruta del subíndice hasta que se
pueda colocar el valor asignado.
-- Donde jsonb_field era {}, ahora es {"a": [{"b": 1}]}
UPDATE table_name SET jsonb_field['a'][0]['b'] = '1';
-- Donde jsonb_field era [], ahora es [null, {"a": 1}]
UPDATE table_name SET jsonb_field[1]['a'] = '1';
Hay extensiones adicionales disponibles que implementan transformaciones para el
tipo jsonb para diferentes lenguajes procedimentales.
Las extensiones para PL/Perl se llaman jsonb_plperl y
jsonb_plperlu. Si las usas, los valores jsonb
se asignan a arrays, hashes y escalares de Perl, según corresponda.
La extensión para PL/Python se llama jsonb_plpython3u.
Si la usas, los valores jsonb se asignan a diccionarios,
listas y escalares de Python, según corresponda.
De estas extensiones, jsonb_plperl se
considera “de confianza” (trusted), es decir, puede ser instalada por
usuarios que no sean superusuarios y que tengan el privilegio CREATE en la
base de datos actual. El resto requiere privilegios de superusuario para instalarse.
El tipo jsonpath implementa el soporte para el lenguaje de rutas SQL/JSON
en PostgreSQL para consultar de manera eficiente datos JSON.
Proporciona una representación binaria de la expresión de ruta SQL/JSON analizada
que especifica los elementos que el motor de rutas debe recuperar de los datos JSON
para su procesamiento posterior con las funciones de consulta SQL/JSON.
La semántica de los predicados y operadores de rutas SQL/JSON generalmente sigue a SQL. Al mismo tiempo, para proporcionar una forma natural de trabajar con datos JSON, la sintaxis de rutas SQL/JSON utiliza algunas convenciones de JavaScript:
El punto (.) se utiliza para el acceso a miembros.
Los corchetes ([]) se utilizan para el acceso a arrays.
Los arrays SQL/JSON son de base 0, a diferencia de los arrays SQL normales que comienzan desde 1.
Los literales numéricos en las expresiones de ruta SQL/JSON siguen las reglas de JavaScript,
que difieren tanto de SQL como de JSON en algunos detalles menores. Por
ejemplo, la ruta SQL/JSON permite .1 y
1., que no son válidos en JSON. Se admiten literales
enteros no decimales y separadores de guion bajo, por ejemplo,
1_000_000, 0x1EEE_FFFF,
0o273, 0b100101. En las rutas SQL/JSON
(y en JavaScript, pero no en SQL propiamente dicho), no debe haber un separador de
guion bajo directamente después del prefijo de base.
Una expresión de ruta SQL/JSON normalmente se escribe en una consulta SQL como un
literal de cadena de caracteres SQL, por lo que debe incluirse entre comillas simples,
y cualquier comilla simple que se desee dentro del valor debe duplicarse
(consulta la Section 4.1.2.1).
Algunas formas de expresiones de ruta requieren literales de cadena dentro de ellas.
Estos literales de cadena integrados siguen las convenciones de JavaScript/ECMAScript:
deben estar rodeados por comillas dobles y se pueden usar escapes de barra invertida
dentro de ellos para representar caracteres que de otro modo serían difíciles de escribir.
En particular, la forma de escribir una comilla doble dentro de un literal de cadena
integrado es \", y para escribir una barra invertida en sí, se
debe escribir \\. Otras secuencias especiales de barra invertida
incluyen las reconocidas en las cadenas de JavaScript:
\b,
\f,
\n,
\r,
\t,
\v
para varios caracteres de control ASCII,
\x para un código de carácter
escrito con solo dos dígitos hexadecimales,
NN\u para un carácter Unicode
identificado por su punto de código de 4 dígitos hexadecimales y
NNNN\u{ para un punto de código de
carácter Unicode escrito con 1 a 6 dígitos hexadecimales.
N...}
Una expresión de ruta consta de una secuencia de elementos de ruta, que pueden ser cualquiera de los siguientes:
Literales de ruta de tipos primitivos JSON: texto Unicode, numérico, true, false o null.
Variables de ruta enumeradas en la Table 8.24.
Operadores de acceso enumerados en la Table 8.25.
Operadores y métodos de jsonpath enumerados
en la Section 9.16.2.3.
Paréntesis, que se pueden utilizar para proporcionar expresiones de filtro o definir el orden de evaluación de las rutas.
Para obtener detalles sobre el uso de expresiones jsonpath con funciones de
consulta SQL/JSON, consulta la Section 9.16.2.
Table 8.24. Variables de jsonpath
| Variable | Descripción |
|---|---|
$ | Una variable que representa el valor JSON que se está consultando (el elemento de contexto). |
$varname |
Una variable con nombre. Su valor puede establecerse mediante el parámetro
vars de varias funciones de procesamiento JSON;
consulta la Table 9.51 para más detalles.
|
@ | Una variable que representa el resultado de la evaluación de la ruta en expresiones de filtro. |
Table 8.25. Accesores de jsonpath
| Operador accesor | Descripción |
|---|---|
|
|
Accesor de miembro que devuelve un miembro de objeto con
la clave especificada. Si el nombre de la clave coincide con alguna variable con nombre
que comience con |
|
|
Accesor de miembro comodín que devuelve los valores de todos los miembros ubicados en el nivel superior del objeto actual. |
|
|
Accesor de miembro comodín recursivo que procesa todos los niveles de la jerarquía JSON del objeto actual y devuelve todos los valores de los miembros, independientemente de su nivel de anidamiento. Esta es una extensión de PostgreSQL del estándar SQL/JSON. |
|
|
Como |
|
|
Accesor de elemento de array.
El |
|
|
Accesor de elemento de array comodín que devuelve todos los elementos del array. |
[7] Para este propósito, el término “valor” incluye elementos de array, aunque la terminología de JSON a veces considera que los elementos de array son distintos de los valores dentro de los objetos.