JSON_OVERLAPS

La función JSON_OVERLAPS compara dos documentos JSON y devuelve verdadero si comparten al menos un elemento en común. Es especialmente útil para detectar intersecciones entre arrays, por ejemplo, cuando quieres encontrar productos que compartan etiquetas, usuarios con intereses similares o registros con categorías coincidentes.

Sintaxis

JSON_OVERLAPS(documento_json1, documento_json2)

La función recibe dos expresiones JSON y devuelve 1 si los documentos tienen al menos un elemento compartido, o 0 si no tienen ninguno. El comportamiento varía según el tipo de los documentos comparados:

Cuando ambos argumentos son arrays, la función comprueba si existe al menos un valor presente en ambos arrays. Cuando ambos son objetos, comprueba si comparten al menos una clave con el mismo valor. Cuando uno es un escalar y el otro un array, comprueba si el escalar es miembro del array. Si ambos son escalares, simplemente compara si son iguales.

JSON_OVERLAPS requiere MySQL 8.0.17 o superior y, al igual que MEMBER OF, puede aprovechar los índices multi-valor para búsquedas eficientes.

Comportamiento básico

Empecemos con arrays simples:

SELECT JSON_OVERLAPS('[1, 2, 3]', '[3, 4, 5]') AS resultado;
resultado
1

Ambos arrays contienen el valor 3, por lo que hay solapamiento. Si no hay elementos comunes:

SELECT JSON_OVERLAPS('[1, 2, 3]', '[4, 5, 6]') AS resultado;
resultado
0

La comparación de tipos es estricta, igual que con MEMBER OF:

SELECT JSON_OVERLAPS('[1, 2, 3]', '["1", "2", "3"]') AS resultado;
resultado
0

Los enteros 1, 2, 3 no son iguales a las cadenas "1", "2", "3", por lo que no hay solapamiento.

Con objetos, la función busca pares clave-valor comunes:

SELECT JSON_OVERLAPS(
    '{"color": "rojo", "talla": "M"}',
    '{"color": "rojo", "peso": 0.5}'
) AS resultado;
resultado
1

Ambos objetos comparten la clave "color" con el valor "rojo". Si la clave coincide pero el valor es diferente, no se considera solapamiento:

SELECT JSON_OVERLAPS(
    '{"color": "rojo", "talla": "M"}',
    '{"color": "azul", "peso": 0.5}'
) AS resultado;
resultado
0

Aunque ambos tienen la clave "color", los valores son diferentes, así que no hay solapamiento.

Caso práctico: sistema de etiquetas de productos

En una tienda online con productos etiquetados, JSON_OVERLAPS permite encontrar productos que compartan etiquetas con un producto dado:

CREATE TABLE productos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(200),
    precio DECIMAL(10,2),
    etiquetas JSON
);
 
INSERT INTO productos (nombre, precio, etiquetas) VALUES
('iPhone 15 Pro', 1299.99, '["smartphone", "apple", "5g", "premium"]'),
('Samsung Galaxy S24', 899.99, '["smartphone", "samsung", "5g", "android"]'),
('Google Pixel 8', 699.00, '["smartphone", "google", "5g", "android"]'),
('MacBook Air M3', 1399.00, '["portatil", "apple", "ultrabook"]'),
('iPad Air', 699.00, '["tablet", "apple", "educacion"]'),
('Sony WH-1000XM5', 349.00, '["auriculares", "sony", "bluetooth", "premium"]');

Para encontrar productos que compartan etiquetas con el iPhone 15 Pro (etiquetas: smartphone, apple, 5g, premium):

SELECT
    p2.nombre,
    p2.precio,
    p2.etiquetas
FROM productos p1
JOIN productos p2 ON p1.id != p2.id
WHERE p1.id = 1
  AND JSON_OVERLAPS(p1.etiquetas, p2.etiquetas);
nombreprecioetiquetas
Samsung Galaxy S24899.99["smartphone", "samsung", "5g", "android"]
Google Pixel 8699.00["smartphone", "google", "5g", "android"]
MacBook Air M31399.00["portatil", "apple", "ultrabook"]
iPad Air699.00["tablet", "apple", "educacion"]
Sony WH-1000XM5349.00["auriculares", "sony", "bluetooth", "premium"]

Todos estos productos comparten al menos una etiqueta con el iPhone. El Samsung y el Pixel comparten "smartphone" y "5g", el MacBook y el iPad comparten "apple", y los Sony comparten "premium". Este es el patrón básico de un sistema de recomendaciones: "productos relacionados".

Caso práctico: filtrar por múltiples criterios

Supongamos que un usuario busca productos que coincidan con cualquiera de sus intereses. En lugar de escribir múltiples condiciones OR, puedes usar JSON_OVERLAPS:

