Visão geral da consulta de gráfico

Este documento oferece uma visão geral da linguagem de consulta de gráficos (GQL) e de como escrever consultas de gráficos para o BigQuery Graph. É possível executar consultas de grafo para encontrar padrões, analisar relacionamentos e extrair insights do gráfico de propriedades. Os exemplos neste documento se referem a um gráfico chamado FinGraph, que mostra as relações entre pessoas, contas que elas possuem e transferências entre contas. Para informações sobre a definição do gráfico, consulte o exemplo de FinGraph.

Estrutura da consulta

Uma consulta de gráfico consiste no nome do gráfico, seguido por uma ou mais instruções de consulta linear. Cada consulta linear contém uma ou mais instruções, que permitem trabalhar com dados de gráficos para encontrar correspondências de padrões, definir variáveis, filtrar e transformar dados intermediários e retornar resultados. Você executa uma consulta de gráfico da mesma forma que executa uma consulta SQL no BigQuery.

Exemplo de estrutura de consulta de gráfico.
Exemplo da estrutura de uma consulta de gráfico.

Correspondência de padrões de gráficos

A correspondência de padrões de grafos encontra padrões específicos no seu grafo. Os padrões mais básicos são os de elementos, como os de nós, que correspondem a nós, e os de arestas, que correspondem a arestas.

Padrões de nós

Um padrão de nó corresponde aos nós no seu gráfico. Esse padrão contém parênteses correspondentes, que podem incluir opcionalmente uma variável de padrão de grafo, uma expressão de rótulo e filtros de propriedade.

Encontrar todos os nós

A consulta a seguir retorna todos os nós no gráfico. A variável n, uma variável de padrão de grafo, é vinculada aos nós correspondentes. Nesse caso, o padrão de nó corresponde a todos os nós no gráfico.

GRAPH graph_db.FinGraph
MATCH (n)
RETURN LABELS(n) AS label, n.id;

Essa consulta retorna label e id:

label id
Account 7
Account 16
Account 20
Person 1
Person 2
Person 3

Encontrar todos os nós com um rótulo específico

A consulta a seguir corresponde a todos os nós no gráfico que têm o rótuloPerson. A consulta retorna o rótulo e algumas propriedades dos nós correspondentes.

GRAPH graph_db.FinGraph
MATCH (p:Person)
RETURN LABELS(p) AS label, p.id, p.name;

Essa consulta retorna as seguintes propriedades dos nós correspondentes:

label id name
Person 1 Alex
Person 2 Dana
Person 3 Lee

Encontrar todos os nós que correspondem a uma expressão de rótulo

É possível criar uma expressão de rótulo com um ou mais operadores lógicos. Por exemplo, a consulta a seguir corresponde a todos os nós no gráfico que têm o rótulo Person ou Account. A variável de padrão de grafo n expõe todas as propriedades de nós com o rótulo Person ou Account.

GRAPH graph_db.FinGraph
MATCH (n:Person|Account)
RETURN LABELS(n) AS label, n.id, n.birthday, n.create_time;

Observe o seguinte nos resultados desta consulta:

  • Todos os nós têm a propriedade id.
  • Os nós que correspondem ao rótulo Account têm a propriedade create_time, mas não têm a propriedade birthday. A propriedade birthday é NULL para esses nós.
  • Os nós que correspondem ao rótulo Person têm a propriedade birthday, mas não têm a propriedade create_time. A propriedade create_time é NULL para esses nós.
label id birthday create_time
Account 7 NULL 2020-01-10T14:22:20.222Z
Account 16 NULL 2020-01-28T01:55:09.206Z
Account 20 NULL 2020-02-18T13:44:20.655Z
Person 1 1991-12-21T08:00:00Z NULL
Person 2 1980-10-31T08:00:00Z NULL
Person 3 1986-12-07T08:00:00Z NULL

Encontrar todos os nós que correspondem à expressão de rótulo e ao filtro de propriedade

A consulta a seguir corresponde a todos os nós no gráfico que têm o rótulo Person e a propriedade id igual a 1:

GRAPH graph_db.FinGraph
MATCH (p:Person {id: 1})
RETURN LABELS(p) AS label, p.id, p.name, p.birthday;

