Ir al contenido
wannacode

Bases de datos de grafos

En resumen: Una base de datos de grafos como Neo4j guarda nodos y relaciones en lugar de tablas, y las consultas de Cypher buscan patrones y caminos en ellos.

Problema

Quienes compraron café también compraron

Bajo cada producto la tienda quiere una línea «Quienes compraron esto también compraron». Para el café en grano la consulta en la base de datos SQL es esta:

SQLbase de datos: tienda

Recorre seis tablas: del producto a las líneas de pedido, a los pedidos, al cliente, a sus otros pedidos, a las líneas de estos y a los productos. orders y order_items participan dos veces cada una. El siguiente deseo es aún más pesado: «los productos de las personas cuyas recomendaciones sigues, y de las personas a las que ellas siguen». Cada paso son más joins, y «hasta tres pasos» necesita WITH RECURSIVE. La pregunta trata de conexiones entre cosas, y en las tablas las conexiones están repartidas en claves foráneas.

La idea

Nodos y relaciones

Una base de datos de grafos guarda los datos como un grafo: nodos (las cosas, como un cliente y un producto) y relaciones entre ellos. Cada relación tiene un tipo (BOUGHT) y una dirección (del cliente al producto). Los nodos tienen etiquetas (:Customer), y tanto los nodos como las relaciones tienen propiedades (name, price, quantity). La relación está guardada en la base de datos, no se busca con un JOIN en cada consulta: desde un nodo la base de datos va directa a sus vecinos.

La base de datos de grafos más conocida es Neo4j, y su lenguaje es Cypher. Sus patrones son dibujos hechos con caracteres: (c:Customer) es un nodo, y -[:BOUGHT]-> es una flecha.

de grafos (como Neo4j)simulador

La consulta termina con ;, así que puede ocupar varias líneas. CREATE crea nodos y relaciones. MATCH encuentra todo lo que encaja con el patrón, y RETURN dice qué mostrar. Bajo la salida el simulador dibuja los nodos y las relaciones que encontró.

Más precisamente, para curiosos

El simulador conoce una parte de Cypher: MATCH, WHERE, CREATE, MERGE, SET, DELETE, DETACH DELETE, RETURN, ORDER BY, SKIP, LIMIT y unas pocas funciones. No hay WITH, ni OPTIONAL MATCH, ni UNWIND, ni índices, ni restricciones. La salida se parece a la consola cypher-shell, sin la línea con el tiempo de la consulta, y los mensajes de error son más cortos. El simulador muestra el prompt antes de cada línea de una consulta. Neo4j Browser no necesita el ; del final y dibuja el resultado por sí mismo. Sin ORDER BY el simulador devuelve las filas en el orden en que se crearon los nodos, y Neo4j no promete ningún orden.

Pruébalo

Georgi y el pan

La base de datos está vacía. Anota que el cliente Georgi Petrov compró pan blanco: un nodo con la etiqueta Customer y una propiedad name, un nodo con la etiqueta Product y una propiedad name, y una relación BOUGHT del cliente al producto.

de grafos (como Neo4j)simulador

La idea

Patrones

A partir de aquí la base de datos es la tienda: 20 clientes, 25 productos en 6 categorías y 109 relaciones BOUGHT, una por cada cliente y producto de los pedidos que no se cancelaron. Vienen de la base de datos SQL del curso. Un producto está conectado con su categoría por una relación IN.

Un patrón puede ser tan largo como necesites: del cliente al producto y a la categoría, sin un solo join.

de grafos (como Neo4j)simulador

La dirección del patrón tiene que coincidir con la dirección de la relación:

de grafos (como Neo4j)simulador

No hay flechas BOUGHT de un producto a un cliente, así que la primera consulta no encuentra a nadie. <-[:BOUGHT]- lee la misma relación desde su otro extremo. -[:BOUGHT]- sin punta de flecha encuentra la relación en cualquier dirección.

Más precisamente, para curiosos

El grafo no guarda los pedidos individuales: una relación BOUGHT solo dice que el cliente ha comprado el producto. Si necesitas también los pedidos, pasan a ser nodos: (:Customer)-[:PLACED]->(:Order)-[:CONTAINS {quantity: 2}]->(:Product). Qué es un nodo y qué es una relación depende de las preguntas que vayas a hacer, igual que el esquema de una base de datos SQL.

Míralo

La misma pregunta, un patrón

Así es «quienes compraron café también compraron» en Cypher:

de grafos (como Neo4j)simulador

La respuesta es la misma que la de la consulta SQL del principio, fila por fila. El patrón se lee de izquierda a derecha: el café, comprado por un cliente que también compró otro producto. El café no se cuenta contra sí mismo: en un MATCH la misma relación no puede participar dos veces, y la relación con el café ya está tomada. En SQL eso era una condición aparte (p2.id <> p1.id). Solo tres personas compraron café, así que los cinco productos tienen dos compradores cada uno y están ordenados por nombre.

