
Las bases de datos de gráficos están diseñadas específicamente para almacenar y consultar datos altamente conectados. Mientras que las bases de datos relacionales destacan en datos tabulares con relaciones conocidas, las bases de datos gráficas destacan cuando las relaciones son tan importantes como los datos mismos: redes sociales, motores de recomendación, detección de fraude, gráficos de conocimiento y cadenas de suministro.
Las bases de datos de gráficos están diseñadas específicamente para almacenar y consultar datos altamente conectados. Mientras que las bases de datos relacionales destacan en datos tabulares con relaciones conocidas, las bases de datos gráficas destacan cuando las relaciones son tan importantes como los datos mismos: redes sociales, motores de recomendación, detección de fraude, gráficos de conocimiento y cadenas de suministro.
La idea fundamental: en muchos dominios, las conexiones entre entidades SON los datos, y las bases de datos gráficas convierten esas conexiones en un ciudadano de primera clase.
┌──────────────────┐
│ Person │
│ name: "Alice" │
│ age: 30 │
└────────┬─────────┘
│
┌────────▼─────────┐
│ KNOWS │
│ since: 2020 │
│ type: "friend" │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Person │
│ name: "Bob" │
│ age: 32 │
└──────────────────┘
Tres componentes principales:
| Componente | Descripción | Ejemplo |
|---|---|---|
| Nodo (Vértice) | Una entidad en el gráfico. | Persona, Empresa, Producto |
| Borde (Relación) | Una conexión entre dos nodos. | SABE, TRABAJA, COMPRADO |
| Propiedad | Atributos en nodos o aristas | nombre: "Alicia", desde: 2020 |
Consultar relaciones en SQL:
-- Find friends of friends who work at Google
SELECT DISTINCT p3.name
FROM persons p1
JOIN friendships f1 ON p1.id = f1.person_id
JOIN persons p2 ON f1.friend_id = p2.id
JOIN friendships f2 ON p2.id = f2.person_id
JOIN persons p3 ON f2.friend_id = p3.id
JOIN employment e ON p3.id = e.person_id
JOIN companies c ON e.company_id = c.id
WHERE p1.name = 'Alice'
AND c.name = 'Google';
Misma consulta en Cypher (Neo4j):
MATCH (alice:Person {name: "Alice"})
-[:KNOWS*2]->
(friend_of_friend:Person)
MATCH (friend_of_friend)-[:WORKS_AT]->(google:Company {name: "Google"})
RETURN DISTINCT friend_of_friend.name
Diferencia clave: SQL hace malabarismos con múltiples JOIN. Los recorridos de gráficos coinciden con los patrones de forma natural.
// Create nodes and relationships
CREATE (alice:Person {name: "Alice", age: 30})
CREATE (bob:Person {name: "Bob", age: 32})
CREATE (alice)-[:KNOWS {since: 2020}]->(bob)
// Find friends of friends who bought products in the last month
MATCH (user:Person {id: $userId})
-[:KNOWS]->
(friend:Person)
-[:PURCHASED]->
(product:Product)
WHERE product.purchased_at > datetime() - duration('P1M')
RETURN product.name, count(*) as purchase_count
ORDER BY purchase_count DESC
LIMIT 10
// Shortest path between two people
MATCH p = shortestPath(
(alice:Person {name: "Alice"})-[:KNOWS*]-(bob:Person {name: "Bob"})
)
RETURN length(p) as degrees_of_separation,
[node IN nodes(p) | node.name] as path
// Same query in Gremlin
g.V().has('Person', 'name', 'Alice')
.repeat(out('KNOWS')).times(2)
.out('WORKS_AT')
.has('Company', 'name', 'Google')
.values('name')
.dedup()
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX org: <http://www.w3.org/ns/org#>
SELECT ?name WHERE {
?alice foaf:name "Alice" ;
foaf:knows ?friend .
?friend foaf:knows ?friend_of_friend .
?friend_of_friend org:memberOf ?company .
?company org:legalName "Google" .
?friend_of_friend foaf:name ?name .
}
| Característica | neo4j | Amazonas Neptuno | ArangoDB | JanusGraph | Dgrafo |
|---|---|---|---|---|---|
| Idioma de consulta | cifrar | Gremlin/SPARQL | AQL | duendecillo | GráficoQL+ |
| Almacenamiento | Gráfico nativo | RDF/Propiedad | Multimodelo | Casandra/HBase | tejón |
| Agrupación | Empresa | Gestionado | si | si | si |
| ÁCIDO | si | si | si | Por almacenamiento | si |
| Nivel gratuito | Comunidad (1 nodo) | Pago por uso | Código abierto | Código abierto | Código abierto |
| Mejor para | Gráficos empresariales | Ecosistema AWS | Multimodelo | Grandes gráficos de datos | Alto rendimiento |
| Búsqueda vectorial | ✅ (5.x+) | ❌ | ✅ | ❌ | ❌ |
(User:Alice)-[:KNOWS]->(User:Bob)
(User:Alice)-[:LIKES]->(Movie:Inception)
(User:Bob)-[:LIKES]->(Movie:Inception)
(User:Charlie)-[:KNOWS]->(User:Alice)
Consulta de recomendación: "Películas que les gustan a los amigos de amigos":
MATCH (me:User {id: $userId})
-[:KNOWS*1..2]-
(other:User)
-[:LIKES]->
(movie:Movie)
WHERE NOT EXISTS {
(me)-[:LIKES]->(movie)
OR (me)-[:DISLIKES]->(movie)
}
RETURN movie.title, count(DISTINCT other) as friend_count,
avg(other.rating) as avg_rating
ORDER BY friend_count DESC, avg_rating DESC
LIMIT 20
Las redes de fraude exhiben patrones complejos y no obvios. Las consultas de gráficos las detectan de manera eficiente:
// Detect potential fraud rings:
// Multiple accounts sharing same device, IP, or phone
MATCH (account:Account)
-[:USED_DEVICE]->(device:Device)<-[:USED_DEVICE]-
(other:Account),
(account)-[:USED_IP]->(ip:IPAddress)<-[:USED_IP]-
(other)
WHERE account.id <> other.id
AND account.created_at > datetime() - duration('P7D')
RETURN account.id, other.id, device.device_id, ip.address,
count(*) as connection_count
ORDER BY connection_count DESC
LIMIT 100
Estadísticas de detección de fraude:
| Métrica | Antes del gráfico | Después del gráfico |
|---|---|---|
| Tasa de detección | 65% | 93% |
| Tasa de falsos positivos | 8% | 2% |
| Tiempo de investigación por caso | 45 minutos | 5 minutos |
Los gráficos de conocimiento conectan datos estructurados y no estructurados en una red unificada de significado:
// Create a knowledge graph from documents
LOAD CSV WITH HEADERS FROM 'file:///entities.csv' AS row
CREATE (e:Entity {
id: row.id,
name: row.name,
type: row.type,
description: row.description
});
LOAD CSV WITH HEADERS FROM 'file:///relationships.csv' AS row
MATCH (a:Entity {id: row.source_id})
MATCH (b:Entity {id: row.target_id})
CALL apoc.create.relationship(a, row.relationship_type, {
source_document: row.document,
confidence: toFloat(row.confidence)
}) YIELD rel
RETURN count(*);
// Query: "How does Product X relate to Regulation Y?"
MATCH path = shortestPath(
(product:Entity {name: "Product X"})
-[:AFFECTS|REGULATES|DEPENDS_ON|MANUFACTURED_BY*]-
(regulation:Entity {name: "Regulation Y"})
)
RETURN [node IN nodes(path) | {name: node.name, type: node.type}],
[rel IN relationships(path) | type(rel)]
// Find all suppliers that feed into a critical component
MATCH (component:Part {name: "Critical Microchip"})
<-[:PRODUCES]-
(:Factory)
-[:SOURCES_FROM]->
(supplier:Supplier)
-[:SUPPLIED_BY]->
(sub_supplier:Supplier)
WHERE sub_supplier.location IN ["Region_A", "Region_B"]
RETURN component.name, supplier.name, sub_supplier.name,
sub_supplier.location
// Impact analysis: "If Supplier X fails, which products are affected?"
MATCH (failing:Supplier {id: $supplierId})
<-[:SUPPLIED_BY]-*
(factory:Factory)
-[:PRODUCES]->
(product:Product)
RETURN product.name, product.revenue,
count(DISTINCT factory) as factories_affected
ORDER BY product.revenue DESC
La ventaja de rendimiento fundamental de las bases de datos de gráficos nativos: cada nodo apunta directamente a sus vecinos conectados. No se necesitan búsquedas de índice para los recorridos.
Relational Database: Graph Database:
┌────────┐
Find friends: │ Alice │──┐
1. Index lookup on person_id ──┐ │ │ │
2. Index lookup on friend_id ──┤ └────────┘ │
3. Join results ──┤ │ │
4. Fetch friend data ──┤ ┌──▼─────┐ │
│ │ Bob │◄─┘
O(log N) per hop │ │ │
│ └────────┘
│
For 6 degrees of separation: │ O(1) per traversal hop
O(log N × 6) = O(log N) │ O(6) = O(1)
│
│ For 6 hops: 6 pointer dereferences
Para las bases de datos de gráficos distribuidos, la partición es un desafío:
| Estrategia | Descripción | problema |
|---|---|---|
| Corte de borde | Dividir por vértice, los bordes cruzan particiones | Muchos recorridos entre particiones |
| Corte de vértice | Dividido por arista, vértice replicado | Gastos generales de coherencia |
| Partición hash | Hash de vértices a particiones | Aleatorio: localidad pobre |
| División de rango | Partición por valor de propiedad | Sesgar si los datos no son uniformes |
// Property graph with versioned edges
MATCH (employee:Employee {id: $empId})
-[assignment:ASSIGNED_TO]->(project:Project)
WHERE assignment.from_date <= date("2026-01-15")
AND (assignment.to_date IS NULL OR assignment.to_date >= date("2026-01-15"))
RETURN employee.name, project.name, assignment.role
// What was the org structure on Jan 1, 2025?
MATCH (manager:Employee)
-[reports:MANAGES]->(report:Employee)
WHERE reports.effective_from <= date("2025-01-01")
AND (reports.effective_to IS NULL OR reports.effective_to > date("2025-01-01"))
RETURN manager.name, collect(report.name) as reports
from neo4j import GraphDatabase
from graphdatascience import GraphDataScience
gds = GraphDataScience("bolt://localhost:7687", auth=("neo4j", "password"))
# Create in-memory graph
G, result = gds.graph.project(
"myGraph",
["Person", "Company"],
["KNOWS", "WORKS_AT"]
)
# PageRank — find influential people
pagerank = gds.pageRank.stream(G)
influencers = pagerank.sort_values('score', ascending=False).head(10)
# Community detection (Louvain) — find clusters
communities = gds.louvain.stream(G)
Las bases de datos de gráficos modernas (Neo4j 5.x+) admiten búsqueda de gráficos y vectores:
// Find similar products using vector embeddings,
// then traverse the recommendation graph
CALL db.index.vector.queryNodes(
'product-embeddings',
10,
$query_embedding
) YIELD node AS similar_product, score
MATCH (similar_product)-[:ALSO_BOUGHT]->(recommended:Product)
WHERE recommended.rating > 4.0
RETURN recommended.name, recommended.price, score
ORDER BY score DESC, recommended.rating DESC
LIMIT 20
| Anti-patrón | Por qué duele | Mejor enfoque |
|---|---|---|
| Operaciones globales | MATCH (n) WHERE n.property escanea todos los nodos |
Agregar índice: CREAR ÍNDICE PARA (n:Etiqueta) EN (n.property) |
| Recorridos profundos e ilimitados | MATCH (a)-[*]->(b) sin límite |
Especificar profundidad: [*1..5] |
| Supernodos | Un nodo con millones de aristas | Dividase en nodos intermedios, utilice limites de tiempo |
| Falta dirección | Los bordes bidireccionales provocan un doble recorrido | Defina una dirección clara y recorra de manera eficiente |
| Sobrenormalización | Crear nodos para atributos simples | Utilice propiedades de nodo en su lugar |
Las bases de datos de gráficos sobresalen cuando las relaciones son complejas, variables y centrales para el dominio. No reemplazan a las bases de datos relacionales: son una herramienta complementaria para espacios problemáticos específicos.
Mejores prácticas: Comience con Neo4j (el ecosistema más maduro), use Cypher para consultas y combínelo con la búsqueda vectorial para aplicaciones de gráficos impulsadas por IA. Mantenga limitadas las profundidades transversales, indexe las propiedades etiquetadas y supervise el crecimiento del supernodo.
Todavía no hay comentarios aprobados. Las respuestas nuevas pueden esperar moderación.