O resultado será semelhante ao seguinte:

label id name birthday
Person 1 Alex 1991-12-21T08:00:00Z

Você pode usar a cláusula WHERE para formar condições de filtragem mais complexas em rótulos e propriedades.

A consulta a seguir usa a cláusula WHERE para filtrar nós em que a propriedade birthday está antes de 1990-01-10:

GRAPH graph_db.FinGraph
MATCH (p:Person WHERE p.birthday < '1990-01-10')
RETURN LABELS(p) AS label, p.name, p.birthday;

O resultado será semelhante ao seguinte:

label name birthday
Person Dana 1980-10-31T08:00:00Z
Person Lee 1986-12-07T08:00:00Z

Padrões de borda

Um padrão de aresta corresponde a arestas ou relações entre nós. Os padrões de borda são delimitados por colchetes ([]) e incluem símbolos como -, -> ou <- para indicar direções. Um padrão de aresta pode incluir opcionalmente uma variável de padrão de gráfico para vincular a arestas correspondentes.

Encontrar todas as arestas com rótulos correspondentes

Essa consulta retorna todas as arestas no gráfico com o rótulo Transfers. A consulta vincula a variável de padrão de gráfico e às arestas correspondentes.

GRAPH graph_db.FinGraph
MATCH -[e:Transfers]->
RETURN e.Id as src_account, e.order_number;

O resultado será semelhante ao seguinte:

src_account order_number
7 304330008004315
7 304120005529714
16 103650009791820
20 304120005529714
20 302290001255747

Encontrar todas as arestas que correspondem à expressão de rótulo e ao filtro de propriedade

O padrão de aresta na consulta a seguir usa uma expressão de rótulo e um filtro de propriedade para encontrar todas as arestas rotuladas como Transfers que correspondem a um número de pedido especificado:

GRAPH graph_db.FinGraph
MATCH -[e:Transfers {order_number: "304120005529714"}]->
RETURN e.Id AS src_account, e.order_number;

O resultado será semelhante ao seguinte:

src_account order_number
7 304120005529714
20 304120005529714

Encontrar todas as bordas usando qualquer padrão de borda de direção

É possível usar o padrão de aresta any direction (-[]-) em uma consulta para corresponder a arestas em qualquer direção. A consulta a seguir encontra todas as transferências com uma conta bloqueada:

GRAPH graph_db.FinGraph
MATCH (account:Account)-[transfer:Transfers]-(:Account {is_blocked:true})
RETURN transfer.order_number, transfer.amount;

O resultado será semelhante ao seguinte:

order_number amount
304330008004315 300
304120005529714 100
103650009791820 300
302290001255747 200

Padrões de caminho

Um padrão de caminho é criado com base em padrões alternados de nó e aresta.

Encontrar todos os caminhos de um nó específico usando um padrão de caminho

A consulta a seguir encontra todas as transferências para uma conta iniciada de uma conta de propriedade de Person com id igual a 2.

Cada resultado correspondente representa um caminho de um nó Person quando id é igual a 2 por um nó Account conectado usando uma aresta Owns, em outro nó Account usando uma aresta Transfers.

GRAPH graph_db.FinGraph
MATCH
  (p:Person {id: 2})-[:Owns]->(account:Account)-[t:Transfers]->
  (to_account:Account)
RETURN
  p.id AS sender_id, account.id AS from_id, to_account.id AS to_id;

O resultado será semelhante ao seguinte:

sender_id from_id to_id
2 20 7
2 20 16

Padrões de caminho quantificados

Um padrão quantificado repete um padrão dentro de um intervalo especificado.

Corresponder a um padrão de aresta quantificado

Para encontrar caminhos de comprimento variável, aplique um quantificador a um padrão de aresta. A consulta a seguir demonstra isso ao encontrar contas de destino que estão de uma a três transferências de distância de uma Account de origem com um valor id de 7.

A consulta aplica o quantificador {1, 3} ao padrão de borda -[e:Transfers]->. Isso instrui a consulta a corresponder a caminhos que repetem o padrão de aresta Transfers uma, duas ou três vezes. A cláusula WHERE é usada para excluir a conta de origem dos resultados. A função ARRAY_LENGTH é usada para acessar o e group variable.

