El operador || sobre jsonb hace una fusion superficial de dos documentos: conserva todas las claves de la izquierda, superpone todas las de la derecha y, ante una colision, gana el valor de la derecha. Es la forma mas corta de aplicar un parche parcial al JSON directamente en la base de datos sin reescribir todo el documento desde la aplicacion.
Fusionar objetos: gana la clave de la derecha
Cuando ambos operandos son objetos JSON, el resultado es la union de sus claves. Si una clave aparece en los dos, el valor sale del operando de la derecha.
SELECT '{"verified": false, "city": "Lima"}'::jsonb
|| '{"verified": true}'::jsonb;
Algunos hechos a tener presentes:
- El operador no muta el origen; devuelve un documento nuevo, como toda operacion sobre
jsonb.
- El orden de los operandos importa:
a || b y b || a difieren cuando las claves se solapan.
- Ambos operandos deben ser
jsonb. Si la columna es de tipo json, conviertela con ::jsonb.
- Una clave de la derecha con valor
null no borra la clave; la sobrescribe con null.
Anadir elementos a un array
Cuando ambos operandos son arrays, || los concatena. Esa es la manera comoda de empujar un elemento al final de un array JSON.
SELECT '["sql", "json"]'::jsonb || '["postgres"]'::jsonb;
Cuando un operando es un array y el otro es un escalar u objeto, este ultimo se envuelve en un array de un elemento y se pega:
SELECT '["a", "b"]'::jsonb || '"c"'::jsonb;
Un caso tipico es anadir una etiqueta a un array dentro de una columna:
UPDATE users
SET profile = profile || '{"tags": []}'::jsonb
WHERE profile -> 'tags' IS NULL;
Un parche parcial en un UPDATE
El caso estrella es actualizar varios campos en una sola expresion. Supongamos que orders tiene una columna JSONB meta; marcamos un pedido como pagado y anotamos el metodo:
UPDATE orders
SET meta = meta || '{"paid": true, "method": "card"}'::jsonb
WHERE id = 1001;
Puedes construir el parche de forma dinamica a partir de columnas con jsonb_build_object en vez de pegar cadenas a mano:
UPDATE users
SET profile = profile || jsonb_build_object(
'country', country,
'email', email
)
WHERE id = 42;
Tambien queda limpio armar una copia enriquecida dentro de un SELECT, sin tocar la tabla:
SELECT id,
profile || jsonb_build_object('active', status = 'active') AS enriched
FROM users;
Trampa: los objetos anidados se reemplazan enteros
El error mas comun es esperar que || haga una fusion profunda; solo opera en el nivel superior. Si una clave de la derecha es un objeto, reemplaza por completo al objeto de la izquierda en lugar de fusionarse campo a campo.
SELECT '{"address": {"city": "Lima", "zip": "15001"}}'::jsonb
|| '{"address": {"city": "Quito"}}'::jsonb;
La clave zip desaparecio porque el address de la derecha sobreescribio entero al de la izquierda. Para cambiar solo un campo anidado, recurre a jsonb_set con una ruta:
UPDATE users
SET profile = jsonb_set(profile, '{address,city}', '"Quito"')
WHERE id = 42;
La regla es simple: || sirve para parches planos y concatenacion; jsonb_set para ediciones quirurgicas mas adentro del arbol.
Diferencias en otros motores
- MySQL no tiene operador
|| para JSON (alli es un OR logico). La fusion se hace con JSON_MERGE_PATCH(a, b), que se comporta como un parche recursivo y ademas borra las claves cuyo valor es null. Tambien existe JSON_MERGE_PRESERVE, que junta los valores en arrays en lugar de reemplazar.
- ClickHouse no esta pensado para ediciones parciales de JSON: sus funciones JSON son sobre todo de lectura, y cambiar un documento suele implicar reescribir todo el valor.
En resumen: en PostgreSQL || es una fusion superficial rapida donde ganan las claves de la derecha; recuerda su poca profundidad y cambia a jsonb_set cuando necesites entrar dentro.
El operador
||sobrejsonbhace una fusion superficial de dos documentos: conserva todas las claves de la izquierda, superpone todas las de la derecha y, ante una colision, gana el valor de la derecha. Es la forma mas corta de aplicar un parche parcial al JSON directamente en la base de datos sin reescribir todo el documento desde la aplicacion.Fusionar objetos: gana la clave de la derecha
Cuando ambos operandos son objetos JSON, el resultado es la union de sus claves. Si una clave aparece en los dos, el valor sale del operando de la derecha.
SELECT '{"verified": false, "city": "Lima"}'::jsonb || '{"verified": true}'::jsonb; -- {"city": "Lima", "verified": true}Algunos hechos a tener presentes:
jsonb.a || byb || adifieren cuando las claves se solapan.jsonb. Si la columna es de tipojson, conviertela con::jsonb.nullno borra la clave; la sobrescribe connull.Anadir elementos a un array
Cuando ambos operandos son arrays,
||los concatena. Esa es la manera comoda de empujar un elemento al final de un array JSON.SELECT '["sql", "json"]'::jsonb || '["postgres"]'::jsonb; -- ["sql", "json", "postgres"]Cuando un operando es un array y el otro es un escalar u objeto, este ultimo se envuelve en un array de un elemento y se pega:
SELECT '["a", "b"]'::jsonb || '"c"'::jsonb; -- ["a", "b", "c"]Un caso tipico es anadir una etiqueta a un array dentro de una columna:
UPDATE users SET profile = profile || '{"tags": []}'::jsonb WHERE profile -> 'tags' IS NULL;Un parche parcial en un UPDATE
El caso estrella es actualizar varios campos en una sola expresion. Supongamos que
orderstiene una columna JSONBmeta; marcamos un pedido como pagado y anotamos el metodo:UPDATE orders SET meta = meta || '{"paid": true, "method": "card"}'::jsonb WHERE id = 1001;Puedes construir el parche de forma dinamica a partir de columnas con
jsonb_build_objecten vez de pegar cadenas a mano:UPDATE users SET profile = profile || jsonb_build_object( 'country', country, 'email', email ) WHERE id = 42;Tambien queda limpio armar una copia enriquecida dentro de un
SELECT, sin tocar la tabla:SELECT id, profile || jsonb_build_object('active', status = 'active') AS enriched FROM users;Trampa: los objetos anidados se reemplazan enteros
El error mas comun es esperar que
||haga una fusion profunda; solo opera en el nivel superior. Si una clave de la derecha es un objeto, reemplaza por completo al objeto de la izquierda en lugar de fusionarse campo a campo.SELECT '{"address": {"city": "Lima", "zip": "15001"}}'::jsonb || '{"address": {"city": "Quito"}}'::jsonb; -- {"address": {"city": "Quito"}}La clave
zipdesaparecio porque eladdressde la derecha sobreescribio entero al de la izquierda. Para cambiar solo un campo anidado, recurre ajsonb_setcon una ruta:UPDATE users SET profile = jsonb_set(profile, '{address,city}', '"Quito"') WHERE id = 42;La regla es simple:
||sirve para parches planos y concatenacion;jsonb_setpara ediciones quirurgicas mas adentro del arbol.Diferencias en otros motores
||para JSON (alli es un OR logico). La fusion se hace conJSON_MERGE_PATCH(a, b), que se comporta como un parche recursivo y ademas borra las claves cuyo valor esnull. Tambien existeJSON_MERGE_PRESERVE, que junta los valores en arrays en lugar de reemplazar.En resumen: en PostgreSQL
||es una fusion superficial rapida donde ganan las claves de la derecha; recuerda su poca profundidad y cambia ajsonb_setcuando necesites entrar dentro.