Prácticas recomendadas para las consultas de gráficos

En este documento, se describen las prácticas recomendadas para optimizar tus consultas de BigQuery Graph.

Comienza el salto de directorio desde los nodos de baja cardinalidad

Para mantener pequeños los conjuntos de resultados intermedios y acelerar la ejecución de las consultas, escribe tus consultas de grafos de modo que el recorrido de la ruta comience desde nodos de menor cardinalidad, independientemente de la dirección del recorrido. Las siguientes instrucciones de MATCH usan filtros de propiedad para reducir la cantidad de nodos iniciales posibles en lugar de calcular todas las coincidencias y, luego, filtrarlas:

MATCH (p:Person {id: 10})-[own:Owns]->(a:Account)
MATCH (a:Account WHERE balance > 10)<-[own:Owns]-(p:Person)

Esto es especialmente importante para las búsquedas de rutas cuantificadas:

MATCH (p:Person {id: 10})-[own:Owns]->{1,3}(a:Account)

Usa la sintaxis ANY o ANY SHORTEST para las verificaciones de conectividad

Las consultas de rutas cuantificadas pueden devolver rutas duplicadas entre los nodos de origen y los nodos de destino. Si tu objetivo es verificar la conectividad y no necesitas todas las rutas posibles, usa ANY o ANY SHORTEST para reducir los cálculos redundantes y mejorar la eficiencia de la búsqueda de rutas. Por ejemplo, la siguiente sentencia MATCH usa ANY SHORTEST para conservar solo una ruta entre cada par de nodos:

MATCH ANY SHORTEST (a1:Account)-[t:Transfers]->{1,3}(a2:Account)

Usa el recorrido direccional de la ruta

Los esquemas de grafos de BigQuery son direccionales, lo que significa que cada borde tiene un nodo de origen y un nodo de destino. Si bien la sintaxis de las consultas de grafos permite el recorrido de rutas en cualquier dirección (por ejemplo, -[edge]-), recomendamos usar el recorrido de rutas direccional (por ejemplo, -[edge]-> o <-[edge]-) para obtener un mejor rendimiento. El recorrido de rutas en cualquier dirección puede causar una pérdida de rendimiento.

La siguiente sentencia MATCH usa cualquier salto de directorio de dirección:

-- Avoid.
MATCH (a1:Account {id: 7})-[t:Transfers]-(a2:Account)

En cambio, combina dos recorridos direccionales con UNION ALL:

MATCH (a1:Account {id: 7})-[t:Transfers]->(a2:Account)
...
UNION ALL
...
MATCH (a1:Account  {id: 7})<-[t:Transfers]-(a2:Account)

Especifica etiquetas de forma explícita

Si se omiten las etiquetas de nodos o bordes en una consulta, BigQuery Graph enumera todas las etiquetas de nodos y bordes aptas. Esta enumeración puede hacer que se analicen más etiquetas de las necesarias. Para evitar esto, especifica etiquetas para todos los nodos y bordes de tu consulta siempre que sea posible.

Por ejemplo, la siguiente consulta especifica las etiquetas Account y Transfers:

GRAPH graph_db.FinGraph
MATCH (a1:Account)-[t:Transfers]->(a2:Account)
RETURN COUNT(*) AS num_transfers;

Evita omitir etiquetas, ya que se podrían analizar otras relaciones innecesarias entre los nodos. En la siguiente búsqueda, a1 puede representar una cuenta o una persona, y t puede representar una transferencia o la propiedad de una cuenta.

GRAPH graph_db.FinGraph
MATCH (a1)-[t]->(a2)
RETURN COUNT(*) AS num_transfers;

Prefiere una sola instrucción MATCH

BigQuery Graph te permite incluir varias instrucciones MATCH en una sola consulta de gráfico. Estas instrucciones están conectadas por variables declaradas varias veces que representan el mismo nodo o borde. Sin embargo, usar varias instrucciones MATCH puede reducir los beneficios de la cardinalidad en todas las instrucciones. Cuando sea posible, usa una sola instrucción MATCH para obtener un mejor rendimiento.

Por ejemplo, las siguientes consultas son equivalentes, pero la primera tiene un mejor rendimiento porque usa una sola instrucción MATCH:

-- Preferred syntax.
GRAPH graph_db.FinGraph
MATCH
  (p:Person {id: 1})-[o:Owns]->
  (a:Account)-[t:Transfers]->(a2:Account)
RETURN o.account_id, t.amount;
-- Avoid this syntax.
GRAPH graph_db.FinGraph
MATCH (p:Person {id: 1})-[o:Owns]->(a:Account)
MATCH (a:Account)-[t:Transfers]->(a2:Account)
RETURN o.account_id, t.amount;

Limita los bordes recorridos desde nodos con alta cardinalidad

Cuando consultas gráficos, algunos nodos pueden tener una cantidad significativamente mayor de aristas entrantes o salientes en comparación con otros nodos. Estos nodos de alta cardinalidad a veces se denominan supernodos o nodos centrales. Los supernodos pueden causar problemas de rendimiento, ya que los recorridos a través de ellos pueden implicar el procesamiento de grandes cantidades de datos, lo que genera sesgos de datos y tiempos de ejecución prolongados.

Para optimizar una consulta de un gráfico con supernodos, usa la función ROW_NUMBER() dentro de una cláusula FILTER o WHERE en MATCH para limitar la cantidad de aristas que la consulta atraviesa desde un nodo o hacia él. Esta técnica es especialmente útil cuando no necesitas una enumeración completa de todas las conexiones desde o hacia un supernodo.

Por ejemplo, si algunas cuentas en FinGraph tienen una gran cantidad de transacciones, puedes usar ROW_NUMBER() para limitar la cantidad de aristas Transfers que se deben tener en cuenta para cada Account y evitar una consulta ineficiente:

GRAPH graph_db.FinGraph
MATCH (a1:Account)-[e1:Transfers WHERE e1 IN {
  GRAPH graph_db.FinGraph
  -- Sample 5 edges per source node
  MATCH -[selected_e:Transfers]->
    FILTER ROW_NUMBER() OVER (
      PARTITION BY SOURCE_NODE_ID(selected_e)) < 5
    RETURN selected_e
}]->{1,3}(a2:Account)
RETURN COUNT(*) AS cnt;

Muestrea nodos o bordes intermedios en consultas de múltiples saltos

También puedes mejorar la eficiencia de las consultas usando ROW_NUMBER() para muestrear nodos intermedios en consultas de múltiples saltos. Esta técnica mejora la eficiencia, ya que limita la cantidad de rutas que la búsqueda considera para cada nodo intermedio. Para ello, divide una consulta de múltiples saltos en varias instrucciones MATCH separadas por NEXT y aplica ROW_NUMBER() en el punto medio en el que necesitas realizar el muestreo:

GRAPH graph_db.FinGraph
MATCH (a1:Account)-[e1:Transfers]->(a2:Account)
-- Sample 5 destination nodes per a1 source node
FILTER ROW_NUMBER() OVER (PARTITION BY ELEMENT_ID(a1)) < 5
RETURN a1, a2
NEXT
MATCH (a2)-[e2:Transfers]->(a3:Account)
RETURN a1.id AS src_id, a2.id AS mid_id, a3.id AS dst_id;

¿Qué sigue?