GRAPH graph_db.FinGraph
MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
WHERE src != dst
RETURN src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length, dst.id AS dst_account_id;

O resultado será semelhante ao seguinte:

src_account_id path_length dst_account_id
7 1 16
7 1 16
7 3 16
7 3 16
7 2 20
7 2 20

Algumas linhas nos resultados são repetidas. Isso ocorre porque vários caminhos que correspondem ao padrão podem existir entre os mesmos nós de origem e destino, e a consulta retorna todos eles.

Corresponder a um padrão de caminho quantificado

A consulta a seguir encontra caminhos entre nós Account com uma a duas arestas Transfers por contas intermediárias bloqueadas.

O padrão de caminho entre parênteses é quantificado, e a cláusula WHERE especifica condições para o padrão repetido.

GRAPH graph_db.FinGraph
MATCH
  (src:Account)
  ((a:Account)-[:Transfers]->(b:Account {is_blocked:true}) WHERE a != b){1,2}
    -[:Transfers]->(dst:Account)
RETURN src.id AS src_account_id, dst.id AS dst_account_id;

O resultado será semelhante ao seguinte:

src_account_id dst_account_id
7 20
7 20
20 20

Variáveis de grupo

Uma variável de padrão de gráfico declarada em um padrão quantificado se torna uma variável de grupo quando acessada fora desse padrão. Em seguida, ele se vincula a uma matriz de elementos de gráfico correspondentes.

É possível acessar uma variável de grupo como uma matriz. Os elementos do gráfico são preservados na ordem em que aparecem nos caminhos correspondentes. É possível agregar uma variável de grupo usando a agregação horizontal.

Variável de grupo de acesso

No exemplo a seguir, a variável e é acessada da seguinte maneira:

  • Como uma variável de padrão de gráfico vinculada a uma única aresta na cláusula WHEREe.amount > 100 quando está dentro do padrão quantificado.
  • Como uma variável de grupo vinculada a uma matriz de elementos de borda em ARRAY_LENGTH(e) na instrução RETURN quando está fora do padrão quantificado.
  • Como uma variável de grupo vinculada a uma matriz de elementos de borda, que é agregada por SUM(e.amount) fora do padrão quantificado. Este é um exemplo de agregação horizontal.
GRAPH graph_db.FinGraph
MATCH
  (src:Account {id: 7})-[e:Transfers WHERE e.amount > 100]->{0,2}
  (dst:Account)
WHERE src.id != dst.id
LET total_amount = SUM(e.amount)
RETURN
  src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length,
  total_amount, dst.id AS dst_account_id;

O resultado será semelhante ao seguinte:

src_account_id path_length total_amount dst_account_id
7 1 300 16
7 2 600 20

Prefixos de pesquisa de caminho

Para limitar os caminhos correspondentes em grupos que compartilham nós de origem e destino, use o prefixo de pesquisa de caminho ANY, ANY SHORTEST ou ANY CHEAPEST. Só é possível aplicar esses prefixos antes de um padrão de caminho inteiro, e não dentro de parênteses.

Correspondência usando ANY

A consulta a seguir encontra todas as contas únicas acessíveis que estão a uma ou duas Transfers de distância de um determinado nó Account.

O prefixo de pesquisa de caminho ANY garante que a consulta retorne apenas um caminho entre um par exclusivo de nós src e dst Account. No exemplo a seguir, embora seja possível alcançar o nó Account com {id: 16} em dois caminhos diferentes do nó de origem Account, a consulta retorna apenas um caminho.

GRAPH graph_db.FinGraph
MATCH ANY (src:Account {id: 7})-[e:Transfers]->{1,2}(dst:Account)
LET ids_in_path = ARRAY_CONCAT(ARRAY_AGG(e.Id), [dst.Id])
RETURN src.id AS src_account_id, dst.id AS dst_account_id, ids_in_path;

O resultado será semelhante ao seguinte:

src_account_id dst_account_id ids_in_path
7 16 7,16
7 20 7,16,20