La diferencia no es solo la longitud. La base de datos SQL busca cada join en un índice o en toda la tabla. La base de datos de grafos parte del café y sigue solo sus relaciones: el trabajo depende de cuántas relaciones hay alrededor del café, no de cuántos pedidos tiene la tienda.

La idea

Caminos de longitud variable

Digamos que la tienda tiene perfiles, y cada cliente puede seguir las recomendaciones de otros clientes: (:Customer)-[:FOLLOWS]->(:Customer). Estas relaciones están inventadas para la lección; la base de datos SQL no las tiene.

-[:FOLLOWS*1..2]-> es un camino de uno a dos pasos FOLLOWS. p = … le da nombre al camino entero, y length(p) es su número de pasos:

de grafos (como Neo4j)simulador

En el primer resultado Viktoria aparece dos veces: Elena sigue a Maria y a Dimitar, y los dos siguen a Viktoria. MATCH devuelve cada camino, no cada persona. En la segunda consulta WHERE f <> me quita a la propia Elena: en tres pasos el camino vuelve a ella (Elena, Maria, Viktoria, Elena).

Quién está en el camino lo muestra nodes(p):

de grafos (como Neo4j)simulador

En SQL lo mismo es WITH RECURSIVE sobre una tabla follows: un inicio, UNION ALL y un paso que une follows con las filas anteriores, más un contador de pasos. Aquí es *1..3.

Míralo

Un camino sin límite

Un patrón sin tipo y sin dirección, «todo lo conectado con Elena», con un camino cada vez más largo:

de grafos (como Neo4j)simulador

98 recorridos, luego 688, luego 4370. Cada paso los multiplica por seis o siete, porque un producto como el pan blanco está conectado con muchos clientes, y cada uno de ellos con muchos productos. * sin límite superior prueba todos los recorridos. El simulador se detiene tras 100 000 pasos, y Neo4j no se detiene solo: la consulta sigue hasta que se queda sin tiempo o sin memoria. Por eso escribe siempre un límite superior y, cuando puedas, también el tipo y la dirección de la relación.

Ejercicio

Recomendaciones

1. El queso también necesita una línea «Quienes compraron esto también compraron».

¿Qué tres productos compraron más las personas que compraron queso blanco? Muestra el nombre y el número de compradores (buyers), de más a menos, y por nombre cuando los números sean iguales.

de grafos (como Neo4j)simulador

2. Maria Ivanova sigue a unas cuantas personas, y ellas siguen a otras.

Muestra los clientes a los que llega Maria Ivanova a través de FOLLOWS en uno o dos pasos, cada uno una vez, ordenados por nombre.

de grafos (como Neo4j)simulador

3. Ahora la recomendación de verdad.

Encuentra los productos comprados por las personas a las que Elena Dimitrova sigue en uno o dos pasos, que ella aún no ha comprado. Muestra los tres comprados por más personas distintas (people), y por nombre cuando los números sean iguales.

de grafos (como Neo4j)simulador

La idea

Cuándo una base de datos de grafos y cuándo no

Una base de datos de grafos es buena cuando la pregunta trata de conexiones, a menudo de varios pasos: recomendaciones («quienes compraron esto también compraron»), redes sociales, detección de fraude (cuentas que comparten un teléfono o una dirección), rutas y dependencias, permisos de acceso a través de grupos. No es buena para informes que suman números de todos los registros, como los ingresos por mes: una consulta así no sigue relaciones, lo lee todo seguido. Eso es trabajo para SQL con GROUP BY o para una base de datos hecha para informes (OLAP). Por eso un grafo suele estar junto a la base de datos SQL: los pedidos y el dinero se quedan en SQL, y una copia de las conexiones va al grafo para las recomendaciones.

Más precisamente, para curiosos

Además de Neo4j, entienden Cypher Memgraph y Amazon Neptune (allí el lenguaje se llama openCypher; Neptune entiende también Gremlin y SPARQL), y la extensión Apache AGE añade Cypher a PostgreSQL. En 2024 salió el estándar internacional GQL (ISO/IEC 39075), basado en gran parte en las ideas de Cypher. Otro tipo de base de datos de grafos es el almacén RDF con el lenguaje SPARQL, donde todo es una tripleta de «sujeto, predicado, objeto».

Test

Ponte a prueba

1. ¿Qué dice el patrón (c:Customer)-[:BOUGHT]->(p:Product)?
2. MATCH (me)-[:FOLLOWS*1..2]->(f) RETURN f.name devuelve a Viktoria dos veces. ¿Por qué?
3. ¿Qué pasa con un patrón -[*]- sin límite superior en un grafo grande?
4. Necesitas los ingresos por mes de los últimos dos años. ¿Dónde va esta consulta?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·