SET @intereses_usuario = '["premium", "bluetooth"]';
 
SELECT nombre, precio, etiquetas
FROM productos
WHERE JSON_OVERLAPS(etiquetas, @intereses_usuario);
nombreprecioetiquetas
iPhone 15 Pro1299.99["smartphone", "apple", "5g", "premium"]
Sony WH-1000XM5349.00["auriculares", "sony", "bluetooth", "premium"]

La consulta devuelve productos que tengan la etiqueta "premium" o "bluetooth" (o ambas). Es una forma elegante de implementar filtros flexibles donde el usuario puede seleccionar múltiples opciones.

Para verificar que un producto tenga todas las etiquetas (en lugar de al menos una), usarías JSON_CONTAINS en su lugar.

Caso práctico: detectar conflictos de horario

Imagina un sistema de reservas donde cada reserva tiene un array de horas ocupadas:

CREATE TABLE reservas_sala (
    id INT AUTO_INCREMENT PRIMARY KEY,
    sala VARCHAR(50),
    fecha DATE,
    empleado VARCHAR(100),
    horas JSON
);
 
INSERT INTO reservas_sala (sala, fecha, empleado, horas) VALUES
('Sala A', '2025-06-15', 'María García', '[9, 10, 11]'),
('Sala A', '2025-06-15', 'Carlos López', '[14, 15, 16]'),
('Sala B', '2025-06-15', 'Ana Martínez', '[10, 11, 12]');

Para verificar si una nueva reserva de la Sala A entre las 11 y las 13 tiene conflicto con reservas existentes:

SET @nueva_reserva = '[11, 12, 13]';
 
SELECT
    empleado,
    horas,
    JSON_OVERLAPS(horas, @nueva_reserva) AS tiene_conflicto
FROM reservas_sala
WHERE sala = 'Sala A'
  AND fecha = '2025-06-15';
empleadohorastiene_conflicto
María García[9, 10, 11]1
Carlos López[14, 15, 16]0

La reserva de María tiene conflicto porque la hora 11 está en ambos arrays. La de Carlos no tiene conflicto. Para bloquear la reserva solo si hay conflicto:

SELECT COUNT(*) AS conflictos
FROM reservas_sala
WHERE sala = 'Sala A'
  AND fecha = '2025-06-15'
  AND JSON_OVERLAPS(horas, @nueva_reserva);
conflictos
1

Si el resultado es mayor que cero, la aplicación puede rechazar la nueva reserva.

Manejo de NULL

Si cualquiera de los argumentos es NULL, JSON_OVERLAPS devuelve NULL:

SELECT JSON_OVERLAPS(NULL, '[1, 2, 3]') AS resultado;
resultado
NULL
SELECT JSON_OVERLAPS('[1, 2, 3]', NULL) AS resultado;
resultado
NULL

Con arrays vacíos, el resultado es 0 porque no hay elementos que puedan coincidir:

SELECT JSON_OVERLAPS('[]', '[1, 2, 3]') AS resultado;
resultado
0

Si ambos arrays contienen null como elemento, sí se detecta solapamiento:

SELECT JSON_OVERLAPS('[1, null, 3]', '[null, 4, 5]') AS resultado;
resultado
1

Combinación con otras funciones

Puedes combinar JSON_OVERLAPS con otras funciones para obtener información más detallada. Por ejemplo, para saber cuántas etiquetas comparten dos productos, no basta con JSON_OVERLAPS (que solo dice sí o no). Puedes complementarlo con subconsultas:

SELECT
    p1.nombre AS producto_1,
    p2.nombre AS producto_2,
    JSON_OVERLAPS(p1.etiquetas, p2.etiquetas) AS se_solapan
FROM productos p1
CROSS JOIN productos p2
WHERE p1.id < p2.id
  AND JSON_OVERLAPS(p1.etiquetas, p2.etiquetas) = 1
ORDER BY p1.nombre, p2.nombre;
producto_1producto_2se_solapan
Google Pixel 8Samsung Galaxy S241
iPad AirMacBook Air M31
iPhone 15 ProGoogle Pixel 81
iPhone 15 ProiPad Air1
iPhone 15 ProMacBook Air M31
iPhone 15 ProSamsung Galaxy S241
iPhone 15 ProSony WH-1000XM51

Esta consulta muestra todos los pares de productos que comparten al menos una etiqueta, lo que puede alimentar un algoritmo de recomendaciones.

Al igual que MEMBER OF, JSON_OVERLAPS puede aprovechar los índices multi-valor cuando uno de los operandos es una columna JSON indexada, lo que mejora significativamente el rendimiento en tablas grandes.

En el siguiente artículo veremos JSON_PRETTY para formatear documentos JSON de forma legible.

Escrito por Eduardo Lázaro