Correspondência usando ANY SHORTEST

O prefixo de pesquisa de caminho ANY SHORTEST retorna um único caminho para cada par de nós de origem e destino, selecionados entre aqueles com o número mínimo de arestas.

Por exemplo, a consulta a seguir encontra um dos caminhos mais curtos entre um nó Account com valor id de 7 e um nó Account com valor id de 20. A consulta considera caminhos com uma a três arestas Transfers.

GRAPH graph_db.FinGraph
MATCH ANY SHORTEST (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account {id: 20})
RETURN src.id AS src_account_id, dst.id AS dst_account_id, ARRAY_LENGTH(e) AS path_length;

O resultado será semelhante ao seguinte:

src_account_id dst_account_id path_length
7 20 2

Correspondência usando ANY CHEAPEST

O prefixo de pesquisa de caminho ANY CHEAPEST garante que, para cada par de contas de origem e destino, a consulta retorne apenas um caminho com o custo total mínimo de computação.

A consulta a seguir encontra um caminho com o custo total mínimo de computação entre os nós Account. Esse custo é baseado na soma da propriedade amount das arestas Transfers. A pesquisa considera caminhos com uma a três arestas Transfers.

GRAPH graph_db.FinGraph
MATCH ANY CHEAPEST (src:Account)-[e:Transfers COST e.amount]->{1,3}(dst:Account)
LET total_cost = sum(e.amount)
RETURN src.id AS src_account_id, dst.id AS dst_account_id, total_cost;

O resultado será semelhante ao seguinte:

src_account_id dst_account_id total_cost
7 7 900
7 16 100
7 20 400
16 7 800
16 16 500
16 20 300
20 7 500
20 16 200
20 20 500

Padrões de gráficos

Um padrão de grafo consiste em um ou mais padrões de caminho, separados por uma vírgula (,). Os padrões de grafo podem conter uma cláusula WHERE, que permite acessar todas as variáveis de padrão de grafo nos padrões de caminho para formar condições de filtro. Cada padrão de caminho produz uma coleção de caminhos.

Fazer correspondência usando um padrão de gráfico

A consulta a seguir identifica contas intermediárias e os proprietários envolvidos em transações com valores superiores a 200, em que os fundos são transferidos de uma conta de origem para uma conta bloqueada.

Os seguintes padrões de caminho formam o padrão de grafo:

  • O primeiro padrão encontra caminhos em que a transferência ocorre de uma conta para uma conta bloqueada usando uma conta intermediária.
  • O segundo padrão encontra caminhos de uma conta para a pessoa proprietária.

A variável interm atua como um link comum entre os dois padrões de caminho, o que exige que interm faça referência ao mesmo nó de elemento em ambos os padrões. Isso cria uma operação de junção igual com base na variável interm.

GRAPH graph_db.FinGraph
MATCH
  (src:Account)-[t1:Transfers]->(interm:Account)-[t2:Transfers]->(dst:Account),
  (interm)<-[:Owns]-(p:Person)
WHERE dst.is_blocked = TRUE AND t1.amount > 200 AND t2.amount > 200
RETURN
  src.id AS src_account_id, dst.id AS dst_account_id,
  interm.id AS interm_account_id, p.id AS owner_id;

O resultado será semelhante ao seguinte:

src_account_id dst_account_id interm_account_id owner_id
20 16 7 1

Instruções de consulta linear

É possível encadear várias instruções de gráfico para formar uma instrução de consulta linear. As instruções são executadas na mesma ordem em que aparecem na consulta.

  • Cada instrução usa a saída da instrução anterior como entrada. A entrada está vazia para a primeira instrução.

  • A saída da última instrução é o resultado final.

Por exemplo, você pode usar instruções de consulta linear para encontrar a transferência máxima para uma conta bloqueada. A consulta a seguir encontra a conta e o proprietário com a maior transferência de saída para uma conta bloqueada.

GRAPH graph_db.FinGraph
MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account {is_blocked:true})
ORDER BY transfer.amount DESC
LIMIT 1
MATCH (src_account:Account)<-[owns:Owns]-(owner:Person)
RETURN src_account.id AS account_id, owner.name AS owner_name;

