DATTAX — Catálogo de Algoritmos de Grafo (Neo4j GDS)
Descobrir quem é central numa rede, que grupos existem ou qual o caminho mais curto entre duas entidades costumava exigir escrever procedures Cypher e conhecer a biblioteca Graph Data Science por dentro. No DATTAX, cada algoritmo é um operador de pipeline: você projeta o grafo, encadeia o algoritmo com |> e recebe o resultado como tabela — pronto para filtrar, juntar e materializar como qualquer outro dado.
Esta página é o catálogo documentado que o próprio motor usa: a lista de algoritmos, as opções de cada um (com tipo, padrão e obrigatoriedade) e as colunas que ele devolve vêm da versão da GDS instalada na plataforma — a que acompanha a própria imagem do Neo4j, na versão do servidor (hoje a 2026.09.0, edição Community) —, e o que cada algoritmo faz, quando usar e o exemplo são texto da plataforma. O mesmo catálogo alimenta o assistente do chat quando ele escolhe um algoritmo.
Todos os algoritmos executam em modo de leitura. As opções WRITE_PROPERTY e MUTATE_PROPERTY são proibidas — o DATTAX nunca escreve no grafo durante pipelines, então sua base original fica intacta.
Os resultados ficam em cache por 30 minutos, pela definição da projeção: repetir o mesmo algoritmo sobre a mesma projeção responde em segundos.
Sintaxe genérica
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (n:Empresa) RETURN id(n) AS id" RELS "MATCH (a:Empresa)-[:TIPO]->(b:Empresa) RETURN id(a) AS source, id(b) AS target"
|> <ALGORITMO> [ON <grafo>] [OPCAO=valor, OPCAO=valor] ;Quando ON <grafo> é omitido, o algoritmo usa a última projeção registrada na execução; a projeção é descartada ao fim. Propriedade de nó usada pelo algoritmo sai como coluna do NODES (RETURN id(n) AS id, n.capitalSocial AS capital); peso de aresta, como coluna do RELS.
Também há uma forma genérica para qualquer algoritmo do catálogo:
|> GDS <ALGORITMO> [ON <grafo>] [OPCAO=valor, ...]Opções: cada algoritmo aceita só as suas
- A opção DATTAX é a chave da GDS em maiúsculas com sublinhado:
dampingFactorviraDAMPING_FACTOR,seedSetSizeviraSEED_SET_SIZE. Os nomes históricosITER(maxIterations),DAMPING(dampingFactor) eRELATIONSHIP_WEIGHT(relationshipWeightProperty) continuam valendo onde o algoritmo tem a chave. - Opções vão separadas por vírgula. Texto entre aspas duplas; lista como texto separado por vírgula (
TARGET_NODES="12,57,98"); mapa como"chave:valor,chave:valor". - Opção que o algoritmo não tem e opção obrigatória ausente são recusadas antes de ir ao banco, com a lista de opções aceitas e a forma do algoritmo — cada seção abaixo diz quais são obrigatórias.
BFS,DFSeALL_SHORTEST_PATHSrecebem o nó de origem peloFROM(o elementId do nó); nas formas genéricas,SOURCE_NODErecebe o id interno do nó, o mesmoid(n)da projeção.
Exemplo prático — quais empresas são mais influentes na rede de sócios?
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (e:Empresa) RETURN id(e) AS id" RELS "MATCH (e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa) WHERE id(e1) < id(e2) RETURN id(e1) AS source, id(e2) AS target"
|> PAGERANK ON rede ITER=20, DAMPING=0.85
|> ORDER BY score DESC
|> LIMIT 20 ;O resultado chega como tabela com as 20 empresas de maior influência — pronto para virar um painel ou ser materializado.
Edição Community instalada
A GDS instalada é a edição Community, e ela tem todos os algoritmos desta página — Leiden, SLLPA, HITS, HDBSCAN, Bellman-Ford, pontes e pontos de articulação inclusive. O que a licença Enterprise mudaria são limites de execução: na Community, cada algoritmo usa no máximo 4 threads (CONCURRENCY acima disso é recusado) e o catálogo de modelos treinados (GraphSAGE) guarda até 3 modelos, só em memória — eles somem quando o servidor reinicia.
<!-- gerado:gds-catalogo:inicio -->
Centralidade
PAGERANK
Dá a cada nó uma nota de relevância que cresce quando ele recebe ligações de outros nós também relevantes.
O que faz: Todo nó começa com uma parcela fixa de relevância, igual a 1 - DAMPINGFACTOR, e a cada rodada repassa o que acabou de ganhar aos vizinhos para onde suas arestas apontam, dividido entre eles (com peso, na proporção do peso de cada aresta). Assim a nota se acumula em quem é apontado por muitos, ou por poucos mas importantes. As rodadas param quando nenhuma nota varia mais que TOLERANCE ou quando se atinge MAXITERATIONS. Com SOURCE_NODES, só os nós de partida recebem a parcela fixa, e a nota passa a medir o quanto cada nó está ligado a eles (versão personalizada).
Quando usar: Use quando a relação tem sentido e a importância deve correr por ele, como em quem controla ou investe em quem. Prefira-o à simples contagem de ligações (DEGREE) quando importa de onde elas vêm, e ao EIGENVECTOR quando a rede é direcionada ou tem nós sem saída, que o amortecimento absorve; com SOURCE_NODES ele responde quem está mais ligado a um ponto de partida.
Em linguagem de negócio:
- Quais holdings estão no topo das cadeias de controle societário?
- Quais grupos econômicos concentram mais poder de controle, pesando também a importância das empresas que comandam?
- Partindo de uma empresa investigada, quais outras estão mais amarradas a ela pela teia de participações?
- Quais controladores finais devem ser priorizados numa fiscalização por comandarem empresas que também comandam outras?
- Quais empresas ganham destaque por receberem vínculos de empresas que também são destaque?
Como usar: Projete no RELS a aresta no sentido em que a relevância deve correr (no exemplo, da investida para a empresa sócia, para que a nota se acumule em quem controla); se a relação for de mão dupla, devolva os dois sentidos. As opções mais usadas são DAMPINGFACTOR (padrão 0.85; aceita DAMPING), MAXITERATIONS (padrão 20; aceita ITER), TOLERANCE, RELATIONSHIPWEIGHTPROPERTY com o nome de uma coluna numérica do RELS, SOURCE_NODES com o id interno de um nó de partida e SCALER (por exemplo "MinMax") para normalizar as notas; nenhuma é obrigatória. A saída traz nodeId, o id interno que o NODES devolveu, e score: ordene por score decrescente e leia as primeiras linhas como as mais relevantes.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH controle
NODES "MATCH (dona:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(investida:Empresa) UNWIND [dona, investida] AS e RETURN DISTINCT id(e) AS id"
RELS "MATCH (dona:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(investida:Empresa) RETURN DISTINCT id(investida) AS source, id(dona) AS target"
|> PAGERANK ON controle DAMPING_FACTOR=0.85, MAX_ITERATIONS=30
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: O score não é porcentagem nem soma 1: quem não recebe nenhuma ligação fica com 1 - DAMPINGFACTOR (0,15 no padrão), e com SOURCENODES quem não é alcançado a partir dos pontos de partida fica com zero. Com peso, use valores positivos, porque a parte de cada aresta é proporcional a ele. O custo cresce com o tamanho da projeção e com MAX_ITERATIONS; em bases grandes, recorte o NODES e o RELS pelo mesmo filtro (município, CNAE), porque a projeção recusa aresta cujo nó não veio no NODES.
Parâmetros:
DAMPING_FACTOR→dampingFactor(Float; padrão0.85)MAX_ITERATIONS→maxIterations(Integer; padrão20)TOLERANCE→tolerance(Float; padrão0.0000001)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SOURCE_NODES→sourceNodes(Node/Number or List or List of pairs as lists; padrão[])SCALER→scaler(String or Map)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
ARTICLE_RANK
Variante do PageRank que atenua o peso das ligações vindas de nós que apontam para poucos vizinhos, para que vínculos exclusivos não inflem a nota.
O que faz: Funciona como o PageRank: todo nó parte de 1 - DAMPING_FACTOR e, a cada rodada, repassa o que ganhou aos vizinhos para onde aponta. A diferença está na divisão: em vez de dividir só pelo número de saídas, divide por esse número somado ao grau médio da rede, o que reduz muito a parte que um nó de poucas saídas entrega a cada vizinho e quase não mexe na de quem tem muitas. Com isso, receber a única ligação de alguém deixa de valer tanto, e pesa mais ser apontado por muitos.
Quando usar: Use em relações do tipo citação ou investimento, em que quem aponta para um único destino (uma holding de uma só participação, um sócio de uma só empresa) não deve transferir a ele toda a sua relevância. Se esse vínculo exclusivo deve contar por inteiro, prefira o PAGERANK.
Em linguagem de negócio:
- Quais empresas recebem participação de vários grupos societários, sem que uma holding de participação única distorça a lista?
- Que empresas são destino de muitos investimentos de outras empresas relevantes, pesando menos quem investe só nelas?
- Quais empresas são as participações mais disputadas pelos grupos econômicos?
- Quais empresas ganham relevância pelo apoio de muitas fontes diferentes, e não por um único vínculo exclusivo?
- Onde estão as empresas que atraem capital de grupos que também investem em outras empresas importantes?
Como usar: Projete no RELS a aresta no sentido de quem aponta para quem recebe (no exemplo, da empresa sócia para a investida). As opções são as mesmas do PageRank: DAMPINGFACTOR (padrão 0.85; aceita DAMPING), MAXITERATIONS (padrão 20; aceita ITER), TOLERANCE, RELATIONSHIPWEIGHTPROPERTY, SOURCE_NODES e SCALER; nenhuma é obrigatória. A saída traz nodeId (o id interno que o NODES devolveu) e score: ordene por score decrescente e use SCALER="MinMax" quando quiser as notas entre 0 e 1.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH participacoes
NODES "MATCH (socia:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(investida:Empresa) UNWIND [socia, investida] AS e RETURN DISTINCT id(e) AS id"
RELS "MATCH (socia:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(investida:Empresa) RETURN DISTINCT id(socia) AS source, id(investida) AS target"
|> ARTICLE_RANK ON participacoes MAX_ITERATIONS=20, SCALER="MinMax"
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: Sem SCALER, as notas saem em geral menores que as do PageRank sobre a mesma projeção, porque o denominador é maior; entre os dois algoritmos, compare posições, não valores. Quem não recebe ligação fica com 1 - DAMPING_FACTOR. O grau médio usado na divisão é o da projeção inteira, então recortes diferentes (outro município, outro CNAE) mudam a força da correção.
Parâmetros:
DAMPING_FACTOR→dampingFactor(Float; padrão0.85)MAX_ITERATIONS→maxIterations(Integer; padrão20)TOLERANCE→tolerance(Float; padrão0.0000001)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SOURCE_NODES→sourceNodes(Node/Number or List or List of pairs as lists; padrão[])SCALER→scaler(String or Map)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
EIGENVECTOR
Mede a influência de cada nó pela influência dos vizinhos: estar ligado a nós bem conectados vale mais do que ter muitas ligações com nós isolados.
O que faz: Parte de uma nota igual para todos (ou só para os SOURCENODES) e, a cada rodada, cada nó soma à própria nota as notas que recebe pelas arestas de entrada; com RELATIONSHIPWEIGHTPROPERTY, cada vizinho reparte a sua nota na proporção do peso de cada aresta. Depois de cada rodada as notas são normalizadas para que a soma dos quadrados seja 1, e o processo termina quando a mudança fica abaixo de TOLERANCE ou em MAXITERATIONS. Não há fator de amortecimento: a nota final reflete apenas a estrutura de ligações.
Quando usar: Use em relações simétricas, como sócios em comum, para achar quem está no núcleo mais denso da rede. Prefira-o ao PAGERANK quando a relação não tem sentido natural e não se quer a parcela fixa do amortecimento, e ao DEGREE quando a qualidade das ligações importa mais que a quantidade.
Em linguagem de negócio:
- Quais empresas estão cercadas de outras empresas também muito conectadas pela rede de sócios?
- Quem está no núcleo mais fechado dos grupos societários da cidade?
- Quais empresas têm influência por associação, por dividirem sócios com empresas influentes?
- Onde está o miolo da teia de sócios em comum de um setor?
- Quais empresas andam na companhia das empresas mais bem relacionadas?
Como usar: Projete a relação nos dois sentidos quando ela for simétrica (no exemplo, empresas de São Paulo que compartilham sócios, com o número de sócios em comum como peso). As opções são MAXITERATIONS (padrão 20; aceita ITER), TOLERANCE, RELATIONSHIPWEIGHTPROPERTY com o nome da coluna de peso do RELS, SOURCENODES e SCALER; nenhuma é obrigatória. A saída traz nodeId e score, que já sai normalizado: ordene por ele e compare posições.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede
NODES "MATCH (m:Municipio {municipioNome: 'SAO PAULO', estadoSigla: 'SP'}) MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(e) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'SAO PAULO', estadoSigla: 'SP'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN id(e1) AS source, id(e2) AS target, count(DISTINCT p) AS peso"
|> EIGENVECTOR ON rede RELATIONSHIP_WEIGHT_PROPERTY="peso", MAX_ITERATIONS=50
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: Em rede partida em vários grupos, a nota tende a se concentrar no grupo mais denso e as empresas dos demais ficam perto de zero, o que não quer dizer que sejam irrelevantes no próprio grupo; compare nós do mesmo grupo ou informe SOURCENODES, que restringe o cálculo à parte da rede alcançável a partir deles. Redes grandes podem precisar de mais que as 20 iterações padrão para estabilizar: aumente MAXITERATIONS. SCALER="L2Norm" é ignorado, porque o resultado já vem nessa escala.
Parâmetros:
MAX_ITERATIONS→maxIterations(Integer; padrão20)TOLERANCE→tolerance(Float; padrão0.0000001)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SOURCE_NODES→sourceNodes(List or Node or Number; padrão[])SCALER→scaler(String or Map)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
BETWEENNESS
Aponta os nós que ficam no meio dos caminhos mais curtos entre outros nós, isto é, os intermediários por onde a rede precisa passar.
O que faz: Para cada nó de partida, o algoritmo percorre a rede em busca dos caminhos mais curtos até todos os demais e, voltando do fim para o começo, credita a cada nó intermediário a fração desses caminhos que passa por ele. A nota final é a soma desses créditos. Com RELATIONSHIPWEIGHTPROPERTY, o caminho mais curto passa a ser o de menor soma de pesos; com SAMPLING_SIZE, só uma amostra de nós de partida é usada (sorteada com preferência pelos mais conectados), o que troca precisão por tempo.
Quando usar: Use para achar pontes e atravessadores: quem liga grupos que, sem ele, ficariam distantes. Difere do DEGREE, que conta ligações, e do CLOSENESS, que mede proximidade de todos: um nó com poucas ligações pode ter nota alta se for a passagem obrigatória entre dois grupos.
Em linguagem de negócio:
- Quais pessoas fazem a ponte entre grupos de construtoras que, sem elas, não teriam sócio em comum?
- Quem são os intermediários por onde passam as ligações entre grupos empresariais distintos?
- Quais sócios controlam o fluxo de relações entre empresas que não se conhecem?
- Que empresas funcionam como elo de passagem entre núcleos societários diferentes?
- Se eu quiser separar dois grupos econômicos, quem são os atravessadores a observar?
Como usar: Projete pessoas e empresas (ou só empresas) com a relação nos dois sentidos, para que o caminho possa ir e voltar (no exemplo, sócios e construtoras de São Paulo). As opções são SAMPLINGSIZE (quantos nós de partida usar; sem ela, todos), SAMPLINGSEED (para repetir a mesma amostra), RELATIONSHIPWEIGHTPROPERTY e CONCURRENCY; nenhuma é obrigatória. A saída traz nodeId e score, a soma das frações de caminhos mais curtos que passam pelo nó: ordene por score decrescente.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH obras
NODES "MATCH (m:Municipio {municipioNome: 'SAO PAULO', estadoSigla: 'SP'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (s)-[:TEM_CNAE]->(c:CNAE) WHERE c.cnaeCodigoprimario STARTS WITH '4120' UNWIND [p, e] AS n RETURN DISTINCT id(n) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'SAO PAULO', estadoSigla: 'SP'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (s)-[:TEM_CNAE]->(c:CNAE) WHERE c.cnaeCodigoprimario STARTS WITH '4120' WITH DISTINCT p, e UNWIND [[p, e], [e, p]] AS par RETURN id(par[0]) AS source, id(par[1]) AS target"
|> BETWEENNESS ON obras SAMPLING_SIZE=2000, SAMPLING_SEED=42
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: É um dos algoritmos mais caros: o tempo cresce com o número de nós vezes o de arestas, então recorte por município ou CNAE e use SAMPLING_SIZE em redes grandes (a edição instalada aceita CONCURRENCY de no máximo 4). O peso é tratado como distância, e quanto maior, mais longe: para usar a força do vínculo como peso, devolva o inverso dela no RELS. Com a relação projetada nos dois sentidos cada par é contado duas vezes, e com amostragem os valores dependem do tamanho da amostra, então compare posições e não números absolutos.
Parâmetros:
SAMPLING_SIZE→samplingSize(Integer; padrãonode count)SAMPLING_SEED→samplingSeed(Integer)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
CLOSENESS
Mede o quão perto cada nó está dos demais, pelo inverso da distância média até os nós do seu grupo.
O que faz: O algoritmo faz buscas em largura a partir de todos os nós ao mesmo tempo, contando em quantos passos cada nó é alcançado e por quantos outros. A nota padrão é o número de nós que o alcançam dividido pela soma dessas distâncias, ou seja, o inverso da distância média dentro do próprio grupo. Com USEWASSERMANFAUST=true, a nota é multiplicada pela fração da rede que esse grupo representa, o que penaliza quem está em grupos pequenos.
Quando usar: Use para achar quem está no centro de uma rede bem conectada, de onde se chega a todos com menos intermediários. Em rede partida em muitos grupos pequenos, ligue USEWASSERMANFAUST ou prefira o HARMONIC, que lida melhor com quem não se alcança.
Em linguagem de negócio:
- Quais empresas alcançam as demais da rede de sócios com menos intermediários?
- De onde uma informação ou prática se espalharia mais rápido por todo o grupo societário?
- Quem está no centro de gravidade das relações entre as empresas da cidade?
- Quais empresas ficam a poucos passos de todas as outras do mesmo grupo econômico?
- Qual empresa é o melhor ponto de observação para acompanhar um grupo inteiro?
Como usar: Projete a relação nos dois sentidos quando ela for simétrica (no exemplo, empresas do Recife ligadas por sócios em comum). As opções são USEWASSERMANFAUST (padrão false; use true em redes fragmentadas) e CONCURRENCY; nenhuma é obrigatória, e o algoritmo ignora pesos, contando só passos. A saída traz nodeId e score: ordene por score decrescente.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede
NODES "MATCH (m:Municipio {municipioNome: 'RECIFE', estadoSigla: 'PE'}) MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(e) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'RECIFE', estadoSigla: 'PE'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> CLOSENESS ON rede USE_WASSERMAN_FAUST=true
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: Sem USEWASSERMANFAUST, um par de empresas isolado do resto recebe a nota máxima (1), porque a distância média dentro dele é 1; em redes fragmentadas isso enche o topo da lista de grupos pequenos. O custo cresce com nós vezes arestas, então recorte a projeção por município ou CNAE (CONCURRENCY no máximo 4 na edição instalada). Em projeção de mão única, a distância é contada no sentido das arestas, a partir de quem chega ao nó.
Parâmetros:
USE_WASSERMAN_FAUST→useWassermanFaust(Boolean; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; sem peso nas ligações.
HARMONIC
Variante da proximidade que soma o inverso das distâncias, de modo que quem não se alcança conta zero e redes fragmentadas não distorcem o resultado.
O que faz: Assim como no CLOSENESS, são feitas buscas em largura a partir de todos os nós ao mesmo tempo. Para cada nó somam-se os inversos das distâncias (um vizinho direto vale 1, um a dois passos vale 1/2, e assim por diante), e o total é dividido pelo número de nós da projeção menos um. Nós que não se alcançam simplesmente não somam nada, sem quebrar a conta.
Quando usar: Use no lugar do CLOSENESS quando a rede tem vários grupos desconectados, o que é comum em quadros societários: a nota continua comparável entre grupos e favorece quem está perto de muitos, e não quem está sozinho num grupo pequeno.
Em linguagem de negócio:
- Mesmo com muitos grupos isolados, quais empresas estão perto do maior número de outras pela rede de sócios?
- Quem tem o maior alcance próximo no mercado local, contando só quem é de fato alcançável?
- Quais empresas conseguiriam influenciar mais empresas em poucos passos de relacionamento?
- Numa rede de sócios espalhada e cheia de ilhas, quem está mais perto de todos?
- Quais empresas estão a um ou dois passos de muitas outras do mesmo setor?
Como usar: Projete a relação nos dois sentidos quando ela for simétrica (no exemplo, empresas de Campinas ligadas por sócios em comum, e só as que têm alguma ligação). A única opção é CONCURRENCY (padrão 4, que também é o máximo na edição instalada); o algoritmo ignora pesos. A saída traz nodeId e score, entre 0 e 1: quanto mais perto de 1, mais perto o nó está de toda a projeção.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede
NODES "MATCH (m:Municipio {municipioNome: 'CAMPINAS', estadoSigla: 'SP'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'CAMPINAS', estadoSigla: 'SP'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> HARMONIC ON rede
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: Como a soma é dividida pelo total de nós da projeção, incluir empresas sem nenhuma ligação reduz as notas de todos (sem mudar a ordem); projete só quem interessa. O custo é o mesmo do CLOSENESS, proporcional a nós vezes arestas, então recorte por município ou CNAE em bases grandes.
Parâmetros:
CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; sem peso nas ligações.
DEGREE
Conta as ligações de cada nó (ou soma os pesos delas) no sentido escolhido.
O que faz: Para cada nó, o algoritmo conta as arestas que saem dele (ORIENTATION="NATURAL"), as que chegam ("REVERSE") ou as duas ("UNDIRECTED"). Com RELATIONSHIPWEIGHTPROPERTY, em vez de contar, soma os pesos positivos dessas arestas. É uma passada única pela projeção, sem iterações.
Quando usar: Use como primeira leitura da rede e para perguntas de quantidade: quem tem mais vínculos. Quando importa a qualidade das ligações, e não só o número, passe para PAGERANK ou EIGENVECTOR.
Em linguagem de negócio:
- Quais pessoas são sócias do maior número de empresas?
- Quais empresas têm o quadro societário mais numeroso?
- Há pessoas que aparecem em empresas demais para serem sócias de verdade, como possíveis laranjas?
- Quais empresas concentram mais participações em outras empresas?
- Quem acumula mais vínculos societários na cidade?
Como usar: Projete a relação com o sentido que dá significado à contagem: no exemplo, de pessoa para empresa em Maceió, então ORIENTATION="NATURAL" conta empresas por pessoa e ORIENTATION="REVERSE" contaria sócios por empresa. As opções são ORIENTATION (padrão NATURAL), RELATIONSHIPWEIGHTPROPERTY e CONCURRENCY; nenhuma é obrigatória. A saída traz nodeId e score, o total de ligações (ou de peso) do nó: ordene por score decrescente.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH quadro
NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) UNWIND [p, e] AS n RETURN DISTINCT id(n) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(p) AS source, id(e) AS target"
|> DEGREE ON quadro ORIENTATION="NATURAL"
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: Arestas repetidas entre o mesmo par contam várias vezes: use DISTINCT no RELS quando a pergunta é quantas empresas diferentes. Com ORIENTATION="UNDIRECTED" sobre uma relação já projetada nos dois sentidos, cada vínculo conta em dobro. O cálculo em si é barato; o custo real está nas consultas do NODES e do RELS, então recorte-as por município ou CNAE em bases grandes.
Parâmetros:
ORIENTATION→orientation(String; padrãoNATURAL)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, score
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
CELF
Escolhe o conjunto de nós que, como ponto de partida, espalharia um efeito pela rede ao maior número de outros nós.
O que faz: O algoritmo simula muitas vezes um contágio em cascata: cada nó atingido tem uma chance fixa (PROPAGATIONPROBABILITY) de atingir cada vizinho para onde aponta. Primeiro estima, para cada nó, quantos seriam atingidos em média a partir dele e escolhe o melhor; depois, até completar SEEDSETSIZE escolhas, acrescenta o nó que mais aumenta o alcance do conjunto já escolhido, reavaliando só os candidatos que ainda podem vencer (a avaliação preguiçosa que dá nome ao método). Cada estimativa é a média de MONTECARLO_SIMULATIONS simulações.
Quando usar: Use quando a pergunta é por onde começar: escolher poucos alvos para que uma ação (fiscalização, aviso, campanha) alcance o máximo de empresas ligadas. Diferente das notas de relevância, ele desconta a sobreposição: um nó que só alcançaria empresas já cobertas pelos escolhidos acrescenta pouco e fica de fora.
Em linguagem de negócio:
- Se só dá para fiscalizar cinco empresas, quais escolher para que o efeito chegue ao maior número de empresas ligadas?
- Por onde começar uma campanha de conformidade para que ela se espalhe mais pela rede de sócios?
- Quais empresas, se autuadas primeiro, provocariam a maior reação em cadeia no grupo?
- Quais poucos alvos cobrem a maior parte da rede sem se sobrepor?
- Onde um problema começaria a se espalhar mais longe pelos vínculos societários?
Como usar: Projete a relação no sentido em que o efeito se propaga, ou nos dois sentidos quando ele corre para ambos os lados, como entre empresas com sócios em comum (no exemplo, em Maceió); projete só quem tem ligação, porque nó isolado nunca espalha. SEEDSETSIZE é obrigatório e diz quantos nós escolher; as demais opções são PROPAGATIONPROBABILITY (padrão 0.1), MONTECARLOSIMULATIONS (padrão 100), RANDOMSEED e CONCURRENCY. A saída traz só os nós escolhidos, com nodeId e spread, o ganho de alcance que cada um acrescentou ao ser escolhido: ordenar por spread decrescente aproxima a ordem de escolha.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede
NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> CELF ON rede SEED_SET_SIZE=5, PROPAGATION_PROBABILITY=0.1, MONTE_CARLO_SIMULATIONS=100, RANDOM_SEED=42
|> ORDER BY spread DESC ;Cuidados: É caro: a primeira etapa simula o contágio a partir de cada nó da projeção, MONTECARLOSIMULATIONS vezes, então recorte bem a rede e comece com poucas simulações (CONCURRENCY no máximo 4 na edição instalada). O spread do primeiro escolhido é o alcance dele sozinho, contando ele próprio; os seguintes mostram só o que acrescentam, e a soma estima o alcance do conjunto. Rodar de novo com outro RANDOM_SEED ajuda a ver se a escolha é estável.
Parâmetros:
SEED_SET_SIZE→seedSetSize(Integer; obrigatório)MONTE_CARLO_SIMULATIONS→monteCarloSimulations(Integer; padrão100)PROPAGATION_PROBABILITY→propagationProbability(Float; padrão0.1)RANDOM_SEED→randomSeed(Integer)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, spread
Grafo: direcionado ou não direcionado; sem peso nas ligações.
HITS
Dá duas notas a cada nó: autoridade (auth), por ser apontado por bons distribuidores de ligações, e hub, por apontar para boas autoridades.
O que faz: Todos começam com as duas notas iguais. A cada rodada, a autoridade de um nó passa a ser a soma das notas de hub de quem aponta para ele e, em seguida, o hub de cada nó passa a ser a soma das autoridades para quem ele aponta; depois de cada cálculo as notas são normalizadas. As rodadas se repetem HITS_ITERATIONS vezes, e esse reforço mútuo separa quem distribui ligações relevantes de quem as recebe.
Quando usar: Use em relações com sentido em que há dois papéis, quem distribui e quem recebe, como sócio e empresa ou holding e investida. Quando basta uma nota única de relevância, prefira o PAGERANK; o HITS exige projeção direcionada.
Em linguagem de negócio:
- Quais sócios espalham participações por empresas que concentram outros sócios de peso?
- Quais empresas reúnem os sócios mais articulados do mercado local?
- Quem são os articuladores que investem justamente nas empresas mais disputadas?
- Quais empresas são referência por terem sócios que também estão nas empresas mais relevantes?
- Quem distribui vínculos para os alvos certos, e quem é o alvo preferido de quem distribui vínculos?
Como usar: Projete a relação num único sentido, de quem aponta para quem recebe (no exemplo, de pessoa para empresa no Recife). As opções são HITSITERATIONS (padrão 20), AUTHPROPERTY e HUB_PROPERTY (nomes das chaves no resultado, auth e hub por padrão), PARTITIONING e CONCURRENCY; nenhuma é obrigatória. A saída traz nodeId e values, um mapa com as duas notas: no exemplo, pessoas têm nota de hub e empresas de autoridade, porque pessoa só aponta e empresa só recebe.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH quadro
NODES "MATCH (m:Municipio {municipioNome: 'RECIFE', estadoSigla: 'PE'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) UNWIND [p, e] AS n RETURN DISTINCT id(n) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'RECIFE', estadoSigla: 'PE'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(p) AS source, id(e) AS target"
|> GDS HITS ON quadro HITS_ITERATIONS=20 ;Cuidados: Projete um único sentido: devolver os dois sentidos mistura os papéis, e todos passam a ser ao mesmo tempo quem aponta e quem recebe. Para calcular as duas notas o algoritmo monta um índice inverso das arestas, que ocupa memória adicional proporcional ao número de arestas; recorte a projeção por município ou CNAE. Quem não recebe nenhuma ligação tem autoridade zero, e quem não aponta para ninguém tem hub zero.
Parâmetros:
HITS_ITERATIONS→hitsIterations(Integer; padrão20)AUTH_PROPERTY→authProperty(String; padrão"auth")HUB_PROPERTY→hubProperty(String; padrão"hub")PARTITIONING→partitioning(String; padrão"AUTO")CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, values
Grafo: direcionado ou não direcionado; sem peso nas ligações.
Comunidade
LOUVAIN
Divide a rede em grupos de empresas muito mais ligadas entre si do que com o restante, sem que seja preciso dizer quantos grupos existem.
O que faz: Começa com cada empresa em um grupo próprio e move cada uma para o grupo vizinho que mais aumenta a modularidade — a medida de quanto os vínculos ficam dentro dos grupos além do que o acaso produziria. Quando nenhuma troca melhora mais o resultado, cada grupo é condensado em um único ponto e a mesma rodada se repete sobre essa rede resumida. O processo sobe de nível até MAX_LEVELS ou até o ganho ficar abaixo de TOLERANCE, e o último nível define o grupo final de cada empresa.
Quando usar: É o ponto de partida para descobrir grupos econômicos de fato quando não se sabe quantos existem. Prefira LEIDEN quando os grupos precisarem ser coesos por dentro (o Louvain às vezes junta partes que só se tocam por um vínculo), LABEL_PROPAGATION quando a base for enorme e a rapidez pesar mais que a qualidade, e SLLPA quando uma empresa puder pertencer a mais de um grupo.
Em linguagem de negócio:
- Quais são os principais grupos de empresas formados por sócios em comum?
- Existem blocos de empresas que operam como um mesmo grupo econômico sem declarar?
- Quais conglomerados familiares controlam várias empresas na cidade?
- Em quantos núcleos societários as empresas do município se dividem, e qual o tamanho de cada um?
- Quais empresas deveriam ser fiscalizadas em conjunto por pertencerem ao mesmo grupo de fato?
Como usar: Projete as empresas no NODES e o vínculo no RELS — no exemplo, o sócio pessoa física em comum entre empresas de Maceió/AL, padrão que já devolve cada par nos dois sentidos —, com a quantidade de sócios compartilhados como coluna de peso indicada em RELATIONSHIPWEIGHTPROPERTY. MAXLEVELS e MAXITERATIONS limitam níveis e rodadas, TOLERANCE define o ganho mínimo para seguir, MINCOMMUNITYSIZE tira da saída os grupos menores que o valor (inclusive as empresas isoladas), SEEDPROPERTY parte de uma divisão anterior e INCLUDEINTERMEDIATE_COMMUNITIES=true devolve também o grupo de cada nível. Cada linha traz nodeId (o mesmo id(e) do NODES), communityId (rótulo do grupo final) e intermediateCommunityIds; agrupe por communityId para ver o tamanho de cada grupo. O recorte da cidade parte do nó Municipio (municipioNome e estadoSigla), porque Endereco.enderecoMunicipio guarda o código de município da Receita, e o mesmo filtro precisa estar no NODES e no RELS, já que a projeção recusa aresta cuja ponta não veio no NODES.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 WITH e1, e2, count(DISTINCT p) AS socios RETURN id(e1) AS source, id(e2) AS target, toFloat(socios) AS peso"
|> GDS LOUVAIN ON rede RELATIONSHIP_WEIGHT_PROPERTY="peso", MAX_LEVELS=10, MIN_COMMUNITY_SIZE=3
|> GROUP BY communityId SUMMARIZE empresas = COUNT(nodeId)
|> ORDER BY empresas DESC
|> LIMIT 20 ;Cuidados: A projeção ocupa memória do Neo4j: recorte o subgrafo por município ou CNAE em vez de projetar o cadastro inteiro, e use MINCOMMUNITYSIZE para não trazer de volta milhares de empresas isoladas. O communityId é só um rótulo, sem relação com ordem ou tamanho, e pode mudar entre execuções, porque a ordem do processamento paralelo (até 4 threads na edição Community) altera as trocas; INCLUDEINTERMEDIATECOMMUNITIES e CONSECUTIVE_IDS não podem ser usados juntos.
Parâmetros:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SEED_PROPERTY→seedProperty(String)MAX_LEVELS→maxLevels(Integer; padrão10)MAX_ITERATIONS→maxIterations(Integer; padrão10)TOLERANCE→tolerance(Float; padrão0.0001)INCLUDE_INTERMEDIATE_COMMUNITIES→includeIntermediateCommunities(Boolean; padrãofalse)CONSECUTIVE_IDS→consecutiveIds(Boolean; padrãofalse)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, intermediateCommunityIds, communityId
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
LABEL_PROPAGATION
Forma grupos deixando cada empresa adotar o rótulo mais comum entre os vizinhos até a divisão se estabilizar.
O que faz: Cada empresa começa com um rótulo próprio (ou com o valor de SEEDPROPERTY) e, a cada rodada, passa a usar o rótulo com mais votos entre os vizinhos, em que cada voto vale o peso da aresta e, se informado, o peso do vizinho em NODEWEIGHTPROPERTY. Os rótulos se espalham pelas regiões mais densas e deixam de mudar; as empresas que terminam com o mesmo rótulo formam um grupo. O cálculo termina quando nenhum rótulo muda ou ao atingir MAXITERATIONS.
Quando usar: Use quando a base é grande e o objetivo é uma divisão rápida em grupos, sem otimizar uma medida global; é mais leve que LOUVAIN e LEIDEN, porém menos estável, e execuções diferentes podem produzir divisões diferentes. Se uma empresa puder estar em dois grupos ao mesmo tempo, use SLLPA.
Em linguagem de negócio:
- Quais empresas são atendidas pelo mesmo escritório de contabilidade?
- Quais carteiras de empresas compartilham o mesmo e-mail de contato?
- Existem aglomerados de empresas administradas pela mesma pessoa ou pelo mesmo escritório?
- Quais conjuntos de empresas giram em torno de um mesmo ponto de contato?
- Como separar rapidamente milhares de empresas em blocos de relacionamento?
Como usar: Projete as empresas no NODES e, no RELS, o vínculo que deve espalhar o rótulo — no exemplo, empresas de Maceió/AL que informam o mesmo e-mail de contato, em geral o do escritório de contabilidade, comparado pelo texto porque cada estabelecimento tem o próprio nó Email. Ajuste MAXITERATIONS; use RELATIONSHIPWEIGHTPROPERTY e NODEWEIGHTPROPERTY para dar mais força a certos vínculos ou empresas, SEEDPROPERTY para partir de rótulos já conhecidos, CONSECUTIVEIDS=true para numerar os grupos a partir de 0 e MINCOMMUNITY_SIZE para esconder grupos pequenos. A saída tem nodeId e communityId; agrupe por communityId para medir o tamanho de cada carteira.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH contatos NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(s:Estabelecimento)-[:TEM_EMAIL]->(x:Email), (e:Empresa)-[:TEM_ESTABELECIMENTO]->(s) WITH toUpper(trim(x.emailEmail)) AS email, collect(DISTINCT e) AS empresas WHERE email <> '' AND size(empresas) > 1 UNWIND empresas AS a UNWIND empresas AS b WITH a, b WHERE a <> b RETURN DISTINCT id(a) AS source, id(b) AS target"
|> GDS LABEL_PROPAGATION ON contatos MAX_ITERATIONS=20, MIN_COMMUNITY_SIZE=3
|> GROUP BY communityId SUMMARIZE empresas = COUNT(nodeId)
|> ORDER BY empresas DESC
|> LIMIT 20 ;Cuidados: A projeção é direcionada e cada empresa só ouve os vizinhos para onde suas arestas apontam: devolva o par nos dois sentidos (sem filtro como id(a) < id(b)), senão parte das empresas nunca muda de rótulo. O resultado depende da ordem das atualizações em paralelo e pode variar entre execuções, e communityId é só um rótulo. Recorte por município ou CNAE antes de projetar, porque o e-mail de um escritório grande liga centenas de empresas e multiplica as arestas.
Parâmetros:
MAX_ITERATIONS→maxIterations(Integer; padrão10)NODE_WEIGHT_PROPERTY→nodeWeightProperty(String)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SEED_PROPERTY→seedProperty(String)CONSECUTIVE_IDS→consecutiveIds(Boolean; padrãofalse)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, communityId
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
WCC
Identifica as ilhas de empresas ligadas por algum caminho de vínculos, sem olhar a direção.
O que faz: Percorre as arestas unindo, a cada uma, os conjuntos a que pertencem as duas pontas, sem considerar a direção; ao final, cada conjunto é um componente, identificado por componentId. Duas empresas caem no mesmo componente se existir qualquer cadeia de vínculos entre elas, ainda que passe por muitas outras. Com RELATIONSHIPWEIGHTPROPERTY e THRESHOLD, só contam as arestas com peso acima do limite.
Quando usar: Use para saber quem está conectado a quem, de qualquer forma, e para medir o tamanho de cada bloco ou achar empresas isoladas — costuma ser a primeira análise, antes dos algoritmos de grupos. Se a direção importa (quem controla quem, participações circulares), use SCC; para quebrar um bloco grande em grupos coesos, use LOUVAIN ou LEIDEN.
Em linguagem de negócio:
- Quais empresas estão ligadas entre si, mesmo que indiretamente, por sócios em comum?
- Quantas ilhas de empresas conectadas existem na cidade e qual o tamanho de cada uma?
- Quais empresas estão isoladas, sem nenhum vínculo societário com outras?
- Até onde se estende a teia de sócios em torno de uma empresa suspeita?
- Este conjunto de fornecedores forma um único bloco ou vários grupos separados?
Como usar: Projete as empresas no NODES e o vínculo no RELS (no exemplo, sócio pessoa física em comum em Maceió/AL); como a direção é ignorada, o par pode vir em um ou nos dois sentidos. MINCOMPONENTSIZE tira da saída os componentes menores que o valor, CONSECUTIVEIDS=true numera os componentes a partir de 0, SEEDPROPERTY continua uma divisão anterior e THRESHOLD, junto com RELATIONSHIPWEIGHTPROPERTY, descarta as arestas de peso igual ou menor que o limite (com peso = sócios em comum, THRESHOLD=1.0 exige dois ou mais). A saída tem nodeId e componentId; agrupe por componentId para contar as empresas de cada ilha.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS WCC ON rede MIN_COMPONENT_SIZE=2
|> GROUP BY componentId SUMMARIZE empresas = COUNT(nodeId)
|> ORDER BY empresas DESC
|> LIMIT 50 ;Cuidados: THRESHOLD sem RELATIONSHIPWEIGHTPROPERTY é recusado. Numa base inteira, o maior componente pode reunir milhões de empresas por encadeamento: recorte por município ou CNAE e olhe o tamanho dos componentes antes de tratá-los como grupo econômico. O componentId é só um rótulo, sem ordem.
Parâmetros:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SEED_PROPERTY→seedProperty(String)THRESHOLD→threshold(Float)CONSECUTIVE_IDS→consecutiveIds(Boolean; padrãofalse)MIN_COMPONENT_SIZE→minComponentSize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, componentId
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
SCC
Encontra conjuntos de empresas em que cada uma alcança todas as outras seguindo a direção dos vínculos, o que revela ciclos de controle.
O que faz: Respeita a direção de cada aresta e junta no mesmo componente as empresas que se alcançam nos dois sentidos: de A se chega a B e de B se volta a A por algum caminho. Numa rede de participações (quem é sócio de quem), um componente com duas ou mais empresas é um ciclo de controle; empresas fora de qualquer ciclo ficam sozinhas no próprio componente.
Quando usar: Use quando a direção tem significado — participação societária, controle, fluxo — e a pergunta é sobre circularidade. Se basta saber se há ligação em qualquer sentido, use WCC, que é mais barato e forma blocos maiores.
Em linguagem de negócio:
- Existem participações cruzadas em que uma empresa acaba sendo sócia de si mesma por meio de outras?
- Quais empresas formam cadeias circulares de controle societário?
- Quais holdings se controlam mutuamente?
- Onde há estruturas de controle em círculo, típicas de blindagem patrimonial?
- Em quais grupos as participações dão a volta e retornam à empresa de origem?
Como usar: Projete no RELS o vínculo orientado de quem detém para quem é detido — no exemplo, a empresa sócia (TEMSOCIEDADEEMPRESA) apontando para a empresa em que tem participação — e, no NODES, as empresas das duas pontas. A opção própria é CONSECUTIVE_IDS=true, que numera os componentes a partir de 0. A saída tem nodeId e componentId; agrupe por componentId e mantenha só os componentes com mais de uma empresa para listar os ciclos.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH participacoes NODES "MATCH (a:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) UNWIND [a, b] AS e RETURN DISTINCT id(e) AS id" RELS "MATCH (a:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE a <> b RETURN DISTINCT id(a) AS source, id(b) AS target"
|> SCC ON participacoes
|> GROUP BY componentId SUMMARIZE empresas = COUNT(nodeId) HAVING COUNT(nodeId) > 1
|> ORDER BY empresas DESC ;Cuidados: A direção decide o resultado: inverter source e target preserva os mesmos ciclos, mas devolver o par nos dois sentidos transforma todo vínculo em ciclo, e o resultado passa a ser igual ao do WCC. A rede de participações entre empresas é bem menor que o cadastro, por isso o exemplo projeta a base inteira; componentes de uma empresa só são a regra e não indicam problema.
Parâmetros:
CONSECUTIVE_IDS→consecutiveIds(Boolean; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, componentId
Grafo: direcionado ou não direcionado; sem peso nas ligações.
TRIANGLE_COUNT
Conta, para cada empresa, quantos trios fechados ela integra — casos em que dois parceiros dela também são ligados entre si.
O que faz: Para cada par de empresas ligadas, cruza as listas de vizinhos das duas e conta os vizinhos em comum; cada vizinho em comum fecha um trio, creditado às três empresas. O total por empresa sai em triangleCount. Empresas com mais vizinhos que MAX_DEGREE ficam de fora da contagem, o que corta o custo dos grandes centros de ligação.
Quando usar: Use para medir, em número absoluto, o quanto a vizinhança de cada empresa é entrelaçada. Para comparar empresas com quantidades diferentes de vínculos, prefira LOCAL_CLUSTERING, que divide pelos pares possíveis; para achar o miolo mais denso da rede, use KCORE.
Em linguagem de negócio:
- Quais empresas estão no centro de trios de empresas ligadas entre si?
- Quais empresas participam de mais relações fechadas, em que os parceiros também se ligam?
- Onde há empresas amarradas umas às outras por sócios em comum, num possível conluio?
- Quais empresas têm os vínculos societários mais entrelaçados da cidade?
Como usar: Projete as empresas no NODES e o vínculo no RELS (no exemplo, sócio pessoa física em comum em Maceió/AL, cada par uma vez). MAXDEGREE pula as empresas com vizinhança grande demais, e LABELFILTER (até três rótulos, separados por vírgula) só conta os trios que envolvam empresas com esses rótulos — o que exige a coluna labels no NODES. A saída tem nodeId e triangleCount; ordene de forma decrescente para achar as empresas mais entrelaçadas.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE id(e1) < id(e2) RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> TRIANGLE_COUNT ON rede MAX_DEGREE=500
|> TOPN 20 BY triangleCount DESC ;Cuidados: O algoritmo trata as ligações sem sentido (como não direcionadas): o DATTAX entrega à GDS a projeção sem direção, então basta devolver cada par uma vez, como o id(e1) < id(e2) do exemplo, sem repetir o par invertido. Empresas cortadas por MAXDEGREE voltam com triangleCount = -1, e não com zero. O custo cresce rapidamente com o número de vizinhos das empresas mais conectadas: recorte por município ou CNAE e use MAXDEGREE.
Parâmetros:
MAX_DEGREE→maxDegree(Integer; padrão2^63^ - 1)LABEL_FILTER→labelFilter(List of String; padrão[])CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, triangleCount
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; sem peso nas ligações.
LOCAL_CLUSTERING
Mede, para cada empresa, a fração de pares de parceiros que também são ligados entre si — de 0 (ninguém se conhece) a 1 (todos se conhecem).
O que faz: Para cada empresa, conta os trios fechados de que ela participa e divide pelo número de pares possíveis entre seus vizinhos: 2 × trios ÷ (grau × (grau - 1)). Um valor perto de 1 indica uma vizinhança em que todos se ligam a todos; perto de 0, uma empresa que conecta parceiros sem relação entre si. A contagem de trios é feita internamente ou reaproveitada de TRIANGLECOUNTPROPERTY.
Quando usar: Use para comparar o entrosamento da vizinhança de empresas com números diferentes de vínculos, já que o valor é normalizado; o TRIANGLE_COUNT dá o número absoluto de trios, e o KCORE aponta o miolo denso da rede como um todo. Valor baixo em empresa com muitos vínculos costuma indicar uma ponte entre grupos.
Em linguagem de negócio:
- Quais empresas têm parceiros que também se relacionam entre si, formando uma panelinha?
- Quais empresas vivem em círculos fechados de sócios, em que todos se conhecem?
- Quais empresas conectam parceiros que não têm nenhuma relação entre si?
- Qual o grau de entrosamento do entorno societário de cada empresa?
- Quais conjuntos de empresas são tão fechados que sugerem um cartel?
Como usar: Projete as empresas no NODES e o vínculo no RELS (no exemplo, sócio pessoa física em comum em Maceió/AL, cada par uma vez). TRIANGLECOUNTPROPERTY aponta uma coluna do NODES com a contagem de trios já calculada, para não recontar. A saída tem nodeId e localClusteringCoefficient; filtre os valores altos para achar panelinhas e os baixos, entre empresas com muitos vínculos, para achar quem faz a ligação entre grupos.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE id(e1) < id(e2) RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> LOCAL_CLUSTERING ON rede
|> FILTER localClusteringCoefficient >= 0.8
|> ORDER BY localClusteringCoefficient DESC
|> LIMIT 100 ;Cuidados: O algoritmo trata as ligações sem sentido (como não direcionadas): o DATTAX entrega à GDS a projeção sem direção, então basta devolver cada par uma vez, como o id(e1) < id(e2) do exemplo, sem repetir o par invertido. Um coeficiente 1,0 em empresa com apenas dois vizinhos pesa pouco: leia o valor junto com o número de vínculos ou de trios. Empresas sem nenhum trio recebem 0.
Parâmetros:
TRIANGLE_COUNT_PROPERTY→triangleCountProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, localClusteringCoefficient
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; sem peso nas ligações.
KCORE
Dá a cada empresa o nível do núcleo mais denso em que ela ainda permanece quando se removem, em camadas, as empresas de poucos vínculos.
O que faz: Remove repetidamente as empresas com menos de k vínculos, aumentando k passo a passo; a cada remoção os vizinhos perdem ligações e podem cair também. O coreValue de uma empresa é o maior k em que ela ainda sobrevive. Por isso, coreValue alto significa estar no miolo, cercada de empresas também bem conectadas, e não apenas ter muitos vínculos.
Quando usar: Use para separar o miolo denso da periferia e para podar empresas de vínculo fraco antes de outras análises. Diferente do grau, que só conta vínculos, o coreValue exige que os vizinhos também sejam conectados; para medir o entrosamento local de cada empresa, use LOCAL_CLUSTERING.
Em linguagem de negócio:
- Qual é o núcleo duro de empresas mais interligadas da cidade?
- Quais empresas continuam conectadas mesmo quando retiramos as de vínculo fraco?
- Quais empresas estão no miolo da estrutura societária e quais estão na periferia?
- Onde está o centro mais denso de empresas amarradas por sócios em comum?
- Quais empresas vale investigar primeiro por estarem no coração do esquema?
Como usar: Projete as empresas no NODES e o vínculo no RELS (no exemplo, sócio pessoa física em comum em Maceió/AL, cada par uma vez); o algoritmo não tem opções próprias. A saída tem nodeId e coreValue; filtre um limite (por exemplo, coreValue >= 3) para ficar só com o miolo e ordene de forma decrescente.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE id(e1) < id(e2) RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> KCORE ON rede
|> FILTER coreValue >= 3
|> ORDER BY coreValue DESC
|> LIMIT 200 ;Cuidados: O algoritmo trata as ligações sem sentido (como não direcionadas): o DATTAX entrega à GDS a projeção sem direção, então basta devolver cada par uma vez, como o id(e1) < id(e2) do exemplo, sem repetir o par invertido. Empresas sem vínculo ficam com coreValue 0, e numa rede formada por grupos fechados (todos ligados a todos) o coreValue reflete o tamanho do grupo menos um.
Parâmetros:
CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, coreValue
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; sem peso nas ligações.
KMEANS
Separa as empresas em K grupos de perfil numérico parecido, medindo a distância entre vetores de características.
O que faz: Trata cada empresa como um ponto no espaço do vetor indicado em NODEPROPERTY. Escolhe K centros iniciais (sorteio uniforme ou kmeans++, em INITIALSAMPLER), atribui cada empresa ao centro mais próximo pela distância euclidiana, recalcula cada centro como a média do grupo e repete até MAXITERATIONS ou até a fração de empresas que trocam de grupo ficar abaixo de DELTATHRESHOLD. Com NUMBEROFRESTARTS maior que 1, roda várias vezes e fica com a divisão de menor distância média.
Quando usar: Use quando você já sabe (ou quer testar) quantos perfis procura e todas as empresas precisam cair em algum grupo. Prefira HDBSCAN quando não souber o número de grupos, quando os grupos tiverem densidades diferentes ou quando quiser que as empresas atípicas fiquem de fora como ruído.
Em linguagem de negócio:
- Como separar as empresas em perfis parecidos de capital, número de filiais e de sócios?
- Quais segmentos de empresas aparecem quando comparamos porte e estrutura societária?
- Em quais grupos de perfil semelhante devo dividir as empresas para uma fiscalização por amostragem?
- Quais empresas estão mais distantes do perfil típico do seu grupo?
- Quantas empresas há em cada faixa de perfil e qual faixa concentra mais empresas?
Como usar: Devolva no NODES, além do id, o vetor numérico de cada empresa — no exemplo, perfil = [log10 do capital social, log10 do número de estabelecimentos, log10 do número de sócios pessoa física] para empresas de Maceió/AL — e informe essa coluna em NODEPROPERTY, obrigatório; o RELS só atende à sintaxe, pois o algoritmo ignora as arestas. K define o número de grupos (padrão 10), RANDOMSEED fixa o sorteio, NUMBEROFRESTARTS repete a execução e COMPUTE_SILHOUETTE=true calcula a silhueta. A saída tem nodeId, communityId (de 0 a K-1), distanceFromCentroid (distância ao centro do grupo) e silhouette (de -1 a 1; quanto maior, melhor a empresa se encaixa no próprio grupo).
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH perfis NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WITH DISTINCT e RETURN id(e) AS id, [log10(1 + coalesce(e.empresaCsocial, 0.0)), log10(1 + COUNT { (e)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento) }), log10(1 + COUNT { (e)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa) })] AS perfil" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> KMEANS ON perfis NODE_PROPERTY="perfil", K=5, RANDOM_SEED=42, COMPUTE_SILHOUETTE=true
|> GROUP BY communityId SUMMARIZE empresas = COUNT(nodeId), distanciaMedia = AVG(distanceFromCentroid), silhuetaMedia = AVG(silhouette)
|> ORDER BY empresas DESC ;Cuidados: As colunas do vetor precisam estar em escalas comparáveis (daí o log10 no exemplo), senão a de maior amplitude domina a distância; o vetor deve ter o mesmo tamanho em todas as empresas e não pode faltar (use coalesce no NODES). Sem COMPUTESILHOUETTE=true a coluna silhouette volta com -1, o que não significa encaixe ruim, e a silhueta custa caro em bases grandes. Sem RANDOMSEED, cada execução sorteia outros centros e os números dos grupos mudam.
Parâmetros:
NODE_PROPERTY→nodeProperty(String; obrigatório)K→k(Integer; padrão10)MAX_ITERATIONS→maxIterations(Integer; padrão10)DELTA_THRESHOLD→deltaThreshold(Float; padrão0.05)NUMBER_OF_RESTARTS→numberOfRestarts(Integer; padrão1)RANDOM_SEED→randomSeed(Integer)INITIAL_SAMPLER→initialSampler(String; padrão"uniform")SEED_CENTROIDS→seedCentroids(List of List of Float; padrão[])COMPUTE_SILHOUETTE→computeSilhouette(Boolean; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, communityId, distanceFromCentroid, silhouette
MODULARITY
Agrupa as empresas maximizando a modularidade em um único nível, sem montar a hierarquia de grupos do Louvain.
O que faz: A cada iteração, as empresas mudam para o grupo vizinho que mais aumenta a modularidade, e o processo para quando o ganho entre iterações fica abaixo de TOLERANCE ou em MAX_ITERATIONS. Para mover muitas empresas ao mesmo tempo sem que vizinhas decidam uma sobre a outra, ele primeiro colore a rede e processa uma cor por vez. Diferente do Louvain, os grupos formados não são condensados e reprocessados em níveis.
Quando usar: Use quando quiser os agrupamentos no nível mais fino, próximos da vizinhança imediata de cada empresa. Para grupos maiores e mais estáveis, que juntam blocos menores, use LOUVAIN ou LEIDEN; para uma divisão ainda mais rápida, LABEL_PROPAGATION.
Em linguagem de negócio:
- Quais pequenos grupos de empresas giram em torno das mesmas pessoas?
- Quais agrupamentos locais de sócios compartilhados existem, sem fundir tudo em grandes blocos?
- Onde estão os aglomerados de empresas com sócios em comum, no nível mais detalhado?
- Como dividir as empresas da cidade em grupos de afinidade societária imediata?
Como usar: Projete as empresas no NODES e o vínculo no RELS (no exemplo, sócio pessoa física em comum em Maceió/AL, com o número de sócios compartilhados como peso). Use RELATIONSHIPWEIGHTPROPERTY para o peso, MAXITERATIONS e TOLERANCE para o critério de parada, SEEDPROPERTY para partir de uma divisão existente, CONSECUTIVEIDS=true para numerar os grupos a partir de 0 e MINCOMMUNITY_SIZE para esconder grupos pequenos. A saída tem nodeId e communityId; agrupe por communityId para ver cada grupo.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 WITH e1, e2, count(DISTINCT p) AS socios RETURN id(e1) AS source, id(e2) AS target, toFloat(socios) AS peso"
|> MODULARITY ON rede RELATIONSHIP_WEIGHT_PROPERTY="peso", MAX_ITERATIONS=20, MIN_COMMUNITY_SIZE=2
|> GROUP BY communityId SUMMARIZE empresas = COUNT(nodeId)
|> ORDER BY empresas DESC
|> LIMIT 20 ;Cuidados: Como a projeção é direcionada, devolva o par nos dois sentidos para que o vínculo conte igualmente para as duas empresas. O processamento é paralelo e os grupos podem variar entre execuções, e communityId é só um rótulo. SEEDPROPERTY e CONSECUTIVEIDS=true não podem ser usados juntos.
Parâmetros:
MAX_ITERATIONS→maxIterations(Integer; padrão10)TOLERANCE→tolerance(Float; padrão0.0001)SEED_PROPERTY→seedProperty(String)CONSECUTIVE_IDS→consecutiveIds(Boolean; padrãofalse)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, communityId
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
LEIDEN
Encontra grupos de empresas como o Louvain, mas garante que cada grupo seja bem conectado por dentro.
O que faz: Move empresas entre grupos vizinhos para ganhar modularidade e depois condensa cada grupo num ponto, como o Louvain, mas insere antes da condensação uma etapa de refinamento: cada grupo é subdividido em partes que precisam estar realmente ligadas por dentro, e só elas são agregadas. Isso evita grupos emendados por um único vínculo. GAMMA ajusta a resolução (valores maiores geram grupos menores e em maior número) e THETA controla o grau de aleatoriedade das escolhas no refinamento.
Quando usar: Prefira ao LOUVAIN quando grupos mal conectados atrapalham a análise ou quando quiser controlar o tamanho dos grupos com GAMMA. Para uma divisão rápida e aproximada, use LABEL_PROPAGATION; para empresas em mais de um grupo, SLLPA.
Em linguagem de negócio:
- Quais grupos econômicos de fato aparecem quando exigimos que cada grupo seja realmente coeso?
- Que subgrupos mais fechados existem dentro dos grandes conglomerados?
- Quais empresas formam blocos societários compactos, sem juntar grupos que só se tocam por um único sócio?
- Como quebrar um conglomerado grande em partes bem amarradas por sócios em comum?
- Quais famílias empresariais formam núcleos compactos de empresas?
Como usar: Projete as empresas no NODES e o vínculo no RELS (no exemplo, sócio pessoa física em comum em Maceió/AL, cada par uma vez, com os sócios compartilhados como peso em RELATIONSHIPWEIGHTPROPERTY). GAMMA controla o tamanho dos grupos, THETA a aleatoriedade do refinamento, MAXLEVELS e TOLERANCE a profundidade e a parada, SEEDPROPERTY parte de uma divisão anterior, MINCOMMUNITYSIZE esconde grupos pequenos e INCLUDEINTERMEDIATECOMMUNITIES=true devolve o grupo de cada nível. A saída tem nodeId, communityId e intermediateCommunityIds; agrupe por communityId para ver o tamanho de cada grupo.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE id(e1) < id(e2) WITH e1, e2, count(DISTINCT p) AS socios RETURN id(e1) AS source, id(e2) AS target, toFloat(socios) AS peso"
|> GDS LEIDEN ON rede RELATIONSHIP_WEIGHT_PROPERTY="peso", GAMMA=1.5, MIN_COMMUNITY_SIZE=3
|> GROUP BY communityId SUMMARIZE empresas = COUNT(nodeId)
|> ORDER BY empresas DESC
|> LIMIT 20 ;Cuidados: O algoritmo trata as ligações sem sentido (como não direcionadas): o DATTAX entrega à GDS a projeção sem direção, então basta devolver cada par uma vez, como o id(e1) < id(e2) do exemplo, sem repetir o par invertido. O refinamento tem sorteio, então os grupos podem variar entre execuções, e communityId é só um rótulo.
Parâmetros:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)MAX_LEVELS→maxLevels(Integer; padrão10)GAMMA→gamma(Float; padrão1.0)THETA→theta(Float; padrão0.01)TOLERANCE→tolerance(Float; padrão0.0001)INCLUDE_INTERMEDIATE_COMMUNITIES→includeIntermediateCommunities(Boolean; padrãofalse)SEED_PROPERTY→seedProperty(String)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, intermediateCommunityIds, communityId
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; aceita peso nas ligações.
SLLPA
Encontra grupos que se sobrepõem, permitindo que uma mesma empresa pertença a mais de um grupo.
O que faz: É uma propagação de rótulos com memória: cada empresa guarda todos os rótulos que já ouviu. A cada rodada, cada empresa passa a cada vizinho um rótulo sorteado da própria memória e acrescenta à sua memória o rótulo mais frequente entre os que recebeu. No fim, mantém os rótulos cuja frequência na memória passa de MINASSOCIATIONSTRENGTH — por isso uma empresa pode sair com vários grupos.
Quando usar: Use quando as empresas naturalmente participam de mais de um grupo — um sócio ligado a duas famílias, um escritório que atende dois conglomerados. Se cada empresa deve ficar em um único grupo, use LABEL_PROPAGATION, LOUVAIN ou LEIDEN.
Em linguagem de negócio:
- Quais empresas pertencem a mais de um grupo econômico ao mesmo tempo?
- Quais empresas fazem a ponte entre dois grupos societários?
- Onde os grupos de empresas se sobrepõem?
- Quais empresas têm dupla vinculação, ligadas a famílias empresariais diferentes?
- Que empresas circulam por vários conglomerados e merecem atenção redobrada?
Como usar: Projete as empresas no NODES e o vínculo no RELS, nos dois sentidos (no exemplo, sócio pessoa física em comum em Maceió/AL). MAXITERATIONS é obrigatório e define quantas rodadas acontecem e o tamanho da memória; MINASSOCIATION_STRENGTH (padrão 0.2) é a frequência mínima para um rótulo ficar, e PARTITIONING ("RANGE", "DEGREE" ou "AUTO", entre aspas) escolhe como o trabalho é repartido entre as threads. A saída tem nodeId e values, um mapa cujo item communityIds lista os grupos da empresa; mais de um id indica sobreposição.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS SLLPA ON rede MAX_ITERATIONS=20, MIN_ASSOCIATION_STRENGTH=0.2
|> LIMIT 500 ;Cuidados: O sorteio usa o relógio como semente e não há opção para fixá-lo, então cada execução pode dar grupos diferentes (o DATTAX reaproveita por 30 minutos o resultado da mesma projeção com as mesmas opções). Os rótulos seguem a direção das arestas: devolva o par nos dois sentidos, senão parte das empresas nunca ouve os vizinhos. MINASSOCIATIONSTRENGTH baixo espalha cada empresa por muitos grupos, e alto aproxima o resultado de uma divisão sem sobreposição.
Parâmetros:
MAX_ITERATIONS→maxIterations(Integer; obrigatório)MIN_ASSOCIATION_STRENGTH→minAssociationStrength(String; padrão0.2)PARTITIONING→partitioning(String; padrão"RANGE")CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, values
Grafo: direcionado ou não direcionado; sem peso nas ligações.
K1COLORING
Atribui uma cor a cada empresa de modo que empresas ligadas fiquem com cores diferentes, usando poucas cores.
O que faz: Colore as empresas em paralelo de forma gulosa: cada empresa pega a menor cor que nenhum vizinho está usando. Como vizinhas processadas ao mesmo tempo podem escolher a mesma cor, uma etapa de verificação encontra esses conflitos, e só as empresas em conflito são recoloridas na rodada seguinte, até não sobrar conflito ou atingir MAX_ITERATIONS. O número de cores obtido é pequeno, mas não necessariamente o mínimo possível.
Quando usar: Use quando precisar repartir empresas em turmas em que nenhuma tenha vínculo com outra da mesma turma, aceitando o número de turmas que o algoritmo encontrar. Se o número de lotes é fixo e é aceitável que alguns vínculos fiquem dentro do mesmo lote, use APPROXMAXK_CUT.
Em linguagem de negócio:
- Como dividir as empresas em rodadas de fiscalização sem que duas empresas ligadas caiam na mesma rodada?
- Qual o menor número de turnos de visita para nunca abordar juntas empresas com sócios em comum?
- Como formar lotes de empresas sem nenhum vínculo societário entre si?
- Como montar amostras independentes, em que nenhuma empresa tenha sócio em comum com outra do mesmo lote?
Como usar: Projete as empresas no NODES e, no RELS, o vínculo que não pode se repetir dentro de uma turma, nos dois sentidos (no exemplo, sócio pessoa física em comum em Maceió/AL). MAXITERATIONS limita as rodadas de correção e MINCOMMUNITY_SIZE esconde as cores com poucas empresas. A saída tem nodeId e color; cada cor é uma turma, e o número de cores distintas é o número de rodadas necessárias.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS K1COLORING ON rede MAX_ITERATIONS=20
|> GROUP BY color SUMMARIZE empresas = COUNT(nodeId)
|> ORDER BY color ;Cuidados: Devolva o par nos dois sentidos: a projeção é direcionada e cada empresa só evita a cor dos vizinhos para onde suas arestas apontam. Se o cálculo parar em MAX_ITERATIONS antes de convergir, podem sobrar vizinhos com a mesma cor; aumente o limite. A cor é só um rótulo, as empresas sem vínculo ficam todas na menor cor, e a distribuição muda entre execuções por causa do processamento paralelo.
Parâmetros:
MAX_ITERATIONS→maxIterations(Integer; padrão10)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, color
Grafo: direcionado ou não direcionado; sem peso nas ligações.
APPROXMAXK_CUT
Divide as empresas em K grupos de forma que o máximo possível de vínculos fique entre grupos diferentes, e não dentro deles.
O que faz: É uma heurística aleatória: em cada uma das ITERATIONS tentativas, distribui as empresas ao acaso entre os K grupos e melhora a divisão por busca local, trocando empresas de grupo enquanto isso aumentar o total (ou o peso) de vínculos cortados. Guarda a melhor tentativa. Com VNSMAXNEIGHBORHOOD_ORDER maior que 0, perturba a solução em vizinhanças cada vez maiores para escapar de ótimos locais.
Quando usar: Use quando o número de grupos é fixo (equipes, lotes, turnos) e o objetivo é separar ao máximo as empresas relacionadas, aceitando que algumas ligações fiquem dentro do mesmo grupo. Se nenhuma ligação pode ficar dentro do grupo e o número de grupos é livre, use K1COLORING; para juntar empresas relacionadas, e não separá-las, use LOUVAIN.
Em linguagem de negócio:
- Como distribuir as empresas entre equipes de análise para que empresas ligadas fiquem com equipes diferentes?
- Como dividir os fornecedores em lotes para que empresas com sócios em comum não disputem o mesmo lote?
- Qual divisão em três grupos deixa a maior parte dos vínculos societários entre grupos diferentes?
- Como separar as empresas em turmas que misturem o mínimo possível de empresas relacionadas?
Como usar: Projete as empresas no NODES e, no RELS, o vínculo que deve ser separado, com peso quando alguns contam mais (no exemplo, sócios pessoa física em comum em Maceió/AL, com peso em RELATIONSHIPWEIGHTPROPERTY). K define o número de grupos (padrão 2), ITERATIONS o número de tentativas, VNSMAXNEIGHBORHOODORDER liga a busca em vizinhanças e MINCOMMUNITY_SIZE esconde da saída as empresas de grupos pequenos. A saída tem nodeId e communityId, de 0 a K-1, que diz o grupo de cada empresa.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 WITH e1, e2, count(DISTINCT p) AS socios RETURN id(e1) AS source, id(e2) AS target, toFloat(socios) AS peso"
|> GDS APPROX_MAX_K_CUT ON rede K=3, ITERATIONS=16, RELATIONSHIP_WEIGHT_PROPERTY="peso"
|> ORDER BY communityId, nodeId
|> LIMIT 1000 ;Cuidados: O resultado é aproximado e muda a cada execução; para repeti-lo, a GDS só aceita RANDOMSEED com CONCURRENCY=1 (o DATTAX reaproveita por 30 minutos o resultado da mesma projeção com as mesmas opções). Mais ITERATIONS e a busca em vizinhanças melhoram o corte, mas multiplicam o tempo. MINCOMMUNITY_SIZE só filtra a saída; não impõe tamanho mínimo aos grupos.
Parâmetros:
K→k(Integer; padrão2)ITERATIONS→iterations(Integer; padrão8)VNS_MAX_NEIGHBORHOOD_ORDER→vnsMaxNeighborhoodOrder(Integer; padrão0 (VNS off))RANDOM_SEED→randomSeed(Integer)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; padrão0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, communityId
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
HDBSCAN
Agrupa as empresas pela densidade de um perfil numérico, descobre sozinho quantos grupos existem e separa as atípicas como ruído.
O que faz: Para cada empresa, mede a distância até o SAMPLES-ésimo vizinho mais próximo no vetor de NODEPROPERTY (a distância de núcleo) e liga todas as empresas por uma árvore geradora mínima, com as distâncias ajustadas por essa densidade local. Depois corta a árvore em níveis crescentes de densidade e escolhe os grupos que se mantêm estáveis e têm ao menos MINCLUSTERSIZE empresas; quem não cabe em nenhum deles recebe o rótulo -1 (ruído). A busca de vizinhos usa uma árvore k-d, cujo tamanho de folha é LEAFSIZE.
Quando usar: Use quando não souber quantos perfis existem, quando os grupos tiverem densidades diferentes ou quando o objetivo for justamente achar as empresas que destoam. Se o número de grupos já é conhecido e toda empresa precisa ser classificada, use KMEANS, que é mais simples de interpretar.
Em linguagem de negócio:
- Quais empresas destoam de todas as demais e não se encaixam em nenhum perfil?
- Existem concentrações naturais de empresas com perfis muito parecidos?
- Quais empresas têm características atípicas que merecem análise individual?
- Quantos perfis naturais de empresa surgem sem que eu precise dizer quantos quero?
- Quais empresas fogem do padrão de capital, filiais e sócios das demais da cidade?
Como usar: Devolva no NODES o vetor numérico de cada empresa e informe essa coluna em NODEPROPERTY, obrigatório (no exemplo, perfil = [log10 do capital social, log10 do número de estabelecimentos, log10 do número de sócios pessoa física] em Maceió/AL); o RELS só atende à sintaxe, pois o algoritmo ignora as arestas. MINCLUSTERSIZE é o menor grupo aceito, SAMPLES regula o quanto o algoritmo é conservador (valores maiores marcam mais empresas como ruído) e LEAFSIZE ajusta a árvore de busca. A saída tem nodeId e label: o número do grupo, ou -1 para ruído.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH perfis NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WITH DISTINCT e RETURN id(e) AS id, [log10(1 + coalesce(e.empresaCsocial, 0.0)), log10(1 + COUNT { (e)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento) }), log10(1 + COUNT { (e)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa) })] AS perfil" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS HDBSCAN ON perfis NODE_PROPERTY="perfil", MIN_CLUSTER_SIZE=20, SAMPLES=10
|> FILTER label < 0
|> LIMIT 200 ;Cuidados: As colunas do vetor precisam estar em escalas comparáveis, e toda empresa precisa ter o vetor (use coalesce no NODES). O rótulo -1 não é um grupo, e sim as empresas atípicas; para filtrá-las no DATTAX use label < 0, pois a linguagem não aceita o literal negativo -1. O custo cresce com o número de empresas e de dimensões do vetor: recorte por município ou CNAE.
Parâmetros:
NODE_PROPERTY→nodeProperty(String; obrigatório)MIN_CLUSTER_SIZE→minClusterSize(Integer; padrão5)SAMPLES→samples(Integer; padrão10)LEAF_SIZE→leafSize(Integer; padrão1)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, label
CONDUCTANCE
Mede, para cada grupo de uma divisão já existente, que fração dos vínculos de suas empresas sai para empresas de outros grupos.
O que faz: Lê o grupo de cada empresa na propriedade indicada em COMMUNITYPROPERTY e percorre os vínculos que saem de cada empresa, somando-os como internos (o vizinho está no mesmo grupo) ou externos (está em outro). A condutância de um grupo é externos ÷ (externos + internos): 0 significa grupo fechado, sem vínculo para fora, e 1 significa que todos os vínculos vão para fora. Com RELATIONSHIPWEIGHT_PROPERTY, soma pesos em vez de contar vínculos.
Quando usar: Use para avaliar uma divisão que você já tem — setor econômico, município, escritório contábil — e ver quais grupos são bem delimitados e quais vazam. Para uma nota que compara a divisão com o acaso e soma para o conjunto todo, use MODULARITY_METRIC; para descobrir os grupos, use LOUVAIN ou LEIDEN.
Em linguagem de negócio:
- Em quais setores os sócios também mantêm empresas em outros setores?
- Quais setores formam ecossistemas societários fechados em si mesmos?
- A divisão por atividade econômica isola bem os grupos de sócios ou vaza para fora?
- Quais segmentos têm mais vínculos com empresas de fora do próprio segmento?
- O agrupamento que usamos na fiscalização é bem delimitado, ou as empresas se ligam demais a outros grupos?
Como usar: Devolva no NODES, além do id, o número inteiro do grupo de cada empresa e informe essa coluna em COMMUNITYPROPERTY, obrigatório — no exemplo, setor = os dois primeiros dígitos do CNAE principal (a divisão), 0 quando falta, para empresas de Maceió/AL. No RELS vão os vínculos a avaliar, nos dois sentidos, e RELATIONSHIPWEIGHT_PROPERTY é opcional. A saída tem uma linha por grupo, com community (o valor da propriedade) e conductance; ordene de forma crescente para ver os grupos mais fechados.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH setores NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(s:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) OPTIONAL MATCH (s)-[:TEM_CNAE]->(c:CNAE) WITH e, min(toInteger(left(c.cnaeCodigoprimario, 2))) AS divisao RETURN id(e) AS id, coalesce(divisao, 0) AS setor" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS CONDUCTANCE ON setores COMMUNITY_PROPERTY="setor"
|> ORDER BY conductance
|> LIMIT 30 ;Cuidados: A contagem parte das arestas que saem de cada empresa: com o par num só sentido, um vínculo entre dois grupos entra só na conta de um deles, então devolva os dois sentidos. A propriedade de grupo precisa ser um número inteiro; empresas sem valor ou com valor negativo ficam de fora da conta, e grupos sem nenhum vínculo não aparecem na saída.
Parâmetros:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)COMMUNITY_PROPERTY→communityProperty(String; obrigatório)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: community, conductance
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
MODULARITY_METRIC
Calcula a modularidade de uma divisão já existente, grupo a grupo, mostrando quanto cada grupo concentra vínculos internos além do esperado ao acaso.
O que faz: Lê o grupo de cada empresa em COMMUNITY_PROPERTY e, para cada grupo, compara o peso dos vínculos internos com o que se esperaria se os vínculos fossem distribuídos ao acaso, preservando o quanto cada empresa se conecta: (internos - total do grupo² ÷ total da rede) ÷ total da rede. O resultado é a contribuição de cada grupo, e a soma de todas as contribuições é a modularidade da divisão inteira. Valores positivos indicam grupos mais coesos do que o acaso produziria.
Quando usar: Use para dar nota a uma divisão existente (setor, município, carteira de contador) ou para comparar duas divisões sobre a mesma projeção. A CONDUCTANCE mostra o vazamento de cada grupo isoladamente; a modularidade compara com o acaso e soma para o conjunto todo, que é o mesmo critério que o LOUVAIN e o LEIDEN otimizam.
Em linguagem de negócio:
- A classificação por setor econômico explica os agrupamentos de sócios?
- Os grupos que definimos por setor, município ou escritório são coesos ou arbitrários?
- Quais setores concentram vínculos societários internos mais do que o acaso explicaria?
- Quanto cada setor contribui para a coesão geral dos vínculos entre as empresas?
- Vale a pena analisar as empresas por setor, ou os grupos reais atravessam os setores?
Como usar: Devolva no NODES o número do grupo de cada empresa e informe a coluna em COMMUNITYPROPERTY, obrigatório — no exemplo, setor = os dois primeiros dígitos do CNAE principal das empresas de Maceió/AL. No RELS vão os vínculos nos dois sentidos, com peso opcional em RELATIONSHIPWEIGHT_PROPERTY (no exemplo, sócios em comum). A saída tem uma linha por grupo, com communityId (o valor da propriedade) e modularity; some a coluna modularity para obter a nota da divisão inteira.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH setores NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(s:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) OPTIONAL MATCH (s)-[:TEM_CNAE]->(c:CNAE) WITH e, min(toInteger(left(c.cnaeCodigoprimario, 2))) AS divisao RETURN id(e) AS id, coalesce(divisao, 0) AS setor" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 WITH e1, e2, count(DISTINCT p) AS socios RETURN id(e1) AS source, id(e2) AS target, toFloat(socios) AS peso"
|> GDS MODULARITY_METRIC ON setores COMMUNITY_PROPERTY="setor", RELATIONSHIP_WEIGHT_PROPERTY="peso"
|> ORDER BY modularity DESC
|> LIMIT 30 ;Cuidados: Os valores por grupo são pequenos e a soma não passa de 1; um grupo com contribuição negativa tem menos vínculos internos do que o acaso daria. Como só as arestas que saem de cada empresa são contadas, devolva o par nos dois sentidos para que o vínculo conte para os dois grupos. Empresas sem a propriedade formam um grupo à parte, então preencha o valor com coalesce.
Parâmetros:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)COMMUNITY_PROPERTY→communityProperty(String; obrigatório)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: communityId, modularity
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
Caminhos
SHORTESTPATHDIJKSTRA
Encontra o caminho de menor custo entre dois nós da projeção; sem peso, é o que passa pelo menor número de ligações.
O que faz: Parte do nó de origem e vai fixando, um de cada vez, o nó ainda não resolvido mais próximo da origem, até chegar ao destino; nesse momento, o caminho encontrado é garantidamente o de menor custo. O custo de cada ligação vem da coluna de peso do RELS; sem ela, toda ligação custa 1 e a resposta é o caminho com menos saltos. Devolve uma única linha, ou nenhuma quando o destino não é alcançável a partir da origem seguindo o sentido das arestas.
Quando usar: Para perguntas de par — origem e destino conhecidos — com pesos positivos ou sem peso. Prefira o A* quando os nós têm coordenadas e o peso é distância, o Yen quando precisar das alternativas e o Bellman-Ford quando houver peso negativo.
Em linguagem de negócio:
- Por quantas empresas e sócios intermediários a pessoa X chega até a empresa Y?
- Qual a ligação mais direta entre o empresário X e a empresa Y?
- Existe alguma conexão societária entre a empresa A e a empresa B? Qual é a mais curta?
- Quantos elos separam o sócio suspeito X da empresa investigada Y?
- Qual a menor sequência de participações que liga o grupo A à empresa B?
- Através de quem a pessoa X se conecta à empresa Y com o menor número de passagens?
Como usar: Projete a rede com as ligações nos dois sentidos quando o vínculo não tem direção (sócio e empresa se alcançam mutuamente) e obtenha origem e destino como elementId num MATCH anterior (RETURN elementId(o) AS origem): FROM e TO recebem esses textos. RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIP_WEIGHT) aponta a coluna de peso do RELS, e CONCURRENCY vai até 4. A linha devolvida traz totalCost (custo total; sem peso, o número de ligações), nodeIds (ids internos na ordem do percurso), costs (custo acumulado a cada passo), path (o percurso), além de index, sourceNode e targetNode.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (o:Pessoa {pessoaNome: 'FULANO DE TAL'}), (d:Empresa {empresaCnpj: '12345678'}) RETURN elementId(o) AS origem, elementId(d) AS destino LIMIT 1"
|> PROJECT GRAPH societaria NODES "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [s, e] AS n RETURN DISTINCT id(n) AS id" RELS "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [[id(s), id(e)], [id(e), id(s)]] AS par RETURN DISTINCT par[0] AS source, par[1] AS target"
|> SHORTEST PATH FROM origem TO destino IN societaria ;Cuidados: A projeção é direcionada: sem os dois sentidos no RELS, o percurso só anda no sentido gravado e pode não achar uma ligação que existe. Pesos negativos não são suportados (use BELLMAN_FORD), e peso nulo vira NaN na projeção — trate com coalesce no RELS. Os ids de nodeIds são internos (id(n)), não elementId.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; preenchido pela sintaxe)TARGET_NODE→targetNode(Integer; preenchido pela sintaxe)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
ASTAR
Encontra o caminho de menor custo entre dois nós com coordenadas, usando a distância em linha reta até o destino para explorar menos a rede.
O que faz: Trabalha como o Dijkstra, mas escolhe o próximo nó a expandir somando o custo já percorrido a uma estimativa do que falta: a distância em linha reta até o destino, calculada pela fórmula de haversine, em milhas náuticas, a partir da latitude e da longitude de cada nó. Com essa estimativa a busca avança na direção do destino e visita bem menos nós em redes geográficas grandes. Devolve uma linha com o percurso, ou nenhuma se o destino não for alcançável.
Quando usar: Quando os nós são lugares — endereços, estabelecimentos, pontos de apoio — com latitude e longitude, e o peso da ligação é a distância entre eles. Para ligações sem sentido geográfico (vínculos societários), use o Dijkstra (SHORTEST PATH).
Em linguagem de negócio:
- Qual o trajeto de menor distância entre a filial de Maceió e a de Arapiraca, parando só em unidades do próprio grupo?
- Qual o roteiro com menos quilômetros para a equipe de fiscalização ir do estabelecimento A ao B, passando por estabelecimentos vizinhos?
- Como transferir mercadoria do depósito X para a loja Y usando apenas unidades a até 50 km uma da outra?
- Qual a sequência de endereços mais próximos que liga a matriz à filial mais distante?
- Por quais unidades intermediárias passa o trajeto de menor quilometragem entre as unidades A e B?
Como usar: Projete os nós com as colunas de latitude e longitude no NODES — no exemplo, as do CEP de cada endereço (c.latitude AS lat, c.longitude AS lon) — e informe os nomes delas nas opções obrigatórias LATITUDEPROPERTY e LONGITUDEPROPERTY; FROM e TO recebem os elementId de origem e destino. Use como peso (RELATIONSHIPWEIGHTPROPERTY) a distância da ligação em milhas náuticas — point.distance(...) / 1852.0 no RELS — para a estimativa nunca passar do custo real; CONCURRENCY vai até 4. A linha devolvida traz totalCost (distância total), nodeIds (ids internos na ordem), costs (acumulado a cada passo), path, index, sourceNode e targetNode.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (m1:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}), (m2:Municipio {municipioNome: 'ARAPIRACA', estadoSigla: 'AL'}) MATCH (e:Empresa {empresaCnpj: '12345678'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(o:Endereco {enderecoMunicipio: m1.municipioCodigo})-[:TEM_CEP]->(co:CEP), (e)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(d:Endereco {enderecoMunicipio: m2.municipioCodigo})-[:TEM_CEP]->(cd:CEP) WHERE co.latitude IS NOT NULL AND co.longitude IS NOT NULL AND cd.latitude IS NOT NULL AND cd.longitude IS NOT NULL RETURN elementId(o) AS origem, elementId(d) AS destino LIMIT 1"
|> PROJECT GRAPH filiais NODES "MATCH (:Empresa {empresaCnpj: '12345678'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(ed:Endereco)-[:TEM_CEP]->(c:CEP) WHERE c.latitude IS NOT NULL AND c.longitude IS NOT NULL RETURN DISTINCT id(ed) AS id, c.latitude AS lat, c.longitude AS lon" RELS "MATCH (e:Empresa {empresaCnpj: '12345678'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(a:Endereco)-[:TEM_CEP]->(ca:CEP), (e)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(b:Endereco)-[:TEM_CEP]->(cb:CEP) WHERE a <> b AND ca.latitude IS NOT NULL AND ca.longitude IS NOT NULL AND cb.latitude IS NOT NULL AND cb.longitude IS NOT NULL WITH a, b, point.distance(point({latitude: ca.latitude, longitude: ca.longitude}), point({latitude: cb.latitude, longitude: cb.longitude})) / 1852.0 AS milhas WHERE milhas <= 27.0 RETURN DISTINCT id(a) AS source, id(b) AS target, milhas"
|> ASTAR FROM origem TO destino IN filiais LATITUDE_PROPERTY="lat", LONGITUDE_PROPERTY="lon", RELATIONSHIP_WEIGHT_PROPERTY="milhas" ;Cuidados: A carga da Receita não traz coordenadas: latitude e longitude do CEP são enriquecimento de geolocalização feito à parte, e o A* só roda depois dele, com as duas em todo nó projetado. Com peso menor que a distância real em milhas náuticas — ou sem peso, quando cada ligação vale 1 —, a estimativa superestima o que falta e o percurso devolvido pode não ser o de menor custo. Projete as ligações nos dois sentidos, porque a projeção é direcionada.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; preenchido pela sintaxe)TARGET_NODE→targetNode(Integer; preenchido pela sintaxe)LATITUDE_PROPERTY→latitudeProperty(Float; obrigatório)LONGITUDE_PROPERTY→longitudeProperty(Float; obrigatório)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
YENS
Encontra os K caminhos de menor custo entre dois nós, do melhor para o pior, sem repetir nó dentro de um mesmo caminho.
O que faz: Calcula primeiro o melhor caminho e, a partir dele, gera candidatos desviando em cada ponto do percurso anterior — bloqueando a ligação já usada naquele trecho —, guardando sempre o candidato mais barato como o próximo da lista. Repete até reunir K caminhos ou esgotar as alternativas. Cada caminho é simples, sem nó repetido, e os resultados vêm numerados por index, do mais barato ao mais caro.
Quando usar: Quando uma resposta só não basta: rotas alternativas, redundância de vínculos ou o que sobra se o intermediário principal sair. Para apenas a melhor ligação, o Dijkstra (SHORTEST PATH) é mais barato, e o custo do Yen cresce com K.
Em linguagem de negócio:
- Quais são as três ligações alternativas mais curtas entre a pessoa X e a empresa Y?
- Além da ligação mais direta, por quais outros intermediários o grupo A chega à empresa B?
- Se o sócio que faz a ponte principal sair, qual a próxima rota de ligação entre X e Y?
- Liste as cinco cadeias societárias mais curtas que conectam a empresa A à empresa B.
- De quantas formas diferentes a pessoa X chega ao controle da empresa Y?
Como usar: Projete a rede como para o SHORTEST PATH (ligações nos dois sentidos quando o vínculo não tem direção) e informe origem e destino por elementId no FROM e no TO; K vem na própria sintaxe (K 3). RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIP_WEIGHT) define o custo de cada ligação e CONCURRENCY vai até 4. Cada linha é um caminho: index (0 é o melhor), totalCost, nodeIds (ids internos na ordem), costs, path, sourceNode e targetNode.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (o:Pessoa {pessoaNome: 'FULANO DE TAL'}), (d:Empresa {empresaCnpj: '12345678'}) RETURN elementId(o) AS origem, elementId(d) AS destino LIMIT 1"
|> PROJECT GRAPH societaria NODES "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [s, e] AS n RETURN DISTINCT id(n) AS id" RELS "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [[id(s), id(e)], [id(e), id(s)]] AS par RETURN DISTINCT par[0] AS source, par[1] AS target"
|> YENS FROM origem TO destino K 3 IN societaria
|> ORDER BY index ;Cuidados: Use DISTINCT no RELS: linha repetida vira aresta paralela, e cada aresta paralela conta como uma rota diferente — surgem alternativas que passam pelos mesmos nós. Menos de K linhas quer dizer que não há mais alternativas sem repetir nó; pesos negativos não são suportados.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; preenchido pela sintaxe)TARGET_NODE→targetNode(Integer; preenchido pela sintaxe)K→k(Integer; preenchido pela sintaxe; padrão1)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
ALLSHORTESTPATHS_DELTA
Calcula, a partir de um nó de origem, o caminho de menor custo até cada nó alcançável, em paralelo, agrupando as distâncias em faixas.
O que faz: É a versão paralela do cálculo de uma origem para todos: as distâncias provisórias são distribuídas em faixas de largura DELTA, e os nós de uma mesma faixa são processados ao mesmo tempo por várias threads, que repetem o ajuste até a faixa estabilizar antes de passar à seguinte. Os custos são os mesmos do Dijkstra de origem única (em empate, o percurso escolhido pode variar), obtidos mais depressa em projeções grandes e com peso; sai uma linha por nó alcançável.
Quando usar: Em projeções grandes, com peso positivo, quando a pergunta é da origem até todos; em projeções pequenas ou sem peso, o ALLSHORTESTPATHS (Dijkstra) dá a mesma resposta sem precisar calibrar o DELTA. Ajuste DELTA para a ordem de grandeza do peso típico de uma ligação.
Em linguagem de negócio:
- Considerando a força de cada vínculo, quão próxima da empresa X está cada empresa de Maceió?
- Partindo do empresário X, quais empresas ele alcança por vínculos mais fortes e quais ficam mais distantes?
- Monte o mapa de alcance da holding X sobre milhares de empresas, do vínculo mais forte ao mais fraco.
- Quais empresas estão mais próximas da investigada quando se pesa quantos sócios cada par compartilha?
- Qual o custo de ligação entre a empresa X e cada empresa da base, levando em conta o peso de cada relação?
Como usar: Projete a rede com a coluna de custo no RELS — no exemplo, 1.0 / count(DISTINCT p) AS custo, que barateia os vínculos com mais sócios em comum — e informe o nó de partida na opção obrigatória SOURCENODE, que recebe o id interno: o mesmo id(n) da projeção, que um MATCH anterior devolve com RETURN id(e). RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIPWEIGHT) aponta o peso, DELTA (padrão 2.0) define a largura das faixas e CONCURRENCY vai até 4. Cada linha é um destino: targetNode, totalCost, nodeIds (ids internos do percurso), costs (acumulado), path, index e sourceNode.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH vinculos NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa) WHERE e1 <> e2 AND EXISTS { (e2)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) } RETURN id(e1) AS source, id(e2) AS target, 1.0 / count(DISTINCT p) AS custo"
|> GDS ALL_SHORTEST_PATHS_DELTA ON vinculos SOURCE_NODE=4211, RELATIONSHIP_WEIGHT_PROPERTY="custo", DELTA=0.5
|> ORDER BY totalCost ASC
|> LIMIT 100 ;Cuidados: A saída tem uma linha por nó alcançável: em projeção grande, recorte antes ou filtre e limite depois. SOURCE_NODE é número (id interno), não elementId, e precisa estar na projeção; pesos devem ser zero ou positivos. CONCURRENCY acima de 4 é recusado pela edição Community instalada.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; obrigatório)DELTA→delta(Float; padrão2.0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
ALLSHORTESTPATHS_DIJKSTRA
Calcula, a partir de um nó de origem, o caminho de menor custo até cada nó alcançável.
O que faz: Roda o Dijkstra a partir da origem sem parar num destino: vai fixando os nós do mais próximo ao mais distante e, para cada um, guarda o percurso que o alcançou com o menor custo. Devolve uma linha por nó alcançável, com o custo total e a sequência de nós até ele. Sem peso, o custo é o número de ligações.
Quando usar: Quando a pergunta é da origem até todos numa projeção pequena ou média, com peso positivo ou sem peso. Em projeções grandes e com peso, o ALLSHORTESTPATHSDELTA paraleliza o mesmo cálculo; para só listar quem está por perto, sem o percurso, o BFS com MAXDEPTH é mais leve.
Em linguagem de negócio:
- A quantos intermediários de distância cada empresa de Maceió está do empresário X?
- Quais empresas o sócio X alcança e por qual ligação mais direta ele chega a cada uma?
- Liste as pessoas e empresas conectadas à pessoa X, da mais próxima à mais distante.
- Até onde vai a influência da holding X e por quem ela passa para chegar a cada empresa?
- Quais empresas estão a no máximo quatro elos do laranja suspeito X, e através de quem?
Como usar: Projete a rede (ligações nos dois sentidos quando o vínculo não tem direção) e informe a origem pelo FROM, com o elementId obtido num MATCH anterior. RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIP_WEIGHT) define o custo de cada ligação e CONCURRENCY vai até 4. Cada linha é um destino: targetNode, totalCost (custo, ou número de ligações sem peso), nodeIds (ids internos do percurso), costs, path, index e sourceNode — filtre por totalCost para ficar com os mais próximos.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (o:Pessoa {pessoaNome: 'FULANO DE TAL'}) RETURN elementId(o) AS origem LIMIT 1"
|> PROJECT GRAPH societaria_maceio NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) UNWIND [s, e] AS n RETURN DISTINCT id(n) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) UNWIND [[id(s), id(e)], [id(e), id(s)]] AS par RETURN DISTINCT par[0] AS source, par[1] AS target"
|> ALL_SHORTEST_PATHS FROM origem IN societaria_maceio
|> FILTER totalCost <= 4
|> ORDER BY totalCost ASC ;Cuidados: A saída tem uma linha por nó alcançável e cada linha carrega o percurso inteiro: numa rede grande isso pesa, então recorte a projeção (por município, por grupo) antes de rodar. Pesos negativos não são suportados, e a origem precisa estar na projeção.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; preenchido pela sintaxe)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
BFS
Percorre a rede a partir de um nó em camadas: primeiro os vizinhos diretos, depois os vizinhos deles, e assim por diante.
O que faz: Visita todos os nós a uma ligação da origem antes de qualquer nó a duas ligações, e assim sucessivamente, seguindo o sentido das arestas e sem usar peso. A varredura termina quando esgota o que é alcançável, quando atinge a profundidade MAXDEPTH ou quando encontra o primeiro dos nós de TARGETNODES, que fecha a lista. Devolve uma única linha com os nós na ordem de visita — os mais próximos primeiro.
Quando usar: Para levantar a vizinhança até N níveis ou descobrir qual alvo de uma lista aparece primeiro, sem peso. Com peso, use o ALLSHORTESTPATHS; para seguir cada cadeia até o fim antes de passar à próxima, o DFS.
Em linguagem de negócio:
- Quem está a até dois vínculos de distância da empresa X?
- Quais são os sócios da empresa X e em que outras empresas eles aparecem?
- Levante a vizinhança societária da empresa X até o terceiro nível.
- Partindo da pessoa X, qual das empresas da lista de suspeitas aparece primeiro?
- Quem são os sócios dos sócios da empresa X?
Como usar: Projete a rede (ligações nos dois sentidos quando o vínculo não tem direção) e informe a origem pelo FROM, com o elementId obtido num MATCH anterior. MAXDEPTH (padrão -1, sem limite) corta a profundidade, TARGETNODES recebe ids internos separados por vírgula (TARGET_NODES="12,57") para parar no primeiro encontrado, e CONCURRENCY vai até 4. A linha devolvida traz sourceNode, nodeIds (ids internos na ordem de visita) e path.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (e:Empresa {empresaCnpj: '12345678'}) RETURN elementId(e) AS origem"
|> PROJECT GRAPH societaria NODES "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [s, e] AS n RETURN DISTINCT id(n) AS id" RELS "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [[id(s), id(e)], [id(e), id(s)]] AS par RETURN DISTINCT par[0] AS source, par[1] AS target"
|> BFS FROM origem IN societaria MAX_DEPTH=2 ;Cuidados: Sem MAXDEPTH nem TARGETNODES, a lista cobre tudo o que é alcançável — numa rede societária inteira, centenas de milhares de ids numa linha só. A coluna path encadeia os nós na ordem de visita: dois vizinhos na lista não são necessariamente ligados entre si.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; preenchido pela sintaxe)TARGET_NODES→targetNodes(List of Integer; padrãoempty list)MAX_DEPTH→maxDepth(Integer; padrão-1)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: sourceNode, nodeIds, path
Grafo: direcionado ou não direcionado; sem peso nas ligações.
DFS
Percorre a rede a partir de um nó seguindo cada ramo até o fim antes de voltar e tentar o próximo.
O que faz: A partir da origem, desce por uma ligação, depois por uma ligação do nó alcançado, e assim por diante, até não haver para onde ir ou atingir MAXDEPTH; só então volta ao último ponto com ligações ainda não exploradas. Segue o sentido das arestas, não usa peso e para ao encontrar o primeiro nó de TARGETNODES. Devolve uma linha com os nós na ordem de visita, agrupados ramo a ramo.
Quando usar: Para seguir cadeias até o fim — participações descendo de uma holding —, testar se um alvo aparece em algum ramo ou listar a estrutura ramo por ramo. A ordem não reflete proximidade: para saber quem está mais perto, use o BFS.
Em linguagem de negócio:
- Seguindo as participações a partir da holding X, até onde vai cada cadeia de controle?
- Descendo pela estrutura societária do grupo A, a empresa Y aparece em algum ramo?
- Liste as empresas controladas pela empresa X, ramo por ramo, até o último nível.
- A partir do sócio X, siga cada ligação até o fim: aonde ela leva?
- Existe alguma sequência de participações que leve da empresa A até a empresa B?
Como usar: Projete a rede no sentido que a pergunta percorre — para descer do controlador ao controlado, RELS de quem é sócia para a empresa investida — e informe a origem pelo FROM, com o elementId obtido num MATCH anterior. TARGETNODES recebe ids internos separados por vírgula para parar no primeiro encontrado, MAXDEPTH (padrão -1, sem limite) corta a profundidade e CONCURRENCY vai até 4. A linha devolvida traz sourceNode, nodeIds (ids internos na ordem de visita) e path; quando o alvo é encontrado, ele é o último id da lista.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (h:Empresa {empresaCnpj: '12345678'}) RETURN elementId(h) AS origem"
|> PROJECT GRAPH participacoes NODES "MATCH (a:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE a <> b UNWIND [a, b] AS e RETURN DISTINCT id(e) AS id" RELS "MATCH (a:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE a <> b RETURN DISTINCT id(a) AS source, id(b) AS target"
|> DFS FROM origem IN participacoes TARGET_NODES="98765", MAX_DEPTH=6 ;Cuidados: A ordem de visita não mede distância, e a coluna path só encadeia os nós nessa ordem, sem representar ligações reais entre ids vizinhos da lista. TARGET_NODES usa o id interno (id(n)), enquanto o FROM usa o elementId.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; preenchido pela sintaxe)TARGET_NODES→targetNodes(List of Integer; padrãoempty list)MAX_DEPTH→maxDepth(Integer; padrão-1)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: sourceNode, nodeIds, path
Grafo: direcionado ou não direcionado; sem peso nas ligações.
RANDOM_WALK
Gera percursos aleatórios pela rede a partir de nós escolhidos, sorteando cada passo entre os vizinhos, com chance proporcional ao peso quando há peso.
O que faz: De cada nó de partida saem WALKSPERNODE percursos de até WALKLENGTH nós; a cada passo o próximo vizinho é sorteado, com chance proporcional ao peso da ligação quando há peso. Dois fatores inclinam o sorteio: RETURNFACTOR alto torna menos provável voltar ao nó anterior, e INOUTFACTOR alto mantém o percurso perto de onde está, enquanto baixo o empurra para longe. O percurso acaba antes do comprimento pedido quando chega a um nó sem ligação de saída.
Quando usar: Para amostrar a vizinhança de um conjunto de nós — com que frequência outros nós aparecem por perto, ou como entrada de modelos de embedding e de similaridade — quando enumerar tudo seria caro demais. Não responde pergunta exata de ligação: para isso, use os algoritmos de caminho.
Em linguagem de negócio:
- Quais empresas aparecem com frequência nos arredores da empresa X quando se percorre a rede ao acaso?
- Gere amostras de trajetos pela rede de sócios a partir destas empresas para treinar um modelo.
- Que empresas costumam surgir perto do grupo suspeito ao navegar pelos vínculos de forma aleatória?
- Monte sequências de empresas vizinhas a partir destes laranjas para uma análise de padrões.
- Com que facilidade quem sai da empresa X, andando ao acaso pelos vínculos, esbarra na empresa Y?
Como usar: Projete a rede (ligações nos dois sentidos quando o vínculo não tem direção) e passe os nós de partida em SOURCENODES, como ids internos separados por vírgula (`SOURCENODES="4211,5873"`); sem essa opção, saem percursos de todos os nós. WALKLENGTH (padrão 80), WALKSPERNODE (padrão 10), RETURNFACTOR e INOUTFACTOR (padrão 1.0), RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIPWEIGHT), RANDOMSEED (fixe para repetir o resultado), WALKBUFFERSIZE (padrão 1000) e CONCURRENCY (até 4) ajustam a amostragem. Cada linha é um percurso: nodeIds (ids internos na ordem) e path.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH vinculos NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa) WHERE e1 <> e2 AND EXISTS { (e2)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) } RETURN id(e1) AS source, id(e2) AS target, count(DISTINCT p) AS socios"
|> RANDOM_WALK ON vinculos SOURCE_NODES="4211,5873", WALK_LENGTH=8, WALKS_PER_NODE=50, RELATIONSHIP_WEIGHT_PROPERTY="socios", RANDOM_SEED=42 ;Cuidados: Sem SOURCENODES, a saída tem WALKSPER_NODE linhas para cada nó da projeção — numa rede grande, milhões de linhas. Peso negativo é recusado, e nó sem ligação de saída não gera percurso.
Parâmetros:
SOURCE_NODES→sourceNodes(List of Integer; padrãoList of all nodes)WALK_LENGTH→walkLength(Integer; padrão80)WALKS_PER_NODE→walksPerNode(Integer; padrão10)IN_OUT_FACTOR→inOutFactor(Float; padrão1.0)RETURN_FACTOR→returnFactor(Float; padrão1.0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)RANDOM_SEED→randomSeed(Integer; padrãorandom)WALK_BUFFER_SIZE→walkBufferSize(Integer; padrão1000)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeIds, path
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
BELLMAN_FORD
Calcula, a partir de um nó de origem, o caminho de menor custo até cada nó alcançável aceitando pesos negativos, e aponta os ciclos negativos quando eles existem.
O que faz: Repete rodadas de ajuste: a cada rodada, os nós cuja distância melhorou repassam a melhora aos vizinhos, até nada mais mudar. Como não depende de fixar o nó mais próximo, funciona com pesos negativos. Se achar um ciclo cuja soma de pesos é negativa — dar voltas nele baratearia o custo sem fim —, devolve só esses ciclos, marcados com isNegativeCycle verdadeiro, no lugar dos caminhos.
Quando usar: Quando há pesos negativos de fato (créditos, abatimentos, ganhos) ou quando se quer detectar circuitos de saldo negativo; com todos os pesos positivos, Dijkstra ou Delta respondem o mesmo mais depressa. Com peso -1 em toda ligação, qualquer ciclo alcançável vira ciclo negativo — um jeito de receber os próprios circuitos, que o SCC apenas agrupa.
Em linguagem de negócio:
- Existe participação circular — A sócia de B, B sócia de C e C sócia de A — a partir da empresa X?
- Há empresas que se controlam mutuamente, fechando um círculo de participações?
- Há algum circuito de operações entre empresas que gera crédito a cada volta?
- Considerando créditos e débitos em cada vínculo, qual o saldo acumulado da empresa X até cada empresa ligada?
- Quais participações cruzadas envolvem as empresas controladas pelo grupo X?
Como usar: Projete a rede com a coluna de peso no RELS — no exemplo, -1.0 AS peso em toda participação de empresa em empresa — e informe o nó de partida na opção obrigatória SOURCENODE, que recebe o id interno (o mesmo id(n) da projeção). RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIPWEIGHT) aponta o peso e CONCURRENCY vai até 4. Cada linha traz index, sourceNode, targetNode, totalCost, nodeIds (ids internos), costs, route (o percurso) e isNegativeCycle, que diz se a linha é um ciclo negativo em vez de um caminho.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH participacoes NODES "MATCH (a:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE a <> b UNWIND [a, b] AS e RETURN DISTINCT id(e) AS id" RELS "MATCH (a:Empresa)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE a <> b RETURN DISTINCT id(a) AS source, id(b) AS target, -1.0 AS peso"
|> GDS BELLMAN_FORD ON participacoes SOURCE_NODE=4211, RELATIONSHIP_WEIGHT_PROPERTY="peso"
|> FILTER isNegativeCycle = TRUE ;Cuidados: Basta um ciclo negativo alcançável para a resposta trazer só os ciclos e nenhum caminho comum. É mais lento que o Dijkstra, porque refaz rodadas até estabilizar: recorte a projeção. SOURCE_NODE é o id interno, não o elementId.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; obrigatório)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, route, isNegativeCycle
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
STEINER_TREE
Monta, a partir de um nó raiz, uma árvore de custo baixo que alcança todos os nós-alvo escolhidos, passando só pelos intermediários necessários.
O que faz: Resolve de forma aproximada o problema de Steiner dirigido: repetidamente, procura o alvo ainda não conectado que sai mais barato alcançar a partir da árvore já montada, por caminhos de menor custo, e incorpora o trecho que o liga. Com APPLY_REROUTING, tenta depois religar partes da árvore por trechos mais baratos. O resultado é a lista de nós da árvore, cada um com o seu pai e o custo da ligação entre eles.
Quando usar: Quando se quer ligar um conjunto específico de nós — a raiz e os alvos — com o menor custo total, aceitando intermediários; difere da árvore geradora, que cobre todos os nós alcançáveis, e do caminho de par, que liga só dois. O resultado é aproximado, sem garantia de ser o ótimo.
Em linguagem de negócio:
- Qual a menor rede de vínculos que liga a empresa X às empresas A, B e C ao mesmo tempo?
- Quais intermediários bastam para conectar o empresário X a todas as empresas da lista?
- Qual a estrutura mínima de sócios e empresas que une estas empresas suspeitas a partir da empresa X?
- Que pessoas estão no centro do esquema que liga estes CNPJs entre si?
- Como a holding X alcança todas estas empresas com o menor número de ligações somadas?
Como usar: Projete a rede (ligações nos dois sentidos quando o vínculo não tem direção) e informe as opções obrigatórias SOURCENODE (a raiz) e TARGETNODES (os alvos), as duas com ids internos — a lista como texto separado por vírgula (TARGET_NODES="5873,6120,7302"). São opcionais RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIPWEIGHT), DELTA (padrão 2.0, largura das faixas da busca interna), APPLYREROUTING (booleano em minúsculas, padrão false) e CONCURRENCY (até 4). Cada linha é um nó da árvore: nodeId, parentId (o nó de onde ele é alcançado; na raiz, ela mesma) e weight (custo da ligação com o pai).
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH societaria NODES "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [s, e] AS n RETURN DISTINCT id(n) AS id" RELS "MATCH (s)-[:TEM_SOCIEDADE_PESSOA|TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) UNWIND [[id(s), id(e)], [id(e), id(s)]] AS par RETURN DISTINCT par[0] AS source, par[1] AS target"
|> GDS STEINER_TREE ON societaria SOURCE_NODE=4211, TARGET_NODES="5873,6120,7302" ;Cuidados: Alvo que a raiz não alcança fica de fora sem erro — confira se todos os alvos aparecem em nodeId. Pesos negativos não são suportados, e os ids são internos (id(n)), não elementId.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; obrigatório)TARGET_NODES→targetNodes(List of Integer; obrigatório)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)DELTA→delta(Float; padrão2.0)APPLY_REROUTING→applyRerouting(Boolean; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, parentId, weight
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
SPANNING_TREE
Monta, a partir de um nó, a árvore que liga todos os nós alcançáveis com o menor — ou o maior — peso total.
O que faz: Usa o algoritmo de Prim: começa pela origem e, a cada passo, incorpora a ligação de menor peso (ou de maior, com OBJECTIVE="maximum") que leva a um nó ainda fora da árvore, até cobrir todo o componente da origem. O resultado não tem ciclo nem ligação redundante: cada nó aparece uma vez, com o pai e o peso da ligação escolhida.
Quando usar: Para achar o esqueleto de um grupo: com OBJECTIVE="maximum", as ligações mais fortes que mantêm todos conectados; com "minimum", a forma mais barata de ligar todos. Para ligar só alguns nós escolhidos, use a árvore de Steiner.
Em linguagem de negócio:
- Qual o esqueleto de ligações mais fortes que mantém o grupo da empresa X conectado?
- Quais vínculos formam a espinha dorsal das empresas ligadas a X, sem redundância?
- Como ligar todas as empresas do grupo usando só as conexões com mais sócios em comum?
- Qual a forma mais econômica de conectar todas as empresas alcançáveis a partir de X?
- Desenhe o organograma mínimo de ligações entre as empresas de Maceió ligadas à empresa X.
Como usar: Projete a rede com uma linha por par ligado e o peso no RELS — no exemplo, count(DISTINCT p) AS socios, os sócios em comum — e informe a origem na opção obrigatória SOURCENODE, com o id interno do nó. RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIPWEIGHT) aponta o peso, OBJECTIVE escolhe "minimum" (padrão) ou "maximum" e CONCURRENCY vai até 4. Cada linha é um nó da árvore: nodeId, parentId (na origem, ela mesma) e weight (peso da ligação com o pai).
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH vinculos NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(e) AS id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa) WHERE id(e1) < id(e2) AND EXISTS { (e2)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) } RETURN id(e1) AS source, id(e2) AS target, count(DISTINCT p) AS socios"
|> GDS SPANNING_TREE ON vinculos SOURCE_NODE=4211, RELATIONSHIP_WEIGHT_PROPERTY="socios", OBJECTIVE="maximum" ;Cuidados: O algoritmo trata as ligações sem sentido — cada par ligado vale nos dois sentidos —, então basta uma linha por par no RELS. A árvore cobre apenas o que a origem alcança: empresas sem ligação com ela ficam de fora da resposta.
Parâmetros:
SOURCE_NODE→sourceNode(Integer; obrigatório)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)OBJECTIVE→objective(String; padrão'minimum')CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, parentId, weight
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; aceita peso nas ligações.
TOPOLOGICAL_SORT
Ordena os nós de uma rede sem ciclos de modo que todo nó venha antes daqueles para os quais aponta.
O que faz: Começa pelos nós sem ligação de entrada e, à medida que eles saem da fila, libera os nós cujas entradas já foram todas atendidas, até esgotar a rede. Nós que fazem parte de um ciclo — ou que só são alcançados passando por um — nunca ficam livres e ficam fora da ordem. Com COMPUTEMAXDISTANCEFROMSOURCE, calcula também, para cada nó, o número de ligações da cadeia mais longa que chega a ele a partir de um nó sem entrada.
Quando usar: Para ordenar dependências — controladora antes de controlada — e descobrir a camada de cada nó numa hierarquia; exige grafo acíclico. Para o percurso da cadeia mais longa, e não só a ordem, use o DAGLONGESTPATH; para localizar os ciclos que atrapalham, o SCC.
Em linguagem de negócio:
- Em que ordem analisar as empresas do grupo X, das controladoras para as controladas?
- Em que camada da estrutura societária do grupo X cada empresa está?
- Quais empresas estão no topo da pirâmide de participações e quais estão na base?
- Quantos níveis de holdings existem acima de cada empresa do grupo?
- A estrutura de participações do grupo é uma hierarquia limpa ou tem empresas que se controlam em círculo?
Como usar: Projete só as ligações de dependência, no sentido da hierarquia — de quem é sócia para a empresa investida —, num recorte do grupo; no exemplo, tudo o que desce da holding. Não há opção obrigatória: COMPUTEMAXDISTANCEFROMSOURCE=true (booleano em minúsculas; padrão false) liga o cálculo da camada, e CONCURRENCY vai até 4. As linhas saem na ordem topológica, com nodeId (id interno) e maxDistanceFromSource (camada do nó; nula quando o cálculo está desligado).
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH grupo NODES "MATCH (:Empresa {empresaCnpj: '12345678'})-[:TEM_SOCIEDADE_EMPRESA|ASSOCIADA_A_EMPRESA*0..12]->(e:Empresa) RETURN DISTINCT id(e) AS id" RELS "MATCH (:Empresa {empresaCnpj: '12345678'})-[:TEM_SOCIEDADE_EMPRESA|ASSOCIADA_A_EMPRESA*0..12]->(e:Empresa) WITH collect(DISTINCT e) AS grupo UNWIND grupo AS a MATCH (a)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE b IN grupo AND a <> b RETURN DISTINCT id(a) AS source, id(b) AS target"
|> GDS TOPOLOGICAL_SORT ON grupo COMPUTE_MAX_DISTANCE_FROM_SOURCE=true ;Cuidados: O grafo precisa ser acíclico: nós em ciclo, e tudo o que depende deles, ficam fora da ordem sem erro — rode antes o SCC no mesmo recorte, e componente com mais de um nó é ciclo. O peso não entra no cálculo: a camada conta ligações.
Parâmetros:
COMPUTE_MAX_DISTANCE_FROM_SOURCE→computeMaxDistanceFromSource(Boolean; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, maxDistanceFromSource
Grafo: só direcionado (as ligações precisam ter sentido); sem peso nas ligações.
DAGLONGESTPATH
Numa rede sem ciclos, encontra para cada nó a cadeia mais longa — de maior custo — que termina nele.
O que faz: Percorre a rede na ordem topológica, a partir dos nós sem ligação de entrada, e para cada nó guarda o predecessor que lhe dá o maior custo acumulado; como não há ciclo, isso resolve em tempo linear um problema que, em redes com ciclo, não tem solução prática. Devolve uma linha por nó alcançado, com a cadeia mais longa que termina nele; sourceNode é o início dela, um nó sem entrada. Sem peso, cada ligação vale 1 e o custo é o número de ligações.
Quando usar: Para medir a profundidade de hierarquias e cadeias de dependência — camadas de holdings, sequência crítica de etapas — em grafo acíclico. Para só a ordem e a camada, basta o TOPOLOGICAL_SORT; em rede com ciclos, o resultado não vale.
Em linguagem de negócio:
- Qual a cadeia mais longa de empresas-sócias acima da empresa X?
- Quantas camadas de holdings separam o controlador do topo da empresa X?
- Quais empresas ficam no fim das cadeias de participação mais longas?
- Onde a estrutura societária é mais profunda, com mais empresas intermediárias empilhadas?
- Qual a sequência completa de controladoras, de cima a baixo, até chegar à empresa X?
Como usar: Projete as ligações no sentido da hierarquia — de quem é sócia para a empresa investida — num recorte acíclico; no exemplo, todas as empresas acima da empresa X. Não há opção obrigatória: RELATIONSHIPWEIGHTPROPERTY (nome legado RELATIONSHIP_WEIGHT) troca a contagem de ligações por um peso, e CONCURRENCY vai até 4. Cada linha é um nó de chegada: targetNode, sourceNode (início da cadeia), totalCost, nodeIds (ids internos, de cima para baixo), costs, path e index; ordene por totalCost para ver as mais longas.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH acima NODES "MATCH (e:Empresa)-[:TEM_SOCIEDADE_EMPRESA|ASSOCIADA_A_EMPRESA*0..12]->(:Empresa {empresaCnpj: '12345678'}) RETURN DISTINCT id(e) AS id" RELS "MATCH (e:Empresa)-[:TEM_SOCIEDADE_EMPRESA|ASSOCIADA_A_EMPRESA*0..12]->(:Empresa {empresaCnpj: '12345678'}) WITH collect(DISTINCT e) AS acima UNWIND acima AS a MATCH (a)-[:TEM_SOCIEDADE_EMPRESA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(b:Empresa) WHERE b IN acima AND a <> b RETURN DISTINCT id(a) AS source, id(b) AS target"
|> GDS DAG_LONGEST_PATH ON acima
|> ORDER BY totalCost DESC
|> LIMIT 1 ;Cuidados: Exige grafo acíclico: participação circular faz os nós do ciclo, e os que dependem dele, sumirem da resposta ou virem com a cadeia incompleta — confira antes com o SCC. Os nós sem entrada aparecem como cadeia de um nó só, com custo 0.
Parâmetros:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Grafo: só direcionado (as ligações precisam ter sentido); aceita peso nas ligações.
Topologia
ARTICULATION_POINTS
Encontra os nós que, se removidos, partem a rede em pedaços desconectados.
O que faz: O algoritmo percorre a rede em profundidade uma única vez, anotando a ordem de visita de cada nó e o ponto mais antigo que seus descendentes conseguem alcançar por outro caminho. Quando nenhum descendente consegue voltar para antes de um nó sem passar por ele, esse nó é um ponto de articulação. Para cada um, o algoritmo informa em quantos pedaços o seu grupo se partiria e o tamanho do maior e do menor pedaço.
Quando usar: Use para achar pontos únicos de falha, quem sozinho mantém grupos unidos. Para essa pergunta é mais direto que o BETWEENNESS, porque responde sim ou não e diz o tamanho do estrago; para achar o vínculo (e não o nó) que sustenta a ligação, use o BRIDGES.
Em linguagem de negócio:
- Quais sócios são o único elo entre grupos de construtoras que, sem eles, ficariam separados?
- Se uma pessoa sair do quadro societário, quais grupos de empresas perdem contato entre si?
- Quem é a peça-chave cuja saída desmontaria a rede de empresas?
- Quais empresas seguram sozinhas a ligação entre diferentes núcleos de sócios?
- Onde estão os pontos únicos de falha na teia societária de um setor?
Como usar: Projete a relação uma única vez por par (no exemplo, cada vínculo entre pessoa e construtora de Campinas, com DISTINCT). A única opção é CONCURRENCY. A saída traz só os nós que são pontos de articulação, com nodeId e resultingComponents, um mapa com count (em quantos pedaços o grupo se parte), max e min (tamanho do maior e do menor pedaço); os casos que mais importam são os de min alto, em que a saída separa grupos grandes.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH obras
NODES "MATCH (m:Municipio {municipioNome: 'CAMPINAS', estadoSigla: 'SP'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (s)-[:TEM_CNAE]->(c:CNAE) WHERE c.cnaeCodigoprimario STARTS WITH '4120' UNWIND [p, e] AS n RETURN DISTINCT id(n) AS id"
RELS "MATCH (m:Municipio {municipioNome: 'CAMPINAS', estadoSigla: 'SP'}) MATCH (p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa)-[:TEM_ESTABELECIMENTO]->(s:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (s)-[:TEM_CNAE]->(c:CNAE) WHERE c.cnaeCodigoprimario STARTS WITH '4120' RETURN DISTINCT id(p) AS source, id(e) AS target"
|> GDS ARTICULATION_POINTS ON obras ;Cuidados: A GDS só executa este algoritmo sobre projeção não direcionada, em que cada ligação vale nos dois sentidos; devolva cada par uma única vez no RELS. Em redes esparsas, como a de sócios, quase todo nó que liga uma ponta solta ao resto é ponto de articulação com min igual a 1; foque nos que separam pedaços grandes. O percurso é linear no tamanho da rede, mas roda em sequência, então o custo maior costuma estar na consulta que monta a projeção.
Parâmetros:
CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, resultingComponents
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; sem peso nas ligações.
BRIDGES
Encontra as ligações que, se desfeitas, separam a rede em duas partes.
O que faz: O algoritmo faz uma busca em profundidade e, para cada aresta percorrida, verifica se o lado de baixo consegue voltar ao lado de cima por outro caminho; se não consegue, a aresta é uma ponte. Para cada ponte, informa as duas pontas e o tamanho dos dois grupos que sobrariam sem ela.
Quando usar: Use quando a pergunta é sobre o vínculo, e não sobre o nó: qual participação ou sociedade é a única ligação entre dois grupos. Para achar o nó cuja saída parte a rede, use ARTICULATION_POINTS.
Em linguagem de negócio:
- Qual vínculo societário é a única ligação entre dois grupos de empresas de obras rodoviárias?
- Se esta sociedade for desfeita, quais grupos de empresas perdem contato?
- Quais participações sustentam sozinhas a ligação entre dois núcleos econômicos?
- Onde a rede de sócios de um setor tem um fio único ligando dois lados?
- Que relações entre empresas, se cortadas, isolariam um grupo inteiro?
Como usar: Projete a relação uma única vez por par (no exemplo, empresas de construção de rodovias ligadas por sócios em comum, com id(e1) < id(e2) e DISTINCT). A única opção é CONCURRENCY. A saída traz uma linha por ponte, com from e to (os ids internos das duas pontas) e remainingSizes, o tamanho dos dois grupos que sobram sem ela; o segundo número é o lado de to, e as pontes que importam são as que deixam dois números grandes.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rodovias
NODES "MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_CNAE]->(c:CNAE) WHERE c.cnaeCodigoprimario STARTS WITH '4211' RETURN DISTINCT id(e) AS id"
RELS "MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_CNAE]->(c1:CNAE) WHERE c1.cnaeCodigoprimario STARTS WITH '4211' MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_CNAE]->(c2:CNAE) WHERE c2.cnaeCodigoprimario STARTS WITH '4211' AND id(e1) < id(e2) RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS BRIDGES ON rodovias ;Cuidados: A GDS só executa este algoritmo sobre projeção não direcionada; devolva cada par uma única vez, porque duas arestas entre o mesmo par formam um caminho duplo e nunca são ponte. Em redes esparsas, quase toda ligação com uma ponta solta é ponte, com um dos tamanhos igual a 1; foque nas que separam grupos grandes. O percurso é linear no tamanho da rede, e o custo maior costuma estar na consulta que monta a projeção.
Parâmetros:
CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: from, to, remainingSizes
Grafo: só não direcionado — a plataforma trata as ligações sem sentido automaticamente; sem peso nas ligações.
Similaridade
NODE_SIMILARITY
Aponta os pares de entidades mais parecidos pelo conjunto de vizinhos que elas dividem, como empresas com os mesmos sócios.
O que faz: Cada nó com arestas de saída é representado pelo conjunto de vizinhos que essas arestas alcançam, e cada par recebe uma nota de 0 a 1 pela sobreposição dos dois conjuntos: por padrão, vizinhos em comum divididos pelo total de vizinhos distintos (JACCARD), com OVERLAP e COSINE como alternativas e peso de aresta opcional. Pares sem nenhum vizinho em comum ficam de fora. Cada nó guarda só os seus TOPK melhores pares, ou os BOTTOMK piores, e TOP_N limita o total da resposta. A comparação é exata, par a par, sem amostragem.
Quando usar: Use quando a semelhança está na estrutura — entidades que apontam para os mesmos vizinhos, como empresas com os mesmos sócios ou fornecedores com os mesmos clientes —, de preferência num grafo bipartido. Se a comparação é por atributos (capital, contagens, conjunto de CNAEs) ou por um embedding já calculado, prefira KNN, que é aproximado e escala melhor; para comparar só uma entidade ou um grupo contra os demais, use FILTEREDNODESIMILARITY.
Em linguagem de negócio:
- Quais pares de empresas do município dividem quase todos os sócios?
- Existem empresas com exatamente o mesmo quadro societário, como se fossem a mesma operação com CNPJs diferentes?
- Quais empresas são gêmeas umas das outras pela composição de sócios?
- Que sócios aparecem juntos, repetidamente, nas mesmas empresas?
- Quais fornecedores atendem praticamente a mesma carteira de clientes?
Como usar: Projete as arestas saindo da entidade a comparar para o que ela compartilha — de (Empresa) para (Pessoa), com RETURN id(e) AS source, id(p) AS target — e ponha as duas pontas no NODES: só nós com aresta de saída são comparados. Nenhuma opção é obrigatória: TOPK (padrão 10) fixa quantos pares cada nó recebe, SIMILARITYCUTOFF descarta notas baixas, DEGREECUTOFF ignora quem tem poucos vizinhos, SIMILARITYMETRIC troca JACCARD por OVERLAP ou COSINE e RELATIONSHIPWEIGHTPROPERTY usa uma coluna numérica do RELS como peso. A saída traz node1 e node2 — os id do NODES — e similarity, de 0 a 1; o mesmo par pode aparecer nas duas ordens.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH socios NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) UNWIND [id(e), id(p)] AS id RETURN DISTINCT id" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) RETURN DISTINCT id(e) AS source, id(p) AS target"
|> NODE_SIMILARITY ON socios TOP_K=5, DEGREE_CUTOFF=2, SIMILARITY_CUTOFF=0.5
|> ORDER BY similarity DESC
|> LIMIT 50 ;Cuidados: O custo cresce com o quadrado do número de nós comparados: projete um recorte (município, CNAE, grupo) em vez do banco inteiro. Empresas de sócio único empatam com nota 1,0 com todas as outras do mesmo sócio, e DEGREE_CUTOFF=2 as tira da comparação. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
SIMILARITY_CUTOFF→similarityCutoff(Float; padrão1e-42)DEGREE_CUTOFF→degreeCutoff(Integer; padrão1)UPPER_DEGREE_CUTOFF→upperDegreeCutoff(Integer; padrão2147483647)TOP_K→topK(Integer; padrão10)BOTTOM_K→bottomK(Integer; padrão10)TOP_N→topN(Integer; padrão0)BOTTOM_N→bottomN(Integer; padrão0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SIMILARITY_METRIC→similarityMetric(String; padrãoJACCARD)USE_COMPONENTS→useComponents(Boolean or String; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: node1, node2, similarity
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
FILTEREDNODESIMILARITY
Faz a mesma comparação por vizinhos em comum do NODE_SIMILARITY, mas só entre um grupo de origem e um grupo de destino escolhidos.
O que faz: Usa a nota do NODE_SIMILARITY — sobreposição dos conjuntos de vizinhos, JACCARD por padrão —, mas só calcula pares cujo primeiro nó passa no filtro de origem e o segundo no filtro de destino. Cada filtro aceita um id interno (o id do NODES) ou um rótulo da projeção, criado pela coluna labels do NODES. Os vizinhos continuam sendo todos os projetados: o filtro só restringe quem é comparado, e as comparações encolhem junto com os grupos.
Quando usar: Use quando a pergunta parte de uma entidade ou de um grupo definido — quem se parece com a empresa X, quais empresas novas repetem os sócios das já investigadas — em vez de todos contra todos. Para todos os pares de um recorte, use NODESIMILARITY; se a semelhança for por atributos, e não por vizinhos, use FILTEREDKNN.
Em linguagem de negócio:
- Quais empresas têm quadro societário parecido com o da empresa X?
- Que outras empresas são comandadas praticamente pelas mesmas pessoas que a empresa X?
- A empresa X tem alguma empresa espelho, com o mesmo grupo de sócios?
- Entre as empresas abertas neste ano, quais repetem os sócios das empresas já investigadas?
- Quais fornecedores disputam a mesma carteira de clientes que o fornecedor Y?
Como usar: Projete como no NODESIMILARITY — arestas da entidade comparada para o que ela compartilha — e inclua no NODES a coluna labels, uma lista de texto por nó, que cria os rótulos usados nos filtros (por exemplo, CASE WHEN n.empresaCnpj = '12345678' THEN ['Referencia'] ELSE labels(n) END AS labels). SOURCENODEFILTER e TARGETNODEFILTER são obrigatórios: cada um recebe um id interno (número) ou um rótulo entre aspas. TOPK, SIMILARITYCUTOFF, DEGREECUTOFF e SIMILARITYMETRIC funcionam como no NODESIMILARITY, e a saída é node1 (da origem), node2 (do destino) e similarity, de 0 a 1.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH quadro NODES "MATCH (:Empresa {empresaCnpj: '12345678'})<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) WITH DISTINCT e MATCH (e)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) UNWIND [e, p] AS n WITH DISTINCT n RETURN id(n) AS id, CASE WHEN n.empresaCnpj = '12345678' THEN ['Referencia'] ELSE labels(n) END AS labels" RELS "MATCH (:Empresa {empresaCnpj: '12345678'})<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) WITH DISTINCT e MATCH (e)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) RETURN DISTINCT id(e) AS source, id(p) AS target"
|> GDS FILTERED_NODE_SIMILARITY ON quadro SOURCE_NODE_FILTER="Referencia", TARGET_NODE_FILTER="Empresa", TOP_K=20
|> ORDER BY similarity DESC ;Cuidados: O rótulo usado no filtro precisa existir na projeção: sem a coluna labels no NODES, os nós ficam sem rótulo e o filtro por nome é recusado. Traga ao NODES e ao RELS todos os sócios de cada empresa candidata, e não só os que ela divide com a origem, senão o total de vizinhos encolhe e a nota sai inflada. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
SIMILARITY_CUTOFF→similarityCutoff(Float; padrão1e-42)DEGREE_CUTOFF→degreeCutoff(Integer; padrão1)UPPER_DEGREE_CUTOFF→upperDegreeCutoff(Integer; padrão2147483647)TOP_K→topK(Integer; padrão10)BOTTOM_K→bottomK(Integer; padrão10)TOP_N→topN(Integer; padrão0)BOTTOM_N→bottomN(Integer; padrão0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SIMILARITY_METRIC→similarityMetric(String; padrãoJACCARD)USE_COMPONENTS→useComponents(Boolean or String; padrãofalse)SOURCE_NODE_FILTER→sourceNodeFilter(Integer or List of Integer or String; obrigatório)TARGET_NODE_FILTER→targetNodeFilter(Integer or List of Integer or String; obrigatório)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: node1, node2, similarity
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
KNN
Encontra, para cada entidade, as K mais parecidas por uma propriedade numérica ou lista de números, sem exigir vizinhos em comum.
O que faz: Compara os nós pelo valor de uma propriedade projetada: número isolado (nota 1/(1 + diferença)), lista de decimais (cosseno) ou lista de inteiros, tratada como conjunto (JACCARD). Em vez de testar todos os pares, parte de vizinhos sorteados e melhora a lista de cada nó examinando os vizinhos dos vizinhos, até as trocas ficarem abaixo de DELTATHRESHOLD ou se esgotarem as MAXITERATIONS. O resultado é aproximado, mas o custo fica perto do linear no número de nós; as arestas projetadas não entram na nota e só servem ao sorteio inicial quando INITIAL_SAMPLER="randomWalk".
Quando usar: Use quando a semelhança está nos atributos — capital, contagens, o conjunto de CNAEs, um embedding —, e não em vizinhos compartilhados; por ser aproximado, é a escolha para recortes grandes. Se parecido significa apontar para os mesmos vizinhos, use NODESIMILARITY, que é exato; para comparar só um grupo contra outro, use FILTEREDKNN.
Em linguagem de negócio:
- Quais empresas do mesmo grupo exercem praticamente as mesmas atividades, sugerindo faturamento dividido entre vários CNPJs?
- Que empresas têm o perfil mais próximo entre si em capital, filiais e número de sócios?
- Quais pares de empresas parecem clones uma da outra no cadastro?
- Que estabelecimentos se assemelham mais pelo conjunto de atividades econômicas?
- Quais empresas se parecem entre si mesmo sem nenhum sócio em comum?
Como usar: Projete no NODES só os nós a comparar, com a propriedade como coluna (RETURN id(e) AS id, [...] AS atividades); NODEPROPERTIES é obrigatório e recebe o nome de UMA coluna projetada, que precisa ter valor em todos os nós. O PROJECT GRAPH exige um RELS, e qualquer ligação entre os próprios nós projetados serve, porque a nota não usa as arestas. TOPK define quantos vizinhos cada nó recebe, SIMILARITYCUTOFF corta notas baixas, SAMPLERATE e MAXITERATIONS trocam precisão por tempo, e RANDOMSEED só é aceito com CONCURRENCY=1. A saída traz node1 e node2 — os id do NODES — e similarity, de 0 a 1.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH grupo NODES "MATCH (:Empresa {empresaCnpj: '12345678'})<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) WITH DISTINCT e RETURN id(e) AS id, COLLECT { MATCH (e)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_CNAE]->(c:CNAE) UNWIND split(c.cnaeCodigoprimario + ',' + coalesce(c.cnaeCodigosecundario, ''), ',') AS cod WITH DISTINCT toInteger(trim(cod)) AS codigo WHERE codigo > 0 RETURN codigo } AS atividades" RELS "MATCH (x:Empresa {empresaCnpj: '12345678'})<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e:Empresa) WHERE e <> x RETURN DISTINCT id(x) AS source, id(e) AS target"
|> KNN ON grupo NODE_PROPERTIES="atividades", TOP_K=3, SIMILARITY_CUTOFF=0.8
|> ORDER BY similarity DESC
|> LIMIT 50 ;Cuidados: O resultado é aproximado e pode mudar entre execuções: para repeti-lo, fixe RANDOM_SEED com CONCURRENCY=1. Listas de decimais devem ter o mesmo tamanho em todos os nós, e a de inteiros é comparada como conjunto, então tire as repetições com DISTINCT; nó sem valor na propriedade faz a execução falhar. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
NODE_PROPERTIES→nodeProperties(String or Map or List of Strings / Maps; obrigatório)TOP_K→topK(Integer; padrão10)SAMPLE_RATE→sampleRate(Float; padrão0.5)DELTA_THRESHOLD→deltaThreshold(Float; padrão0.001)MAX_ITERATIONS→maxIterations(Integer; padrão100)RANDOM_JOINS→randomJoins(Integer; padrão10)INITIAL_SAMPLER→initialSampler(String; padrão"uniform")RANDOM_SEED→randomSeed(Integer)SIMILARITY_CUTOFF→similarityCutoff(Float; padrão0.0)PERTURBATION_RATE→perturbationRate(Float; padrão0.0)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: node1, node2, similarity
FILTERED_KNN
Versão do KNN que busca os vizinhos mais parecidos só para os nós de origem escolhidos, e só entre os nós de destino escolhidos.
O que faz: Usa a mesma nota e o mesmo processo aproximado do KNN — vizinhos sorteados e refinados pelos vizinhos dos vizinhos —, mas só devolve resultado para os nós que passam no filtro de origem, e só com vizinhos que passam no filtro de destino. Cada filtro aceita um id interno (o id do NODES) ou um rótulo criado pela coluna labels do NODES. Com SEEDTARGETNODES ligado, a lista de cada origem já começa com nós de destino, e, se houver no máximo TOP_K destinos, a resposta sai exata.
Quando usar: Use quando a pergunta tem ponto de partida — as empresas mais parecidas com a empresa X, os fornecedores novos que lembram os já problemáticos — e a comparação é por atributos. Para todos os pares de um recorte, use KNN; se a semelhança for por vizinhos em comum, use FILTEREDNODESIMILARITY.
Em linguagem de negócio:
- Quais empresas no mesmo CEP da empresa X exercem as mesmas atividades que ela?
- Que empresas do município têm cadastro parecido com o da empresa X?
- Entre os fornecedores novos, quais se parecem com os que já deram problema?
- Existe outra empresa no mesmo endereço que copia o ramo de atuação da empresa X?
- Quais empresas lembram, pelo porte e pelas atividades, as que já foram autuadas?
Como usar: Projete os nós com a propriedade como coluna e acrescente a coluna labels (lista de texto) para marcar origem e destino, por exemplo CASE WHEN e.empresaCnpj = '12345678' THEN ['Referencia'] ELSE ['Empresa'] END AS labels. NODEPROPERTIES, SOURCENODEFILTER e TARGETNODEFILTER são obrigatórios: o primeiro nomeia a coluna comparada, e os filtros recebem um id interno (número) ou um rótulo entre aspas. TOPK, SIMILARITYCUTOFF e SEEDTARGET_NODES=true (valor lógico em minúsculas) ajustam a resposta, que traz node1 (origem), node2 (destino) e similarity, de 0 a 1.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH vizinhanca NODES "MATCH (:Empresa {empresaCnpj: '12345678'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(dx:Endereco) MATCH (:Endereco {enderecoCEP: dx.enderecoCEP})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WITH DISTINCT e RETURN id(e) AS id, CASE WHEN e.empresaCnpj = '12345678' THEN ['Referencia'] ELSE ['Empresa'] END AS labels, COLLECT { MATCH (e)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_CNAE]->(c:CNAE) UNWIND split(c.cnaeCodigoprimario + ',' + coalesce(c.cnaeCodigosecundario, ''), ',') AS cod WITH DISTINCT toInteger(trim(cod)) AS codigo WHERE codigo > 0 RETURN codigo } AS atividades" RELS "MATCH (x:Empresa {empresaCnpj: '12345678'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(dx:Endereco) MATCH (:Endereco {enderecoCEP: dx.enderecoCEP})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WHERE e <> x RETURN DISTINCT id(x) AS source, id(e) AS target"
|> FILTERED_KNN ON vizinhanca NODE_PROPERTIES="atividades", SOURCE_NODE_FILTER="Referencia", TARGET_NODE_FILTER="Empresa", TOP_K=10, SEED_TARGET_NODES=true
|> ORDER BY similarity DESC ;Cuidados: Como no KNN, o resultado é aproximado e só se repete com RANDOMSEED e CONCURRENCY=1; quando os destinos são poucos perto do total de nós, ligue SEEDTARGET_NODES para que eles não fiquem fora do sorteio. O rótulo do filtro precisa existir na projeção, e a propriedade precisa ter valor em todos os nós, inclusive nos que não são origem nem destino. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
NODE_PROPERTIES→nodeProperties(String or Map or List of Strings / Maps; obrigatório)TOP_K→topK(Integer; padrão10)SAMPLE_RATE→sampleRate(Float; padrão0.5)DELTA_THRESHOLD→deltaThreshold(Float; padrão0.001)MAX_ITERATIONS→maxIterations(Integer; padrão100)RANDOM_JOINS→randomJoins(Integer; padrão10)INITIAL_SAMPLER→initialSampler(String; padrão"uniform")RANDOM_SEED→randomSeed(Integer)SIMILARITY_CUTOFF→similarityCutoff(Float; padrão0.0)PERTURBATION_RATE→perturbationRate(Float; padrão0.0)SOURCE_NODE_FILTER→sourceNodeFilter(Integer or List of Integer or String; obrigatório)TARGET_NODE_FILTER→targetNodeFilter(Integer or List of Integer or String; obrigatório)SEED_TARGET_NODES→seedTargetNodes(Boolean; padrãofalse)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: node1, node2, similarity
Embeddings
FASTRP
Gera rápido, sem treino, um embedding por nó que resume a vizinhança dele na rede, para comparar ou agrupar entidades pela posição que ocupam.
O que faz: Sorteia um vetor esparso para cada nó e, a cada rodada, troca-o pela média dos vetores dos vizinhos, alcançando um salto a mais por rodada; o embedding final soma as rodadas com os pesos de ITERATIONWEIGHTS (padrão 0, 1 e 1: ignora o primeiro salto e usa o segundo e o terceiro). Nós com vizinhanças parecidas terminam com vetores próximos, sem nenhuma etapa de treinamento. Atributos numéricos entram na mistura por FEATUREPROPERTIES e PROPERTYRATIO, e NORMALIZATIONSTRENGTH negativa reduz a influência de nós muito conectados.
Quando usar: É o ponto de partida para representar a posição de cada entidade na rede: o mais rápido dos quatro embeddings, reproduzível com RANDOM_SEED e bom em grafos grandes. Prefira NODE2VEC para escolher entre mesmo círculo e mesmo papel, HASHGNN quando atributos categóricos e tipos de relacionamento devem pesar, e GRAPHSAGE quando precisar de um modelo que se aplique a nós ou recortes novos.
Em linguagem de negócio:
- Quais empresas ocupam posição parecida na rede de sócios, mesmo sem sócio em comum?
- Como separar as empresas do município em grupos pela forma como se ligam a pessoas e a outras empresas?
- Que sócios têm papel semelhante na teia de empresas da cidade?
- Quais empresas estão cercadas pelo mesmo tipo de vizinhança societária que uma empresa de fachada conhecida?
- Quais empresas do município ficam no mesmo bolsão da rede societária?
Como usar: Projete as arestas nos dois sentidos — a projeção do DATTAX é dirigida e o FASTRP só segue arestas de saída —, por exemplo com UNWIND [[id(e), id(p)], [id(p), id(e)]] AS par RETURN par[0] AS source, par[1] AS target. EMBEDDINGDIMENSION é obrigatório e define o tamanho do vetor; RANDOMSEED torna o resultado reproduzível e RELATIONSHIPWEIGHTPROPERTY usa um peso do RELS. A saída é nodeId (o id do NODES) e embedding, uma lista de números que pode ser comparada, agrupada ou materializada.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'RECIFE', estadoSigla: 'PE'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) UNWIND [id(e), id(p)] AS id RETURN DISTINCT id" RELS "MATCH (m:Municipio {municipioNome: 'RECIFE', estadoSigla: 'PE'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) WITH DISTINCT e, p UNWIND [[id(e), id(p)], [id(p), id(e)]] AS par RETURN par[0] AS source, par[1] AS target"
|> FASTRP ON rede EMBEDDING_DIMENSION=128, RANDOM_SEED=42
|> LIMIT 1000 ;Cuidados: Sem as arestas nos dois sentidos, boa parte dos nós termina com vetor zerado e a comparação perde o sentido. As coordenadas só valem dentro da mesma execução: outro recorte ou outra semente produzem vetores que não se comparam com os anteriores. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
PROPERTY_RATIO→propertyRatio(Float; padrão0.0)FEATURE_PROPERTIES→featureProperties(List of String; padrão[])EMBEDDING_DIMENSION→embeddingDimension(Integer; obrigatório)ITERATION_WEIGHTS→iterationWeights(List of Float; padrão[0.0, 1.0, 1.0])NODE_SELF_INFLUENCE→nodeSelfInfluence(Float; padrão0.0)NORMALIZATION_STRENGTH→normalizationStrength(Float; padrão0.0)RANDOM_SEED→randomSeed(Integer)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, embedding
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
HASHGNN
Gera, sem treino, um embedding por nó que mistura os atributos binários do próprio nó com os dos vizinhos por meio de hashing.
O que faz: Parte de atributos binários de cada nó — colunas 0/1 do NODES em FEATUREPROPERTIES, atributos contínuos convertidos em bits por BINARIZEFEATURES ou bits sorteados por GENERATEFEATURES — e, em cada uma das ITERATIONS, junta os bits do nó aos dos vizinhos e escolhe por min-hashing EMBEDDINGDENSITY deles. NEIGHBORINFLUENCE regula o peso dos vizinhos frente ao próprio nó, e HETEROGENEOUS=true trata cada tipo de relacionamento em separado. O resultado é binário, com a mesma dimensão dos atributos de entrada, ou, com OUTPUTDIMENSION, projetado num vetor denso daquele tamanho.
Quando usar: Use quando os nós têm atributos categóricos (porte, natureza jurídica, regime tributário) que devem pesar junto com a estrutura, ou quando há vários tipos de relacionamento: é rápido como o FASTRP e não exige treino como o GRAPHSAGE. Sem atributos, GENERATE_FEATURES produz um embedding só estrutural, mas nesse caso o FASTRP costuma ser a escolha mais simples.
Em linguagem de negócio:
- Quais empresas combinam o mesmo porte, a mesma natureza jurídica e o mesmo tipo de vizinhança societária?
- Como agrupar as empresas da cidade juntando o que elas são com a forma como se ligam?
- Que empresas do Simples estão cercadas por empresas de perfil parecido com o de um grupo investigado?
- Quais empresas têm entorno societário equivalente, considerando o perfil de cada vizinha?
- Quais empresas se parecem tanto no cadastro quanto nas ligações com outras empresas?
Como usar: Projete as arestas nos dois sentidos e ponha os atributos como colunas 0/1 do NODES (uma lista como [toInteger(porte = '01'), ...] AS perfil serve). ITERATIONS e EMBEDDINGDENSITY são obrigatórios, e é preciso escolher exatamente uma fonte de bits: FEATUREPROPERTIES (colunas 0/1, ou contínuas com BINARIZEFEATURES="dimension:64,threshold:0.0") ou GENERATEFEATURES="dimension:64,densityLevel:1". A saída é nodeId (o id do NODES) e embedding, binário ou denso com OUTPUT_DIMENSION.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'NATAL', estadoSigla: 'RN'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WITH DISTINCT e, coalesce(e.empresaPorte, '00') AS porte, coalesce(toString(e.empresaNjcod), '') AS nj RETURN id(e) AS id, [toInteger(porte = '01'), toInteger(porte = '03'), toInteger(porte = '05'), toInteger(nj = '2135'), toInteger(nj = '2062'), toInteger(nj = '2240'), toInteger(nj = '2305'), toInteger(EXISTS { (e)-[:TEM_SIMPLES]->() }), toInteger(EXISTS { (e)-[:TEM_MEI]->() })] AS perfil" RELS "MATCH (m:Municipio {municipioNome: 'NATAL', estadoSigla: 'RN'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> HASHGNN ON rede FEATURE_PROPERTIES="perfil", ITERATIONS=2, EMBEDDING_DENSITY=4, RANDOM_SEED=42
|> LIMIT 1000 ;Cuidados: Coluna de FEATUREPROPERTIES com valor diferente de 0 e 1 é recusada, a não ser que BINARIZEFEATURES esteja ligado, e ela precisa ter valor em todos os nós (use coalesce). Usar FEATUREPROPERTIES e GENERATEFEATURES juntos, ou nenhum dos dois, também é recusado. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
FEATURE_PROPERTIES→featureProperties(List of String; padrão[])ITERATIONS→iterations(Integer; obrigatório)EMBEDDING_DENSITY→embeddingDensity(Integer; obrigatório)HETEROGENEOUS→heterogeneous(Boolean; padrãofalse)NEIGHBOR_INFLUENCE→neighborInfluence(Float; padrão1.0)BINARIZE_FEATURES→binarizeFeatures(Map)GENERATE_FEATURES→generateFeatures(Map)OUTPUT_DIMENSION→outputDimension(Integer)RANDOM_SEED→randomSeed(Integer)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, embedding
Grafo: direcionado ou não direcionado; sem peso nas ligações.
NODE2VEC
Aprende um embedding por nó a partir de passeios aleatórios pela rede, de modo que nós que aparecem nos mesmos trajetos fiquem próximos.
O que faz: Dispara WALKSPERNODE passeios de WALKLENGTH passos a partir de cada nó e treina, sobre essas sequências, um modelo no estilo word2vec (skip-gram com amostragem negativa), em que nós que aparecem perto uns dos outros nos trajetos ganham vetores próximos. INOUTFACTOR e RETURNFACTOR moldam o passeio: INOUTFACTOR abaixo de 1 empurra o passeio para longe e destaca quem está no mesmo círculo, acima de 1 o prende perto da origem e destaca quem ocupa papel parecido, e RETURNFACTOR abaixo de 1 favorece voltar ao nó anterior. WINDOWSIZE, ITERATIONS e as taxas de aprendizado controlam o treino.
Quando usar: Use quando quiser escolher o tipo de proximidade — mesmo círculo (INOUTFACTOR < 1) ou mesmo papel na estrutura (INOUTFACTOR > 1) — e puder pagar um processamento mais lento que o do FASTRP. Não serve para nós que entram depois do cálculo: para isso, GRAPHSAGE; para atributos categóricos junto com a estrutura, HASHGNN.
Em linguagem de negócio:
- Quais empresas circulam no mesmo círculo de sócios, ainda que sem ligação direta entre elas?
- Que empresas desempenham o mesmo papel — ponte entre grupos, controladora, satélite — em grupos diferentes?
- Quais sócios transitam pelos mesmos ambientes empresariais?
- Como agrupar as empresas da cidade pela proximidade nos caminhos de sociedades?
- Quais empresas estão a poucos passos umas das outras na teia societária?
Como usar: Projete as arestas nos dois sentidos: o passeio só segue arestas de saída e para em nós sem saída. Nenhuma opção é obrigatória; EMBEDDINGDIMENSION (padrão 128), WALKLENGTH (80), WALKSPERNODE (10), INOUTFACTOR e RETURNFACTOR (1,0) são as que mais mudam o resultado, e RELATIONSHIPWEIGHT_PROPERTY faz o passeio preferir arestas de peso maior. A saída é nodeId (o id do NODES) e embedding, lista de números do tamanho pedido.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'ARACAJU', estadoSigla: 'SE'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) UNWIND [id(e), id(p)] AS id RETURN DISTINCT id" RELS "MATCH (m:Municipio {municipioNome: 'ARACAJU', estadoSigla: 'SE'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa) WITH DISTINCT e, p UNWIND [[id(e), id(p)], [id(p), id(e)]] AS par RETURN par[0] AS source, par[1] AS target"
|> NODE2VEC ON rede EMBEDDING_DIMENSION=64, WALK_LENGTH=20, WALKS_PER_NODE=10, IN_OUT_FACTOR=0.5, RANDOM_SEED=42
|> LIMIT 1000 ;Cuidados: Custa bem mais que o FASTRP, porque o trabalho cresce com passeios por nó vezes comprimento vezes nós: projete um recorte e comece com WALKLENGTH e WALKSPERNODE menores. O treino em paralelo faz o resultado variar entre execuções; para repeti-lo, fixe RANDOMSEED e use CONCURRENCY=1. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
WALK_LENGTH→walkLength(Integer; padrão80)WALKS_PER_NODE→walksPerNode(Integer; padrão10)IN_OUT_FACTOR→inOutFactor(Float; padrão1.0)RETURN_FACTOR→returnFactor(Float; padrão1.0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)WINDOW_SIZE→windowSize(Integer; padrão10)NEGATIVE_SAMPLING_RATE→negativeSamplingRate(Integer; padrão5)POSITIVE_SAMPLING_FACTOR→positiveSamplingFactor(Float; padrão0.001)NEGATIVE_SAMPLING_EXPONENT→negativeSamplingExponent(Float; padrão0.75)EMBEDDING_DIMENSION→embeddingDimension(Integer; padrão128)EMBEDDING_INITIALIZER→embeddingInitializer(String; padrãoNORMALIZED)ITERATIONS→iterations(Integer; padrão1)INITIAL_LEARNING_RATE→initialLearningRate(Float; padrão0.01)MIN_LEARNING_RATE→minLearningRate(Float; padrão0.0001)RANDOM_SEED→randomSeed(Integer; padrãorandom)WALK_BUFFER_SIZE→walkBufferSize(Integer; padrão1000)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, embedding
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
GRAPHSAGE
Gera um embedding por nó aplicando um modelo GraphSAGE já treinado com GRAPHSAGE_TRAIN, inclusive a nós que o modelo nunca viu.
O que faz: Busca no catálogo da GDS o modelo indicado em MODEL "nome" e, para cada nó da projeção, combina os atributos dele com os de uma amostra de vizinhos usando as funções aprendidas no treino. Como o modelo aprendeu a agregar atributos, e não a decorar nós, ele funciona em outro recorte com os mesmos atributos — outro município, empresas abertas depois do treino. O resultado é um vetor por nó, do tamanho definido no treino.
Quando usar: Use depois de um GRAPHSAGE_TRAIN, quando quiser embeddings comparáveis entre recortes ou ao longo do tempo, gerados pelo mesmo modelo. Sem modelo treinado, ou sem atributos que importem, FASTRP ou NODE2VEC resolvem sem treino.
Em linguagem de negócio:
- Como as empresas do município vizinho se encaixam no perfil aprendido na capital?
- Empresas recém-abertas já se parecem com quais perfis conhecidos?
- Quais empresas deste novo recorte têm estrutura parecida com as que analisamos antes?
- Dá para comparar empresas de cidades diferentes com a mesma régua?
Como usar: Projete o grafo com as MESMAS colunas de atributo usadas no treino, com os mesmos nomes, e as arestas nos dois sentidos. O nome do modelo vai na própria sintaxe (MODEL "nome") e não se repete como opção; BATCHSIZE e CONCURRENCY só mudam o desempenho. A saída é nodeId (o id do NODES) e embedding, lista de números do tamanho de EMBEDDINGDIMENSION no treino.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'CAMPINA GRANDE', estadoSigla: 'PB'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WITH DISTINCT e RETURN id(e) AS id, COUNT { (e)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa) } AS grau, log10(coalesce(e.empresaCsocial, 0.0) + 1.0) AS capital" RELS "MATCH (m:Municipio {municipioNome: 'CAMPINA GRANDE', estadoSigla: 'PB'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GRAPHSAGE MODEL "perfil_societario_pb" ON rede
|> LIMIT 1000 ;Cuidados: O modelo só existe na memória da GDS: some quando o Neo4j reinicia, porque a edição Community instalada não grava modelo em disco, e aí é preciso treinar de novo com GRAPHSAGE_TRAIN. Atributo ausente, ou com outro nome na projeção, faz a execução falhar, e o grafo precisa de pelo menos uma aresta. Na edição Community instalada, CONCURRENCY acima de 4 é recusado.
Parâmetros:
BATCH_SIZE→batchSize(Integer; padrão100)MODEL_NAME→modelName(String; preenchido pela sintaxe)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: nodeId, embedding
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
GRAPHSAGE_TRAIN
Treina, e guarda na memória da GDS, um modelo GraphSAGE que aprende a combinar os atributos de cada nó com os dos vizinhos, para depois gerar embeddings com GRAPHSAGE.
O que faz: Aprende funções de agregação (média ou pool) que, camada a camada, juntam os atributos numéricos de FEATUREPROPERTIES do nó com os de uma amostra de vizinhos (SAMPLESIZES, padrão 25 e 10) e produzem um vetor de EMBEDDINGDIMENSION posições. O treino não precisa de rótulos: nós que aparecem próximos em passeios curtos (SEARCHDEPTH) devem terminar com vetores próximos, e nós sorteados ao acaso, com vetores distantes. O modelo vai para o catálogo de modelos da GDS com o nome de MODEL_NAME, e a execução devolve modelInfo, configuration e trainMillis.
Quando usar: Use quando os nós têm atributos numéricos que importam e você quer um modelo reaproveitável: treinado num recorte, ele gera embeddings para nós novos ou para outro recorte com os mesmos atributos, como treinar num município e aplicar em outro. Se não há atributos nem necessidade de reaproveitar, FASTRP ou NODE2VEC são mais simples e rápidos.
Em linguagem de negócio:
- Dá para aprender o perfil típico das empresas de um município e usá-lo para avaliar as de outro?
- Como montar uma régua única de perfil — capital, sócios e ligações — para comparar empresas que ainda vão ser abertas?
- Podemos preparar uma base de comparação das empresas da capital e reaproveitá-la nas próximas análises?
- Como ensinar a plataforma a reconhecer empresas de estrutura parecida pelo cadastro e pelas ligações?
Como usar: Projete os nós com os atributos como colunas numéricas (COUNT {...} AS grau, log10(...) AS capital) e as arestas nos dois sentidos, com pelo menos uma aresta. MODELNAME (único e sem espaços) e FEATUREPROPERTIES (as colunas separadas por vírgula, num texto entre aspas) são obrigatórios; EMBEDDINGDIMENSION (padrão 64), EPOCHS, SAMPLESIZES e AGGREGATOR (mean ou pool) ajustam o modelo. A saída é uma linha com modelInfo (nome e métricas do treino, um mapa exibido como veio), configuration e trainMillis; os embeddings saem depois, com GRAPHSAGE MODEL "nome".
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH rede NODES "MATCH (m:Municipio {municipioNome: 'JOAO PESSOA', estadoSigla: 'PB'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e:Empresa) WITH DISTINCT e RETURN id(e) AS id, COUNT { (e)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa) } AS grau, log10(coalesce(e.empresaCsocial, 0.0) + 1.0) AS capital" RELS "MATCH (m:Municipio {municipioNome: 'JOAO PESSOA', estadoSigla: 'PB'}) MATCH (:Endereco {enderecoMunicipio: m.municipioCodigo})<-[:TEM_ENDERECO]-(:Estabelecimento)<-[:TEM_ESTABELECIMENTO]-(e1:Empresa)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) WHERE e1 <> e2 RETURN DISTINCT id(e1) AS source, id(e2) AS target"
|> GDS GRAPHSAGE_TRAIN ON rede MODEL_NAME="perfil_societario_pb", FEATURE_PROPERTIES="grau,capital", EMBEDDING_DIMENSION=64, EPOCHS=3, RANDOM_SEED=42 ;Cuidados: Na edição Community instalada o modelo fica só na memória: some quando o Neo4j reinicia, STOREMODELTO_DISK é recusado e cabem no máximo 3 modelos GraphSAGE — como o DATTAX não apaga modelos, o quarto treino falha até o próximo reinício. O catálogo pertence ao usuário técnico com que o motor acessa o Neo4j e é compartilhado por toda a plataforma, então repetir um nome já usado é recusado. Treinar custa bem mais que o FASTRP: projete um recorte e comece com poucas EPOCHS.
Parâmetros:
EMBEDDING_DIMENSION→embeddingDimension(Integer; padrão64)AGGREGATOR→aggregator(String; padrão"mean")ACTIVATION_FUNCTION→activationFunction(String; padrão"sigmoid")SAMPLE_SIZES→sampleSizes(List of Integer; padrão[25, 10])PROJECTED_FEATURE_DIMENSION→projectedFeatureDimension(Integer)BATCH_SIZE→batchSize(Integer; padrão100)TOLERANCE→tolerance(Float; padrão1e-4)LEARNING_RATE→learningRate(Float; padrão0.1)EPOCHS→epochs(Integer; padrão1)MAX_ITERATIONS→maxIterations(Integer; padrão10)BATCH_SAMPLING_RATIO→batchSamplingRatio(Float; padrãoconcurrency * batchSize / nodeCount)SEARCH_DEPTH→searchDepth(Integer; padrão5)NEGATIVE_SAMPLE_WEIGHT→negativeSampleWeight(Integer; padrão20)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)RANDOM_SEED→randomSeed(Integer; padrãorandom)PENALTY_L2→penaltyL2(Float; padrão0.0)STORE_MODEL_TO_DISK→storeModelToDisk(Boolean; padrãofalse)MODEL_NAME→modelName(String; obrigatório)FEATURE_PROPERTIES→featureProperties(List of String; obrigatório)CONCURRENCY→concurrency(Integer; padrão4)
Colunas devolvidas: modelInfo, configuration, trainMillis
Grafo: direcionado ou não direcionado; aceita peso nas ligações.
Predição de vínculo
LINKPREDICTIONADAMIC_ADAR
Pontua a chance de ligação entre duas entidades do banco somando os vizinhos em comum, com peso maior para os vizinhos pouco conectados.
O que faz: Lê do banco, sem projeção, os vizinhos diretos em comum dos dois nós e soma, para cada um, 1 dividido pelo logaritmo do número de conexões desse vizinho. Um vizinho compartilhado que liga pouca gente — uma sociedade de dois sócios, um contador com três clientes — vale mais que um ponto de passagem que liga todo mundo. RELATIONSHIP_QUERY e DIRECTION limitam o tipo e o sentido das arestas consideradas, inclusive na contagem de conexões de cada vizinho.
Quando usar: Use quando a rede tem pontos de passagem que inflariam a contagem simples e o que interessa é o elo exclusivo, indício típico de relação pessoal ou de operação conjunta. RESOURCEALLOCATION penaliza esses pontos com ainda mais força (1/conexões), e COMMONNEIGHBORS trata todos os vizinhos igual.
Em linguagem de negócio:
- Os sócios A e B provavelmente agem juntos, já que dividem sociedades pequenas?
- Há indício de vínculo oculto entre essas duas pessoas, dando mais peso aos elos raros?
- Duas empresas que usam o mesmo contador, pouco requisitado, têm ligação escondida?
- Qual a força da relação entre A e B, descontando os intermediários que ligam todo mundo?
- Esses dois nomes se cruzam em lugares exclusivos ou só em pontos de passagem comuns?
Como usar: Monte o par pelo MATCH do FROM GRAPH, devolvendo os elementIds (RETURN elementId(a) AS socio1, elementId(b) AS socio2), e passe as duas colunas no BETWEEN; não há PROJECT GRAPH e só a primeira linha é usada. RELATIONSHIP_QUERY e DIRECTION são opcionais. A saída é uma linha com score: 0 sem vizinho em comum, crescendo sem teto fixo com o número e a raridade dos vizinhos compartilhados.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (a:Pessoa), (b:Pessoa) WHERE a.pessoaUnico STARTS WITH '***111222**' AND a.pessoaNome STARTS WITH 'FULANO DE TAL' AND b.pessoaUnico STARTS WITH '***333444**' AND b.pessoaNome STARTS WITH 'BELTRANO DA SILVA' RETURN elementId(a) AS socio1, elementId(b) AS socio2"
|> LINK_PREDICTION ADAMIC_ADAR BETWEEN socio1 AND socio2 ;Cuidados: Mantenha DIRECTION em BOTH (padrão): em OUTGOING ou INCOMING, um vizinho com uma única conexão no sentido escolhido gera divisão por log(1) = 0 e a nota vira infinita — no banco cnpj, a Sociedade tem uma só aresta de saída. Só vizinhos diretos contam: no banco cnpj, duas empresas quase nunca têm vizinho direto em comum, e entre duas pessoas o vizinho em comum é a Sociedade em que entraram juntas ou o mesmo representante legal.
Parâmetros:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; padrãoBOTH)
Colunas devolvidas: score
LINKPREDICTIONCOMMON_NEIGHBORS
Conta quantos vizinhos diretos duas entidades do banco têm em comum: quanto mais, maior a chance de haver ligação entre elas.
O que faz: Lê direto do banco, sem projeção, os vizinhos imediatos, a um salto, de cada um dos dois nós e devolve o tamanho da interseção. RELATIONSHIPQUERY restringe a contagem a um tipo de relacionamento, e DIRECTION escolhe se valem as arestas de saída, de entrada ou as duas (padrão). É a medida mais simples de proximidade local; ADAMICADAR e RESOURCE_ALLOCATION partem desse mesmo conjunto e ponderam cada vizinho.
Quando usar: Use para uma leitura direta e fácil de explicar — os dois dividem N elos — quando todos os vizinhos em comum valem o mesmo. Se alguns vizinhos são pontos de passagem que ligam muita gente, como um escritório que atende milhares de empresas, prefira ADAMICADAR ou RESOURCEALLOCATION; para comparar muitos pares de uma vez, use NODE_SIMILARITY numa projeção.
Em linguagem de negócio:
- Em quantas sociedades os sócios A e B entraram juntos?
- Quantos contatos em comum essas duas pessoas têm no cadastro?
- A julgar pelos elos que compartilham, qual a chance de A e B estarem ligados?
- Essas duas pessoas aparecem juntas com frequência nas mesmas empresas?
- Os dois sócios têm o mesmo representante legal ou entradas conjuntas em sociedades?
Como usar: Não há PROJECT GRAPH: o MATCH do FROM GRAPH devolve os dois nós pelo elementId (RETURN elementId(a) AS socio1, elementId(b) AS socio2) e o BETWEEN recebe essas colunas — só a primeira linha é usada. RELATIONSHIPQUERY (por exemplo "TEMSOCIEDADE_PESSOA") e DIRECTION (BOTH, OUTGOING ou INCOMING) são opcionais. A saída é uma linha com score, a contagem de vizinhos em comum, que vale 0 quando não há nenhum.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (a:Pessoa), (b:Pessoa) WHERE a.pessoaUnico STARTS WITH '***555666**' AND a.pessoaNome STARTS WITH 'CICLANO SOUZA' AND b.pessoaUnico STARTS WITH '***777888**' AND b.pessoaNome STARTS WITH 'FULANA PEREIRA' RETURN elementId(a) AS socio1, elementId(b) AS socio2"
|> LINK_PREDICTION COMMON_NEIGHBORS BETWEEN socio1 AND socio2 RELATIONSHIP_QUERY="TEM_SOCIEDADE_PESSOA" ;Cuidados: Só vizinhos diretos contam: no banco cnpj, sócio e empresa se ligam pela Sociedade, então duas empresas quase nunca têm vizinho direto em comum — para empresas com sócios em comum, use NODE_SIMILARITY sobre uma projeção Empresa→Pessoa. Entre duas pessoas, o vizinho em comum é a Sociedade (entrada na mesma empresa, na mesma data e com a mesma qualificação) ou o mesmo representante legal.
Parâmetros:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; padrãoBOTH)
Colunas devolvidas: score
LINKPREDICTIONPREFERENTIAL_ATTACHMENT
Multiplica o número de conexões das duas entidades do banco: pares muito conectados tendem a se ligar, mesmo sem vizinho em comum.
O que faz: Lê do banco, sem projeção, quantos relacionamentos cada nó tem — do tipo e no sentido escolhidos em RELATIONSHIP_QUERY e DIRECTION — e devolve o produto das duas contagens. Não olha se há vizinhos em comum: traduz a ideia de que quem já tem muitos vínculos atrai novos vínculos, e de que dois grandes tendem a se encontrar. É útil como linha de base ou como ingrediente, ao lado das métricas de vizinhos em comum.
Quando usar: Use para medir o potencial de ligação entre entidades muito ativas — sócios com dezenas de empresas, empresas com grandes quadros societários — ou como termo de controle que separa o efeito do tamanho do efeito da proximidade. Não diz se A e B se conhecem; para isso, COMMONNEIGHBORS, ADAMICADAR ou RESOURCE_ALLOCATION.
Em linguagem de negócio:
- Duas empresas com quadros societários grandes tendem a acabar ligadas?
- Qual o potencial de conexão entre dois sócios que participam de muitas empresas?
- Esse par de grandes grupos tem porte para já estar interligado?
- Quanto do vínculo esperado entre A e B se explica só pelo tamanho da rede de cada um?
- Os sócios mais ativos da cidade tendem a se encontrar em alguma sociedade?
Como usar: Monte o par como nas demais funções: o MATCH do FROM GRAPH devolve os elementIds e o BETWEEN recebe as colunas (só a primeira linha vale; não há PROJECT GRAPH). RELATIONSHIPQUERY define o que conta como conexão — com "ASSOCIADAA_EMPRESA", o número de vínculos societários de cada empresa — e DIRECTION, o sentido (BOTH por padrão). A saída é score, o produto das duas contagens: 0 se um dos dois não tiver aresta, e sem teto.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (a:Empresa {empresaCnpj: '12345678'}), (b:Empresa {empresaCnpj: '87654321'}) RETURN elementId(a) AS empresa1, elementId(b) AS empresa2"
|> LINK_PREDICTION PREFERENTIAL_ATTACHMENT BETWEEN empresa1 AND empresa2 RELATIONSHIP_QUERY="ASSOCIADA_A_EMPRESA" ;Cuidados: Sem RELATIONSHIP_QUERY a contagem soma todos os tipos de relacionamento — no banco cnpj, filiais, vínculos societários, Simples e MEI juntos —, o que mistura coisas diferentes; escolha o tipo que responde à pergunta. A nota cresce com o tamanho, não com a proximidade: compare-a com a das métricas de vizinhos em comum antes de concluir.
Parâmetros:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; padrãoBOTH)
Colunas devolvidas: score
LINKPREDICTIONRESOURCE_ALLOCATION
Pontua a chance de ligação entre duas entidades do banco somando, para cada vizinho em comum, 1 dividido pelo número de conexões desse vizinho.
O que faz: Lê do banco, sem projeção, os vizinhos diretos em comum dos dois nós e soma 1/conexões de cada um: é como se cada vizinho repartisse uma unidade entre todos os que liga, e a fatia que chega ao par é maior quando ele liga pouca gente. Penaliza os pontos de passagem com mais força que o ADAMICADAR, que divide pelo logaritmo. RELATIONSHIPQUERY e DIRECTION limitam o tipo e o sentido das arestas, inclusive na contagem de conexões.
Quando usar: Use em redes grandes e esparsas, cheias de pontos de passagem (sociedades com dezenas de sócios, endereços de escritório virtual), quando só o elo realmente exclusivo deve pesar. Para uma penalização mais branda, ADAMICADAR; para a contagem pura, COMMONNEIGHBORS.
Em linguagem de negócio:
- Qual a probabilidade de os sócios A e B terem vínculo, desconsiderando sociedades com muitos participantes?
- Essas duas empresas dividem algum ponto de contato exclusivo, que não seja um escritório que atende todo mundo?
- Há sinal de relação próxima entre essas duas pessoas pelos elos que só elas compartilham?
- Quanto do que A e B têm em comum é realmente particular dos dois?
- Esses dois investigados se ligam por canais restritos ou só por intermediários de massa?
Como usar: Monte o par pelo MATCH do FROM GRAPH, devolvendo os elementIds, e passe as duas colunas no BETWEEN; não há PROJECT GRAPH e só a primeira linha é usada. RELATIONSHIPQUERY (por exemplo "TEMSOCIEDADE_PESSOA", para contar só as entradas em sociedade) e DIRECTION são opcionais. A saída é uma linha com score, que vale 0 sem vizinho em comum e soma 1/conexões de cada vizinho compartilhado.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (a:Pessoa), (b:Pessoa) WHERE a.pessoaUnico STARTS WITH '***222333**' AND a.pessoaNome STARTS WITH 'BELTRANA COSTA' AND b.pessoaUnico STARTS WITH '***444555**' AND b.pessoaNome STARTS WITH 'CICLANO ALVES' RETURN elementId(a) AS socio1, elementId(b) AS socio2"
|> LINK_PREDICTION RESOURCE_ALLOCATION BETWEEN socio1 AND socio2 RELATIONSHIP_QUERY="TEM_SOCIEDADE_PESSOA" ;Cuidados: Mantenha DIRECTION em BOTH (padrão): em OUTGOING ou INCOMING, um vizinho sem conexão no sentido escolhido gera divisão por zero e nota infinita. Só vizinhos diretos contam: no banco cnpj, duas empresas quase nunca têm vizinho direto em comum, e entre duas pessoas o vizinho em comum é a Sociedade em que entraram juntas ou o mesmo representante legal.
Parâmetros:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; padrãoBOTH)
Colunas devolvidas: score
LINKPREDICTIONTOTAL_NEIGHBORS
Conta quantos vizinhos diretos distintos as duas entidades do banco somam juntas: o tamanho do círculo combinado.
O que faz: Lê do banco, sem projeção, os vizinhos diretos de cada nó e devolve o tamanho da união dos dois conjuntos, com cada vizinho contado uma vez. RELATIONSHIPQUERY e DIRECTION limitam o tipo e o sentido das arestas. Sozinho indica o alcance somado do par; usado para dividir o COMMONNEIGHBORS do mesmo par, dá a fração do círculo que os dois compartilham.
Quando usar: Use para normalizar as métricas de vizinhos em comum — COMMONNEIGHBORS dividido por TOTALNEIGHBORS dá a proporção compartilhada, que não favorece quem simplesmente tem muitos vínculos — ou para medir o alcance combinado de dois investigados. Sozinho, não indica proximidade.
Em linguagem de negócio:
- Quantos vínculos societários distintos os sócios A e B somam juntos?
- Qual o tamanho do círculo combinado dessas duas pessoas?
- Que parcela das relações de A e B é compartilhada entre os dois?
- Se juntarmos as redes dessas duas empresas, quantos contatos diferentes aparecem?
- Qual o alcance somado desses dois investigados no cadastro?
Como usar: Monte o par pelo MATCH do FROM GRAPH, devolvendo os elementIds, e passe as duas colunas no BETWEEN; não há PROJECT GRAPH e só a primeira linha é usada. RELATIONSHIPQUERY e DIRECTION são opcionais e devem ser os mesmos do COMMONNEIGHBORS quando as duas notas forem combinadas. A saída é uma linha com score, o número de vizinhos distintos dos dois somados.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (a:Pessoa), (b:Pessoa) WHERE a.pessoaUnico STARTS WITH '***111222**' AND a.pessoaNome STARTS WITH 'FULANO DE TAL' AND b.pessoaUnico STARTS WITH '***444555**' AND b.pessoaNome STARTS WITH 'CICLANO ALVES' RETURN elementId(a) AS socio1, elementId(b) AS socio2"
|> LINK_PREDICTION TOTAL_NEIGHBORS BETWEEN socio1 AND socio2 RELATIONSHIP_QUERY="TEM_SOCIEDADE_PESSOA" ;Cuidados: A conta não desconta o que é comum e, sozinha, favorece quem tem muitos vínculos; para a proporção compartilhada, divida o COMMON_NEIGHBORS do mesmo par por ela. Só vizinhos diretos contam: no banco cnpj, os vizinhos de uma pessoa são as Sociedades de que participa e os vínculos de representação legal.
Parâmetros:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; padrãoBOTH)
Colunas devolvidas: score
LINKPREDICTIONSAME_COMMUNITY
Diz se duas entidades do banco têm o mesmo valor numa propriedade de agrupamento: 1 se sim, 0 se não.
O que faz: Lê direto do banco, sem projeção, a propriedade indicada em COMMUNITY_PROPERTY (padrão community) nos dois nós e devolve 1 quando os valores são iguais e 0 quando diferem ou quando algum dos dois não tem a propriedade. Não calcula grupos: só compara um rótulo de grupo que já está gravado — uma comunidade calculada antes, um CEP, uma natureza jurídica. Serve de ingrediente para combinar com as métricas de vizinhos em comum.
Quando usar: Use quando o banco já guarda uma propriedade que define grupos (comunidade, segmento, CEP, UF) e pertencer ao mesmo grupo é indício de ligação; combine com COMMONNEIGHBORS ou ADAMICADAR para um sinal mais forte. Para descobrir os grupos, use LOUVAIN, LEIDEN ou WCC numa projeção.
Em linguagem de negócio:
- As sedes dessas duas empresas ficam no mesmo CEP?
- Essas duas empresas pertencem ao mesmo segmento cadastrado?
- Os dois sócios estão no mesmo grupo já identificado pela equipe?
- As duas empresas têm a mesma natureza jurídica?
- Esse par cai na mesma região ou no mesmo grupo econômico registrado?
Como usar: Monte o par pelo MATCH do FROM GRAPH, devolvendo os elementIds, e passe as duas colunas no BETWEEN (só a primeira linha vale; não há PROJECT GRAPH). COMMUNITY_PROPERTY diz qual propriedade comparar — o banco cnpj não tem community, então informe uma que exista, como "enderecoCEP" no Endereco ou "empresaNjcod" na Empresa. A saída é score: 1,0 se iguais e 0,0 se diferentes ou ausentes.
Exemplo:
EVALUATE FROM GRAPH "cnpj" MATCH "MATCH (:Empresa {empresaCnpj: '12345678'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento {estabelecimentoCid: '1'})-[:TEM_ENDERECO]->(a:Endereco), (:Empresa {empresaCnpj: '87654321'})-[:TEM_ESTABELECIMENTO]->(:Estabelecimento {estabelecimentoCid: '1'})-[:TEM_ENDERECO]->(b:Endereco) RETURN elementId(a) AS sede1, elementId(b) AS sede2"
|> LINK_PREDICTION SAME_COMMUNITY BETWEEN sede1 AND sede2 COMMUNITY_PROPERTY="enderecoCEP" ;Cuidados: O DATTAX é somente leitura: a comunidade calculada por LOUVAIN ou WCC numa projeção não é gravada no banco, e esta função não a enxerga. Sem COMMUNITY_PROPERTY, a função procura community, que o banco cnpj não tem, e devolve sempre 0.
Parâmetros:
COMMUNITY_PROPERTY→communityProperty(String; padrãocommunity)
Colunas devolvidas: score
Projeção
PROJECT_GRAPH
Monta em memória, a partir de duas consultas Cypher, o recorte da rede sobre o qual os algoritmos de grafo rodam.
O que faz: Executa a consulta NODES, que devolve um nó por linha, e a consulta RELS, que devolve uma ligação por linha, e com elas cria uma projeção em memória no servidor Neo4j — é sobre ela, e não sobre o banco, que todo algoritmo de grafo trabalha. Colunas numéricas além das obrigatórias viram propriedades: no NODES, de nó (coordenadas, capital); no RELS, de ligação (peso, custo). Por baixo, o DATTAX chama gds.graph.project.cypher, marcado como deprecado na GDS 2.27, mas ainda o caminho usado hoje.
Quando usar: Sempre, antes de qualquer algoritmo de grafo: todo algoritmo exige uma projeção feita na mesma execução, depois de um FROM GRAPH que escolhe o banco (sem MATCH, ele não lê nó nenhum). Projete só o recorte que a pergunta pede — um município, um grupo, um tipo de vínculo — e, se o script compara recortes, crie projeções com nomes diferentes e escolha com ON ou IN.
Em linguagem de negócio:
- Monte a rede de empresas de Maceió ligadas por sócios em comum para eu analisar.
- Quero estudar só as empresas com capital acima de R$ 10 milhões e os sócios que elas compartilham.
- Separe as participações entre empresas, só as sócias pessoa jurídica, num recorte próprio.
- Leve em conta quantos sócios cada par de empresas compartilha ao analisar a rede.
- Analise apenas as empresas de um setor e as pessoas ligadas a elas.
Como usar: NODES (a chave nodeQuery da GDS) precisa devolver a coluna id com o id(n) numérico de cada nó — elementId não serve —, e RELS (relationshipQuery) as colunas source e target com os ids das pontas; colunas numéricas extras viram propriedades, como coalesce(e.empresaCsocial, 0.0) AS capital ou count(DISTINCT p) AS socios, e são esses nomes que se citam em opções como RELATIONSHIPWEIGHTPROPERTY. O nome da projeção vale só dentro da execução: no catálogo da GDS ele ganha o sufixo da execução, então execuções simultâneas não se atrapalham, repetir o nome na mesma execução substitui a anterior, e tudo é descartado ao fim; algoritmo sem ON (ou IN) usa a última projeção. O passo repassa adiante as linhas que chegaram a ele; o que a GDS informa (graphName, nodeQuery, relationshipQuery, nodeCount, relationshipCount, projectMillis) serve ao motor, que avisa PROJECAO_VAZIA quando a projeção nasce sem nós ou sem ligações.
Exemplo:
EVALUATE FROM GRAPH "cnpj"
|> PROJECT GRAPH socios_maceio NODES "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) RETURN DISTINCT id(e) AS id, coalesce(e.empresaCsocial, 0.0) AS capital" RELS "MATCH (m:Municipio {municipioNome: 'MACEIO', estadoSigla: 'AL'}) MATCH (e1:Empresa)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) MATCH (e1)<-[:ASSOCIADA_A_EMPRESA]-(:Sociedade)<-[:TEM_SOCIEDADE_PESSOA]-(p:Pessoa)-[:TEM_SOCIEDADE_PESSOA]->(:Sociedade)-[:ASSOCIADA_A_EMPRESA]->(e2:Empresa) WHERE e1 <> e2 AND EXISTS { (e2)-[:TEM_ESTABELECIMENTO]->(:Estabelecimento)-[:TEM_ENDERECO]->(:Endereco {enderecoMunicipio: m.municipioCodigo}) } RETURN id(e1) AS source, id(e2) AS target, count(DISTINCT p) AS socios"
|> PAGERANK ON socios_maceio RELATIONSHIP_WEIGHT_PROPERTY="socios"
|> ORDER BY score DESC
|> LIMIT 20 ;Cuidados: Todo source e target do RELS precisa estar entre os ids do NODES, senão a projeção falha: filtre as duas consultas pelo mesmo critério, e nunca corte só o NODES com LIMIT. Linha repetida no RELS vira ligação paralela e peso nulo vira NaN — use DISTINCT ou agregue, e coalesce no peso. As ligações saem direcionadas e os algoritmos de caminho seguem esse sentido: para vínculo sem direção, devolva os dois sentidos.
Parâmetros:
NODE_QUERY→nodeQuery(String; preenchido pela sintaxe)RELATIONSHIP_QUERY→relationshipQuery(String; preenchido pela sintaxe)
Colunas devolvidas:
<!-- gerado:gds-catalogo:fim -->