A tabela a seguir ilustra esse processo mostrando os resultados intermediários transmitidos entre cada instrução. Por praticidade, apenas algumas propriedades são mostradas.

Afirmação Resultado intermediário (abreviado)
MATCH
  (src_account:Account)
    -[transfer:Transfers]->
  (dst_account:Account {is_blocked:true})
src_account transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}
{id: 7} {amount: 100.0} {id: 16, is_blocked: true}
{id: 20} {amount: 200.0} {id: 16, is_blocked: true}

ORDER BY transfer.amount DESC
src_account transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}
{id: 20} {amount: 200.0} {id: 16, is_blocked: true}
{id: 7} {amount: 100.0} {id: 16, is_blocked: true}

LIMIT 1
src_account transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}

MATCH
  (src_account:Account)
    <-[owns:Owns]-
  (owner:Person)
src_account transfer dst_account owns proprietário
{id: 7} {amount: 300.0} {id: 16, is_blocked: true} {person_id: 1, account_id: 7} {id: 1, name: Alex}
RETURN
  src_account.id AS account_id,
  owner.name AS owner_name
        
account_id owner_name
7 Alex

O resultado será semelhante ao seguinte:

account_id owner_name
7 Alex

Instrução de retorno

A instrução RETURN especifica o que retornar dos padrões correspondentes. Ele pode acessar variáveis de padrão de grafo e incluir expressões e outras cláusulas, como ORDER BY e GROUP BY.

O BigQuery Graph não oferece suporte ao retorno de elementos de gráfico como resultados de consultas. Para retornar o elemento de gráfico inteiro, use a função TO_JSON.

Retornar elementos de gráfico como JSON

GRAPH graph_db.FinGraph
MATCH (n:Account {id: 7})
-- Returning a graph element in the final results is NOT allowed. Instead, use
-- the TO_JSON function or explicitly return the graph element's properties.
RETURN TO_JSON(n) AS n;

O resultado será semelhante ao seguinte:

n
{"identifier":"mUZpbkdyYXBoLkFjY291bnQAeJEO","kind":"node","labels":["Account"],"properties":{"create_time":"2020-01-10T14:22:20.222Z","id":7,"is_blocked":false,"nick_name":"Vacation Fund"}}

Escrever consultas maiores com a palavra-chave NEXT

É possível encadear várias instruções de consulta linear de gráfico usando a palavra-chave NEXT. A primeira instrução recebe uma entrada vazia, e a saída de cada instrução subsequente se torna a entrada para a próxima.

O exemplo a seguir encontra o proprietário da conta com mais transferências recebidas encadeando várias instruções lineares de gráfico. É possível usar a mesma variável, por exemplo, account, para se referir ao mesmo elemento de gráfico em várias instruções lineares.

GRAPH graph_db.FinGraph
MATCH (:Account)-[:Transfers]->(account:Account)
RETURN account, COUNT(*) AS num_incoming_transfers
GROUP BY account
ORDER BY num_incoming_transfers DESC
LIMIT 1

NEXT

MATCH (account:Account)<-[:Owns]-(owner:Person)
RETURN account.id AS account_id, owner.name AS owner_name, num_incoming_transfers;

O resultado será semelhante ao seguinte:

account_id owner_name num_incoming_transfers
16 Lee 3

Também é possível combinar instruções de consulta linear com operadores de conjunto.

Funções e expressões

É possível usar todas as funções (agregadas e escalares), os operadores e as expressões condicionais do GoogleSQL em consultas de gráficos. O BigQuery Graph também oferece suporte a funções GQL e operadores GQL que só podem ser usados em consultas de gráficos.

A consulta a seguir inclui uma combinação de funções e operadores de GQL e SQL em uma consulta de gráfico:

GRAPH graph_db.FinGraph
MATCH (person:Person)-[o:Owns]->(account:Account)
WHERE person IS SOURCE OF o
RETURN person, ARRAY_AGG(account.nick_name) AS accounts
GROUP BY person

NEXT

RETURN
  LABELS(person) AS labels,
  accounts,
  CONCAT(person.city, ", ", person.country) AS location,
  TO_JSON(person) AS person
LIMIT 1;

O resultado será semelhante ao seguinte:

rótulos contas local pessoa
Person ["Vacation Fund"] Adelaide, Australia {"identifier":"mUZpbkdyYXBoLlBlcnNvbgB4kQI=","kind":"node","labels":["Person"],"properties":{"birthday":"1991-12-21T08:00:00Z","city":"Adelaide","country":"Australia","id":1,"name":"Alex"}}

Subconsultas

Uma subconsulta é uma consulta aninhada em outra. As regras a seguir se aplicam a subconsultas em consultas de gráficos:

  • Uma subconsulta é incluída entre um par de chaves {}.
  • Uma subconsulta começa com uma cláusula GRAPH para especificar o gráfico no escopo. O gráfico especificado não precisa ser o mesmo usado na consulta externa.
  • Uma variável de padrão de grafo declarada fora do escopo da subconsulta não pode ser declarada novamente dentro dela, mas pode ser referenciada em expressões ou funções dentro da subconsulta.

Usar uma subconsulta para encontrar o número total de transferências de cada conta

A consulta a seguir ilustra o uso da subconsulta VALUE. A subconsulta é colocada entre chaves {} prefixadas pela palavra-chave VALUE. A consulta retorna o número total de transferências iniciadas de uma conta.

GRAPH graph_db.FinGraph
MATCH (p:Person)-[:Owns]->(account:Account)
RETURN p.name, account.id AS account_id, VALUE {
  GRAPH graph_db.FinGraph
  MATCH (a:Account)-[transfer:Transfers]->(:Account)
  WHERE a = account
  RETURN COUNT(transfer) AS num_transfers
} AS num_transfers;

O resultado será semelhante ao seguinte:

nome account_id num_transfers
Alex 7 2
Dana 20 2
Lee 16 1

Para conferir uma lista de expressões de subconsultas compatíveis, consulte Subconsultas de gráficos do BigQuery.

Parâmetros de consulta

É possível consultar o BigQuery Graph com parâmetros. Para mais informações, consulte a sintaxe e saiba como executar consultas parametrizadas.

A consulta a seguir corresponde a nós Person que têm um valor id correspondente a um parâmetro de consulta:

GRAPH graph_db.FinGraph
MATCH (person:Person {id: @id})
RETURN person.name;

Consultar gráficos e tabelas juntos

É possível combinar consultas de gráficos e consultas SQL usando o operador GRAPH_TABLE.

O operador GRAPH_TABLE usa uma consulta de gráfico linear e retorna o resultado em formato tabular que pode ser integrado a uma consulta SQL. Essa interoperabilidade permite enriquecer os resultados de consultas de gráficos com conteúdo não gráfico e vice-versa.

Por exemplo, você pode criar uma tabela CreditReports e inserir alguns relatórios de crédito, como mostrado no exemplo a seguir:

CREATE TABLE graph_db.CreditReports (
  person_id     INT64 NOT NULL,
  create_time   TIMESTAMP NOT NULL,
  score         INT64 NOT NULL,
  PRIMARY KEY (person_id, create_time) NOT ENFORCED
);
INSERT INTO graph_db.CreditReports (person_id, create_time, score)
VALUES
  (1,"2020-01-10 06:22:20.222", 700),
  (2,"2020-02-10 06:22:20.222", 800),
  (3,"2020-03-10 06:22:20.222", 750);

Em seguida, é possível identificar pessoas específicas usando a correspondência de padrões de gráficos em GRAPH_TABLE e combinar os resultados da consulta de gráficos com a tabela CreditReports para recuperar as pontuações de crédito.

SELECT
  gt.person.id,
  credit.score AS latest_credit_score
FROM GRAPH_TABLE(
  graph_db.FinGraph
  MATCH (person:Person)-[:Owns]->(:Account)-[:Transfers]->(account:Account {is_blocked:true})
  RETURN DISTINCT person
) AS gt
JOIN graph_db.CreditReports AS credit
  ON gt.person.id = credit.person_id
ORDER BY credit.create_time;

O resultado será semelhante ao seguinte:

person_id latest_credit_score
1 700
2 800

A seguir