DATTAX — Graph Algorithm Catalog (Neo4j GDS)
Finding who is central in a network, which groups exist or what the shortest path between two entities is used to require writing Cypher procedures and knowing the Graph Data Science library inside out. In DATTAX, each algorithm is a pipeline operator: you project the graph, chain the algorithm with |> and get the result back as a table — ready to filter, join and materialize like any other data.
This page is the documented catalog the engine itself uses: the list of algorithms, each one's options (with type, default and whether they are required) and the columns it returns come from the GDS version installed in the platform — the one shipped in the Neo4j image itself, at the server's version (currently 2026.09.0, Community edition) —, and what each algorithm does, when to use it and the example are the platform's own text. The same catalog feeds the chat assistant when it picks an algorithm.
Every algorithm runs in read mode. The WRITE_PROPERTY and MUTATE_PROPERTY options are forbidden — DATTAX never writes to the graph during pipelines, so your original database stays intact.
Results are cached for 30 minutes, keyed by the projection definition: repeating the same algorithm on the same projection answers in seconds.
Generic syntax
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"
|> <ALGORITHM> [ON <graph>] [OPTION=value, OPTION=value] ;When ON <graph> is omitted, the algorithm uses the last projection registered in the execution; the projection is dropped at the end. A node property used by the algorithm comes out as a column of NODES (RETURN id(n) AS id, n.capitalSocial AS capital); a relationship weight, as a column of RELS.
There is also a generic form for any algorithm in the catalog:
|> GDS <ALGORITHM> [ON <graph>] [OPTION=value, ...]Options: each algorithm accepts only its own
- The DATTAX option is the GDS key in upper case with underscores:
dampingFactorbecomesDAMPING_FACTOR,seedSetSizebecomesSEED_SET_SIZE. The historical namesITER(maxIterations),DAMPING(dampingFactor) andRELATIONSHIP_WEIGHT(relationshipWeightProperty) still work where the algorithm has the key. - Options are comma-separated. Text goes in double quotes; a list as comma-separated text (
TARGET_NODES="12,57,98"); a map as"key:value,key:value". - An option the algorithm does not have and a missing required option are refused before reaching the database, with the list of accepted options and the algorithm's form — each section below says which ones are required.
BFS,DFSandALL_SHORTEST_PATHStake the source node throughFROM(the node's elementId); in the generic forms,SOURCE_NODEtakes the node's internal id, the sameid(n)as the projection.
Practical example — which companies are the most influential in the partner network?
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 ;The result arrives as a table with the 20 most influential companies — ready to become a dashboard or to be materialized.
Installed Community edition
The installed GDS is the Community edition, and it has all the algorithms on this page — including Leiden, SLLPA, HITS, HDBSCAN, Bellman-Ford, bridges and articulation points. What an Enterprise license would change are execution limits: in Community, each algorithm uses at most 4 threads (a higher CONCURRENCY is refused) and the trained-model catalog (GraphSAGE) keeps up to 3 models, in memory only — they are gone when the server restarts.
<!-- gerado:gds-catalogo:inicio -->
Centrality
PAGERANK
Gives each node a relevance score that grows when it receives links from other relevant nodes.
What it does: Every node starts with a fixed share of relevance equal to 1 - DAMPINGFACTOR and, on each round, passes what it has just gained on to the neighbours its relationships point to, split among them (in proportion to each relationship's weight when a weight is given). The score therefore builds up on nodes pointed to by many, or by a few important ones. Rounds stop when no score changes by more than TOLERANCE or when MAXITERATIONS is reached. With SOURCE_NODES, only the starting nodes receive the fixed share, and the score measures how closely each node is tied to them (the personalised version).
When to use: Use it when the relationship has a direction and importance should flow along it, as in who controls or invests in whom. Prefer it to simply counting links (DEGREE) when it matters where the links come from, and to EIGENVECTOR when the network is directed or has nodes without outgoing links, which the damping absorbs; with SOURCE_NODES it tells who is most tied to a starting point.
In business language:
- Which holding companies sit at the top of the corporate control chains?
- Which business groups concentrate the most control, also weighing how important the companies they run are?
- Starting from a company under investigation, which other companies are most tightly bound to it through the web of shareholdings?
- Which ultimate controllers should an audit prioritise because they run companies that in turn run others?
- Which companies stand out because they receive ties from companies that also stand out?
How to use: Project the relationship in RELS in the direction relevance should flow (in the example, from the investee to the shareholder company, so the score builds up on whoever controls); if the relationship goes both ways, return both directions. The most used options are DAMPINGFACTOR (default 0.85; DAMPING also works), MAXITERATIONS (default 20; ITER also works), TOLERANCE, RELATIONSHIPWEIGHTPROPERTY with the name of a numeric column from RELS, SOURCE_NODES with the internal id of a starting node and SCALER (for example "MinMax") to normalise the scores; none is required. The output has nodeId, the internal id returned by NODES, and score: sort by score descending and read the first rows as the most relevant.
Example:
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 ;Caveats: The score is neither a percentage nor does it sum to 1: a node that receives no links keeps 1 - DAMPINGFACTOR (0.15 by default), and with SOURCENODES any node not reachable from the starting points gets zero. With a weight, use positive values, because each relationship's share is proportional to it. Cost grows with the size of the projection and with MAX_ITERATIONS; on large bases, cut NODES and RELS with the same filter (municipality, CNAE activity code), because the projection rejects a relationship whose node was not returned by NODES.
Parameters:
DAMPING_FACTOR→dampingFactor(Float; default0.85)MAX_ITERATIONS→maxIterations(Integer; default20)TOLERANCE→tolerance(Float; default0.0000001)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SOURCE_NODES→sourceNodes(Node/Number or List or List of pairs as lists; default[])SCALER→scaler(String or Map)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; accepts relationship weights.
ARTICLE_RANK
A PageRank variant that dampens the weight of links coming from nodes that point to few neighbours, so exclusive ties do not inflate the score.
What it does: It works like PageRank: every node starts at 1 - DAMPING_FACTOR and, on each round, passes what it gained on to the neighbours it points to. The difference is in the split: instead of dividing only by the number of outgoing links, it divides by that number plus the average degree of the network, which sharply reduces what a node with few outgoing links hands to each neighbour and barely changes it for nodes with many. As a result, receiving someone's only link is worth less, and being pointed to by many counts more.
When to use: Use it for citation-like or investment relationships in which whoever points to a single target (a holding with one stake, a partner in a single company) should not hand it all of its relevance. If such an exclusive tie should count in full, prefer PAGERANK.
In business language:
- Which companies receive stakes from several different corporate groups, without a single-stake holding skewing the list?
- Which companies are the target of many investments from other relevant companies, giving less weight to investors that only invest in them?
- Which companies are the most sought-after holdings for business groups?
- Which companies gain relevance from the support of many different sources rather than from one exclusive tie?
- Where are the companies that attract capital from groups that also invest in other important companies?
How to use: Project the relationship in RELS from whoever points to whoever receives (in the example, from the shareholder company to the investee). The options are the same as PageRank's: DAMPINGFACTOR (default 0.85; DAMPING also works), MAXITERATIONS (default 20; ITER also works), TOLERANCE, RELATIONSHIPWEIGHTPROPERTY, SOURCE_NODES and SCALER; none is required. The output has nodeId (the internal id returned by NODES) and score: sort by score descending and use SCALER="MinMax" when you want scores between 0 and 1.
Example:
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 ;Caveats: Without SCALER, scores usually come out lower than PageRank's on the same projection, because the denominator is larger; between the two algorithms, compare positions, not values. A node that receives no links keeps 1 - DAMPING_FACTOR. The average degree used in the split is that of the whole projection, so different cuts (another municipality, another CNAE code) change the strength of the correction.
Parameters:
DAMPING_FACTOR→dampingFactor(Float; default0.85)MAX_ITERATIONS→maxIterations(Integer; default20)TOLERANCE→tolerance(Float; default0.0000001)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SOURCE_NODES→sourceNodes(Node/Number or List or List of pairs as lists; default[])SCALER→scaler(String or Map)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; accepts relationship weights.
EIGENVECTOR
Measures each node's influence by its neighbours' influence: being linked to well-connected nodes is worth more than having many links to isolated ones.
What it does: It starts with the same score for everyone (or only for SOURCENODES) and, on each round, each node adds to its own score the scores it receives through its incoming relationships; with RELATIONSHIPWEIGHTPROPERTY, each neighbour splits its score in proportion to the weight of each relationship. After every round the scores are normalised so that the sum of their squares is 1, and the process ends when the change falls below TOLERANCE or at MAXITERATIONS. There is no damping factor: the final score reflects only the link structure.
When to use: Use it on symmetric relationships, such as shared partners, to find who sits in the densest core of the network. Prefer it to PAGERANK when the relationship has no natural direction and you do not want the fixed damping share, and to DEGREE when the quality of links matters more than their number.
In business language:
- Which companies are surrounded by other companies that are also highly connected through shared partners?
- Who sits at the tightest core of the city's corporate groups?
- Which companies have influence by association, because they share partners with influential companies?
- Where is the heart of the shared-partner web in a given sector?
- Which companies keep company with the best-connected companies?
How to use: Project the relationship in both directions when it is symmetric (in the example, companies in São Paulo that share partners, with the number of shared partners as the weight). The options are MAXITERATIONS (default 20; ITER also works), TOLERANCE, RELATIONSHIPWEIGHTPROPERTY with the name of the weight column in RELS, SOURCENODES and SCALER; none is required. The output has nodeId and score, which is already normalised: sort by it and compare positions.
Example:
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 ;Caveats: In a network split into several groups, the score tends to concentrate in the densest group and companies in the others stay close to zero, which does not mean they are irrelevant within their own group; compare nodes of the same group or pass SOURCENODES, which restricts the calculation to the part of the network reachable from them. Large networks may need more than the default 20 iterations to settle: raise MAXITERATIONS. SCALER="L2Norm" is ignored, because the result already comes on that scale.
Parameters:
MAX_ITERATIONS→maxIterations(Integer; default20)TOLERANCE→tolerance(Float; default0.0000001)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SOURCE_NODES→sourceNodes(List or Node or Number; default[])SCALER→scaler(String or Map)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; accepts relationship weights.
BETWEENNESS
Points out the nodes that lie on the shortest paths between other nodes, that is, the intermediaries the network has to pass through.
What it does: For each starting node, the algorithm searches the network for the shortest paths to all the others and, walking back from the end, credits every intermediate node with the fraction of those paths that goes through it. The final score is the sum of those credits. With RELATIONSHIPWEIGHTPROPERTY the shortest path becomes the one with the smallest sum of weights; with SAMPLING_SIZE only a sample of starting nodes is used (drawn with a preference for the best-connected ones), trading precision for time.
When to use: Use it to find bridges and go-betweens: whoever links groups that would otherwise be far apart. It differs from DEGREE, which counts links, and from CLOSENESS, which measures closeness to everyone: a node with few links can score high if it is the mandatory passage between two groups.
In business language:
- Which people bridge groups of construction companies that would otherwise have no partner in common?
- Who are the intermediaries through which the ties between separate business groups pass?
- Which partners control the flow of relationships between companies that do not know each other?
- Which companies act as the passageway between different corporate cores?
- If I wanted to keep two business groups apart, which go-betweens should I watch?
How to use: Project people and companies (or companies only) with the relationship in both directions, so a path can go either way (in the example, partners and construction companies in São Paulo). The options are SAMPLINGSIZE (how many starting nodes to use; without it, all of them), SAMPLINGSEED (to repeat the same sample), RELATIONSHIPWEIGHTPROPERTY and CONCURRENCY; none is required. The output has nodeId and score, the sum of the fractions of shortest paths that go through the node: sort by score descending.
Example:
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 ;Caveats: It is one of the most expensive algorithms: time grows with the number of nodes times the number of relationships, so cut the projection by municipality or CNAE code and use SAMPLING_SIZE on large networks (the installed edition accepts CONCURRENCY of at most 4). The weight is treated as a distance, and the larger it is, the farther apart: to use tie strength as the weight, return its inverse in RELS. With the relationship projected in both directions each pair is counted twice, and with sampling the values depend on the sample size, so compare positions rather than absolute numbers.
Parameters:
SAMPLING_SIZE→samplingSize(Integer; defaultnode count)SAMPLING_SEED→samplingSeed(Integer)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; accepts relationship weights.
CLOSENESS
Measures how close each node is to the others, as the inverse of the average distance to the nodes in its group.
What it does: The algorithm runs breadth-first searches from all nodes at once, counting in how many steps each node is reached and by how many others. The default score is the number of nodes that reach it divided by the sum of those distances, that is, the inverse of the average distance within its own group. With USEWASSERMANFAUST=true, the score is multiplied by the share of the network that group represents, which penalises nodes in small groups.
When to use: Use it to find who is at the centre of a well-connected network, from where everyone can be reached through fewer intermediaries. In a network broken into many small groups, turn on USEWASSERMANFAUST or prefer HARMONIC, which handles unreachable nodes better.
In business language:
- Which companies can reach the rest of the partner network through the fewest intermediaries?
- From where would a piece of information or a practice spread fastest through the whole corporate group?
- Who is at the centre of gravity of the relationships among the city's companies?
- Which companies are only a few steps away from all the others in the same business group?
- Which company is the best vantage point for keeping an eye on an entire group?
How to use: Project the relationship in both directions when it is symmetric (in the example, companies in Recife linked by shared partners). The options are USEWASSERMANFAUST (default false; use true on fragmented networks) and CONCURRENCY; none is required, and the algorithm ignores weights, counting only steps. The output has nodeId and score: sort by score descending.
Example:
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 ;Caveats: Without USEWASSERMANFAUST, a pair of companies cut off from everyone else gets the maximum score (1), because the average distance inside it is 1; on fragmented networks this fills the top of the list with small groups. Cost grows with nodes times relationships, so cut the projection by municipality or CNAE code (CONCURRENCY at most 4 on the installed edition). In a one-way projection the distance is counted along the direction of the relationships, from whoever reaches the node.
Parameters:
USE_WASSERMAN_FAUST→useWassermanFaust(Boolean; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; no relationship weights.
HARMONIC
A closeness variant that adds up the inverse of the distances, so unreachable nodes count as zero and fragmented networks do not skew the result.
What it does: As in CLOSENESS, breadth-first searches run from all nodes at once. For each node, the inverses of the distances are added up (a direct neighbour is worth 1, one two steps away is worth 1/2, and so on), and the total is divided by the number of nodes in the projection minus one. Nodes that cannot reach each other simply add nothing, without breaking the calculation.
When to use: Use it instead of CLOSENESS when the network has several disconnected groups, which is common in shareholding data: the score stays comparable across groups and favours whoever is close to many, not whoever is alone in a small group.
In business language:
- Even with many isolated groups, which companies are close to the largest number of others through shared partners?
- Who has the largest nearby reach in the local market, counting only those who can actually be reached?
- Which companies could influence the most companies within a few steps of relationship?
- In a scattered partner network full of islands, who is closest to everyone?
- Which companies are one or two steps away from many others in the same sector?
How to use: Project the relationship in both directions when it is symmetric (in the example, companies in Campinas linked by shared partners, and only those with at least one link). The only option is CONCURRENCY (default 4, which is also the maximum on the installed edition); the algorithm ignores weights. The output has nodeId and score, between 0 and 1: the closer to 1, the closer the node is to the whole projection.
Example:
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 ;Caveats: Because the sum is divided by the total number of nodes in the projection, including companies with no links lowers everyone's score (without changing the order); project only what matters. Cost is the same as CLOSENESS, proportional to nodes times relationships, so cut the projection by municipality or CNAE code on large bases.
Parameters:
CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; no relationship weights.
DEGREE
Counts each node's links (or adds up their weights) in the chosen direction.
What it does: For each node, the algorithm counts the relationships leaving it (ORIENTATION="NATURAL"), those arriving ("REVERSE") or both ("UNDIRECTED"). With RELATIONSHIPWEIGHTPROPERTY, instead of counting, it adds up the positive weights of those relationships. It is a single pass over the projection, with no iterations.
When to use: Use it as a first reading of the network and for questions about quantity: who has the most ties. When the quality of the links matters, not just their number, move on to PAGERANK or EIGENVECTOR.
In business language:
- Which people are partners in the largest number of companies?
- Which companies have the largest partner roster?
- Are there people who show up in too many companies to be genuine partners, possibly front men?
- Which companies hold stakes in the most other companies?
- Who accumulates the most corporate ties in the city?
How to use: Project the relationship with the direction that gives the count its meaning: in the example, from person to company in Maceió, so ORIENTATION="NATURAL" counts companies per person and ORIENTATION="REVERSE" would count partners per company. The options are ORIENTATION (default NATURAL), RELATIONSHIPWEIGHTPROPERTY and CONCURRENCY; none is required. The output has nodeId and score, the node's total number of links (or total weight): sort by score descending.
Example:
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 ;Caveats: Repeated relationships between the same pair count several times: use DISTINCT in RELS when the question is how many different companies. With ORIENTATION="UNDIRECTED" on a relationship already projected in both directions, each tie counts twice. The calculation itself is cheap; the real cost is in the NODES and RELS queries, so cut them by municipality or CNAE code on large bases.
Parameters:
ORIENTATION→orientation(String; defaultNATURAL)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, score
Graph: directed or undirected; accepts relationship weights.
CELF
Picks the set of nodes that, as starting points, would spread an effect through the network to the largest number of other nodes.
What it does: The algorithm simulates a cascade many times: each node that is hit has a fixed chance (PROPAGATIONPROBABILITY) of hitting each neighbour it points to. First it estimates, for every node, how many would be hit on average starting from it and picks the best; then, until SEEDSETSIZE picks are made, it adds the node that most increases the reach of the set already chosen, re-evaluating only the candidates that can still win (the lazy evaluation that gives the method its name). Each estimate is the average of MONTECARLO_SIMULATIONS simulations.
When to use: Use it when the question is where to start: choosing a few targets so that an action (an audit, a notice, a campaign) reaches as many linked companies as possible. Unlike relevance scores, it discounts overlap: a node that would only reach companies already covered by the chosen ones adds little and is left out.
In business language:
- If only five companies can be audited, which ones should be picked so the effect reaches the most linked companies?
- Where should a compliance campaign start so that it spreads furthest through the partner network?
- Which companies, if fined first, would set off the biggest chain reaction in the group?
- Which few targets cover most of the network without overlapping?
- Where would a problem start to spread furthest through corporate ties?
How to use: Project the relationship in the direction the effect travels, or in both directions when it runs both ways, as between companies with shared partners (in the example, in Maceió); project only nodes that have links, because an isolated node never spreads anything. SEEDSETSIZE is required and says how many nodes to pick; the other options are PROPAGATIONPROBABILITY (default 0.1), MONTECARLOSIMULATIONS (default 100), RANDOMSEED and CONCURRENCY. The output has only the chosen nodes, with nodeId and spread, the reach each one added when it was picked: sorting by spread descending approximates the order in which they were picked.
Example:
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 ;Caveats: It is expensive: the first stage simulates the cascade from every node in the projection, MONTECARLOSIMULATIONS times, so cut the network well and start with few simulations (CONCURRENCY at most 4 on the installed edition). The spread of the first pick is its own reach, counting itself; the following ones show only what they add, and the sum estimates the reach of the whole set. Running again with another RANDOM_SEED helps check whether the choice is stable.
Parameters:
SEED_SET_SIZE→seedSetSize(Integer; required)MONTE_CARLO_SIMULATIONS→monteCarloSimulations(Integer; default100)PROPAGATION_PROBABILITY→propagationProbability(Float; default0.1)RANDOM_SEED→randomSeed(Integer)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, spread
Graph: directed or undirected; no relationship weights.
HITS
Gives each node two scores: authority (auth), for being pointed to by good distributors of links, and hub, for pointing to good authorities.
What it does: Everyone starts with both scores equal. On each round, a node's authority becomes the sum of the hub scores of those pointing to it and then each node's hub becomes the sum of the authorities it points to; after each calculation the scores are normalised. Rounds repeat HITS_ITERATIONS times, and this mutual reinforcement separates those who hand out relevant links from those who receive them.
When to use: Use it on directed relationships with two roles, one that hands out and one that receives, such as partner and company or holding and investee. When a single relevance score is enough, prefer PAGERANK; HITS requires a directed projection.
In business language:
- Which partners spread their stakes across companies that gather other heavyweight partners?
- Which companies bring together the best-connected partners in the local market?
- Who are the dealmakers who invest precisely in the most sought-after companies?
- Which companies are a reference because their partners also sit in the most relevant companies?
- Who hands out ties to the right targets, and who is the favourite target of those handing out ties?
How to use: Project the relationship in one direction only, from whoever points to whoever receives (in the example, from person to company in Recife). The options are HITSITERATIONS (default 20), AUTHPROPERTY and HUB_PROPERTY (key names in the result, auth and hub by default), PARTITIONING and CONCURRENCY; none is required. The output has nodeId and values, a map with the two scores: in the example people get a hub score and companies an authority score, because a person only points and a company only receives.
Example:
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 ;Caveats: Project a single direction: returning both directions mixes the roles, and everyone becomes at once someone who points and someone who receives. To compute both scores the algorithm builds an inverse index of the relationships, which takes extra memory proportional to the number of relationships; cut the projection by municipality or CNAE code. A node that receives no links has zero authority, and one that points to nobody has a zero hub score.
Parameters:
HITS_ITERATIONS→hitsIterations(Integer; default20)AUTH_PROPERTY→authProperty(String; default"auth")HUB_PROPERTY→hubProperty(String; default"hub")PARTITIONING→partitioning(String; default"AUTO")CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, values
Graph: directed or undirected; no relationship weights.
Community
LOUVAIN
Splits the network into groups of companies that are far more tightly linked to each other than to the rest, without being told how many groups exist.
What it does: It starts with every company in its own group and moves each one into the neighbouring group that most increases modularity — a measure of how many ties stay inside groups beyond what chance alone would produce. When no move improves the result any further, each group is collapsed into a single point and the same round runs again on that summarised network. It keeps climbing levels until MAX_LEVELS or until the gain drops below TOLERANCE, and the last level gives each company its final group.
When to use: It is the starting point for uncovering de facto business groups when you do not know how many there are. Prefer LEIDEN when groups must hold together internally (Louvain sometimes merges parts that touch through a single tie), LABEL_PROPAGATION when the data set is huge and speed matters more than quality, and SLLPA when a company may belong to more than one group.
In business language:
- What are the main groups of companies formed by shared partners?
- Are there blocks of companies operating as a single business group without declaring it?
- Which family conglomerates control several companies in the city?
- Into how many ownership cores do the companies in the municipality split, and how large is each one?
- Which companies should be audited together because they belong to the same de facto group?
How to use: Project the companies in NODES and the tie in RELS — in the example, an individual partner shared by companies in Maceió/AL, a pattern that already returns each pair in both directions — with the number of shared partners as the weight column named in RELATIONSHIPWEIGHTPROPERTY. MAXLEVELS and MAXITERATIONS cap levels and rounds, TOLERANCE sets the minimum gain to keep going, MINCOMMUNITYSIZE drops groups smaller than the value from the output (isolated companies included), SEEDPROPERTY starts from an earlier split and INCLUDEINTERMEDIATE_COMMUNITIES=true also returns the group at every level. Each row carries nodeId (the same id(e) returned by NODES), communityId (the label of the final group) and intermediateCommunityIds; group by communityId to see the size of each group. The city cut starts from the Municipio node (municipioNome and estadoSigla), because Endereco.enderecoMunicipio holds the Federal Revenue municipality code, and the same filter must appear in NODES and RELS, since the projection rejects a relationship whose end did not come from NODES.
Example:
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 ;Caveats: The projection takes up Neo4j memory: cut out a subgraph by municipality or CNAE instead of projecting the whole register, and use MINCOMMUNITYSIZE so thousands of isolated companies do not come back. communityId is only a label, unrelated to order or size, and it may change between runs because the order of parallel processing (up to 4 threads in the Community edition) changes the moves; INCLUDEINTERMEDIATECOMMUNITIES and CONSECUTIVE_IDS cannot be combined.
Parameters:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SEED_PROPERTY→seedProperty(String)MAX_LEVELS→maxLevels(Integer; default10)MAX_ITERATIONS→maxIterations(Integer; default10)TOLERANCE→tolerance(Float; default0.0001)INCLUDE_INTERMEDIATE_COMMUNITIES→includeIntermediateCommunities(Boolean; defaultfalse)CONSECUTIVE_IDS→consecutiveIds(Boolean; defaultfalse)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, intermediateCommunityIds, communityId
Graph: directed or undirected; accepts relationship weights.
LABEL_PROPAGATION
Builds groups by letting each company adopt the most common label among its neighbours until the split settles.
What it does: Every company starts with a label of its own (or the value of SEEDPROPERTY) and, in each round, switches to the label with the most votes among its neighbours, where each vote is worth the relationship weight and, when given, the neighbour's weight in NODEWEIGHTPROPERTY. Labels spread through the densest regions and stop changing; companies that end up with the same label form a group. The run ends when no label changes or when MAXITERATIONS is reached.
When to use: Use it when the data set is large and you want a fast split into groups without optimising a global measure; it is lighter than LOUVAIN and LEIDEN but less stable, and different runs may yield different splits. If a company may sit in two groups at once, use SLLPA.
In business language:
- Which companies are served by the same accounting firm?
- Which portfolios of companies share the same contact e-mail?
- Are there clumps of companies managed by the same person or the same office?
- Which sets of companies revolve around the same point of contact?
- How can thousands of companies be quickly sorted into relationship blocks?
How to use: Project the companies in NODES and, in RELS, the tie the label should spread along — in the example, companies in Maceió/AL that report the same contact e-mail, usually the accounting firm's, compared as text because each establishment has its own Email node. Tune MAXITERATIONS; use RELATIONSHIPWEIGHTPROPERTY and NODEWEIGHTPROPERTY to give some ties or companies more pull, SEEDPROPERTY to start from known labels, CONSECUTIVEIDS=true to number the groups from 0 and MINCOMMUNITY_SIZE to hide small groups. The output has nodeId and communityId; group by communityId to measure each portfolio.
Example:
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 ;Caveats: The projection is directed and each company only listens to the neighbours its relationships point to: return each pair in both directions (no filter such as id(a) < id(b)), otherwise some companies never change label. The result depends on the order of parallel updates and may vary between runs, and communityId is only a label. Cut the data by municipality or CNAE before projecting, because the e-mail of a large firm links hundreds of companies and multiplies the relationships.
Parameters:
MAX_ITERATIONS→maxIterations(Integer; default10)NODE_WEIGHT_PROPERTY→nodeWeightProperty(String)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SEED_PROPERTY→seedProperty(String)CONSECUTIVE_IDS→consecutiveIds(Boolean; defaultfalse)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, communityId
Graph: directed or undirected; accepts relationship weights.
WCC
Finds the islands of companies connected by some chain of ties, regardless of direction.
What it does: It walks the relationships and, for each one, merges the sets its two ends belong to, ignoring direction; at the end, each set is a component, identified by componentId. Two companies land in the same component whenever any chain of ties links them, even through many others. With RELATIONSHIPWEIGHTPROPERTY and THRESHOLD, only relationships weighing more than the threshold count.
When to use: Use it to learn who is connected to whom in any way, to measure the size of each block or to spot isolated companies — it is usually the first analysis, before the grouping algorithms. If direction matters (who controls whom, circular holdings), use SCC; to break a large block into cohesive groups, use LOUVAIN or LEIDEN.
In business language:
- Which companies are linked to each other, even indirectly, through shared partners?
- How many islands of connected companies exist in the city, and how big is each one?
- Which companies are isolated, with no ownership tie to any other?
- How far does the web of partners around a suspicious company reach?
- Does this set of suppliers form a single block or several separate groups?
How to use: Project the companies in NODES and the tie in RELS (in the example, a shared individual partner in Maceió/AL); since direction is ignored, each pair may come in one or both directions. MINCOMPONENTSIZE drops components smaller than the value from the output, CONSECUTIVEIDS=true numbers the components from 0, SEEDPROPERTY continues an earlier split and THRESHOLD, together with RELATIONSHIPWEIGHTPROPERTY, discards relationships whose weight is equal to or below the threshold (with weight = shared partners, THRESHOLD=1.0 requires two or more). The output has nodeId and componentId; group by componentId to count the companies on each island.
Example:
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 ;Caveats: THRESHOLD without RELATIONSHIPWEIGHTPROPERTY is rejected. Over a whole register, the largest component can gather millions of companies by chaining: cut the data by municipality or CNAE and check component sizes before treating them as a business group. componentId is only a label, with no order.
Parameters:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SEED_PROPERTY→seedProperty(String)THRESHOLD→threshold(Float)CONSECUTIVE_IDS→consecutiveIds(Boolean; defaultfalse)MIN_COMPONENT_SIZE→minComponentSize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, componentId
Graph: directed or undirected; accepts relationship weights.
SCC
Finds sets of companies in which each one reaches all the others following the direction of the ties, which exposes control cycles.
What it does: It respects the direction of every relationship and puts into the same component the companies that reach each other both ways: from A you get to B, and from B some path leads back to A. In a holdings network (who is a partner of whom), a component with two or more companies is a control cycle; companies outside any cycle stay alone in their own component.
When to use: Use it when direction carries meaning — shareholding, control, flow — and the question is about circularity. If it is enough to know whether a tie exists in any direction, use WCC, which is cheaper and forms larger blocks.
In business language:
- Are there cross-holdings in which a company ends up owning itself through other companies?
- Which companies form circular chains of corporate control?
- Which holding companies control each other?
- Where are there circular control structures, typical of asset shielding?
- In which groups do the shareholdings loop back to the company where they started?
How to use: Project in RELS the tie oriented from holder to held — in the example, the partner company (TEMSOCIEDADEEMPRESA) pointing to the company it holds a stake in — and, in NODES, the companies at both ends. Its own option is CONSECUTIVE_IDS=true, which numbers the components from 0. The output has nodeId and componentId; group by componentId and keep only components with more than one company to list the cycles.
Example:
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 ;Caveats: Direction decides the result: swapping source and target keeps the same cycles, but returning each pair in both directions turns every tie into a cycle, and the result becomes the same as WCC's. The network of holdings between companies is much smaller than the register, which is why the example projects the whole base; single-company components are the rule and do not signal a problem.
Parameters:
CONSECUTIVE_IDS→consecutiveIds(Boolean; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, componentId
Graph: directed or undirected; no relationship weights.
TRIANGLE_COUNT
Counts, for each company, how many closed trios it belongs to — cases where two of its partners are also linked to each other.
What it does: For every pair of linked companies it intersects their neighbour lists and counts the shared neighbours; each shared neighbour closes a trio, credited to all three companies. The total per company is returned in triangleCount. Companies with more neighbours than MAX_DEGREE are left out of the count, which cuts the cost of large hubs.
When to use: Use it to measure, as an absolute number, how intertwined each company's neighbourhood is. To compare companies with different numbers of ties, prefer LOCAL_CLUSTERING, which divides by the possible pairs; to find the densest core of the network, use KCORE.
In business language:
- Which companies sit at the centre of trios of companies linked to each other?
- Which companies take part in the most closed relationships, where the partners are also connected?
- Where are companies tied to one another through shared partners, in possible collusion?
- Which companies have the most intertwined ownership ties in the city?
How to use: Project the companies in NODES and the tie in RELS (in the example, a shared individual partner in Maceió/AL, each pair once). MAXDEGREE skips companies with an oversized neighbourhood, and LABELFILTER (up to three labels, comma-separated) only counts trios that involve companies with those labels — which requires the labels column in NODES. The output has nodeId and triangleCount; sort in descending order to find the most intertwined companies.
Example:
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 ;Caveats: The algorithm treats ties as having no direction (undirected): DATTAX hands GDS the projection without direction, so it is enough to return each pair once, like the id(e1) < id(e2) in the example, without repeating the reversed pair. Companies cut by MAXDEGREE come back with triangleCount = -1, not zero. Cost grows quickly with the number of neighbours of the most connected companies: cut the data by municipality or CNAE and use MAXDEGREE.
Parameters:
MAX_DEGREE→maxDegree(Integer; default2^63^ - 1)LABEL_FILTER→labelFilter(List of String; default[])CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, triangleCount
Graph: undirected only — the platform treats the relationships as undirected automatically; no relationship weights.
LOCAL_CLUSTERING
Measures, for each company, the share of pairs of partners that are also linked to each other — from 0 (nobody knows each other) to 1 (everybody does).
What it does: For each company it counts the closed trios it belongs to and divides by the number of possible pairs among its neighbours: 2 × trios ÷ (degree × (degree - 1)). A value close to 1 means a neighbourhood where everyone is tied to everyone; close to 0, a company that connects partners with no relation to each other. The trio count is computed internally or reused from TRIANGLECOUNTPROPERTY.
When to use: Use it to compare how close-knit the neighbourhoods of companies with different numbers of ties are, since the value is normalised; TRIANGLE_COUNT gives the absolute number of trios, and KCORE points to the dense core of the network as a whole. A low value in a company with many ties usually marks a bridge between groups.
In business language:
- Which companies have partners that also deal with each other, forming a tight inner circle?
- Which companies live in closed circles of partners where everybody knows everybody?
- Which companies connect partners that have no relation to one another?
- How close-knit is the ownership surroundings of each company?
- Which sets of companies are so closed that they suggest a cartel?
How to use: Project the companies in NODES and the tie in RELS (in the example, a shared individual partner in Maceió/AL, each pair once). TRIANGLECOUNTPROPERTY names a NODES column holding an already computed trio count, so it is not recounted. The output has nodeId and localClusteringCoefficient; filter high values to find tight circles and low values, among companies with many ties, to find those linking separate groups.
Example:
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 ;Caveats: The algorithm treats ties as having no direction (undirected): DATTAX hands GDS the projection without direction, so it is enough to return each pair once, like the id(e1) < id(e2) in the example, without repeating the reversed pair. A coefficient of 1.0 in a company with only two neighbours carries little weight: read the value together with the number of ties or trios. Companies with no trio get 0.
Parameters:
TRIANGLE_COUNT_PROPERTY→triangleCountProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, localClusteringCoefficient
Graph: undirected only — the platform treats the relationships as undirected automatically; no relationship weights.
KCORE
Gives each company the level of the densest core it still belongs to when companies with few ties are peeled off layer by layer.
What it does: It repeatedly removes companies with fewer than k ties, raising k step by step; each removal costs the neighbours ties, and they may drop out too. A company's coreValue is the largest k at which it still survives. A high coreValue therefore means sitting in the core surrounded by companies that are also well connected, not merely having many ties.
When to use: Use it to separate the dense core from the periphery and to prune weakly tied companies before other analyses. Unlike degree, which only counts ties, coreValue requires the neighbours to be connected as well; to measure each company's local cohesion, use LOCAL_CLUSTERING.
In business language:
- What is the hard core of the most interlinked companies in the city?
- Which companies stay connected even after removing the weakly tied ones?
- Which companies are at the heart of the ownership structure and which are on the fringe?
- Where is the densest centre of companies bound together by shared partners?
- Which companies should be investigated first because they sit at the heart of the scheme?
How to use: Project the companies in NODES and the tie in RELS (in the example, a shared individual partner in Maceió/AL, each pair once); the algorithm has no options of its own. The output has nodeId and coreValue; filter a threshold (for example, coreValue >= 3) to keep only the core and sort in descending order.
Example:
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 ;Caveats: The algorithm treats ties as having no direction (undirected): DATTAX hands GDS the projection without direction, so it is enough to return each pair once, like the id(e1) < id(e2) in the example, without repeating the reversed pair. Companies with no tie get coreValue 0, and in a network made of closed groups (everyone tied to everyone) coreValue reflects the group size minus one.
Parameters:
CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, coreValue
Graph: undirected only — the platform treats the relationships as undirected automatically; no relationship weights.
KMEANS
Splits the companies into K groups with similar numeric profiles, measuring the distance between feature vectors.
What it does: It treats each company as a point in the space of the vector named in NODEPROPERTY. It picks K initial centres (uniform sampling or kmeans++, in INITIALSAMPLER), assigns each company to the nearest centre by Euclidean distance, recomputes each centre as the mean of its group and repeats until MAXITERATIONS or until the share of companies switching groups falls below DELTATHRESHOLD. With NUMBEROFRESTARTS above 1 it runs several times and keeps the split with the smallest average distance.
When to use: Use it when you already know (or want to test) how many profiles you are looking for and every company must fall into some group. Prefer HDBSCAN when you do not know the number of groups, when groups have different densities or when atypical companies should be left out as noise.
In business language:
- How can the companies be split into similar profiles of capital, number of branches and partners?
- Which company segments emerge when we compare size and ownership structure?
- Into which groups of similar profile should I split the companies for a sample-based audit?
- Which companies are furthest from the typical profile of their group?
- How many companies are there in each profile band, and which band holds the most?
How to use: Return in NODES, besides the id, the numeric vector of each company — in the example, perfil = [log10 of share capital, log10 of the number of establishments, log10 of the number of individual partners] for companies in Maceió/AL — and name that column in NODEPROPERTY, which is required; RELS only satisfies the syntax, since the algorithm ignores relationships. K sets the number of groups (default 10), RANDOMSEED fixes the sampling, NUMBEROFRESTARTS repeats the run and COMPUTE_SILHOUETTE=true computes the silhouette. The output has nodeId, communityId (from 0 to K-1), distanceFromCentroid (distance to the group's centre) and silhouette (from -1 to 1; the higher, the better the company fits its own group).
Example:
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 ;Caveats: The vector's columns must be on comparable scales (hence the log10 in the example), otherwise the one with the widest range dominates the distance; the vector must have the same length for every company and cannot be missing (use coalesce in NODES). Without COMPUTESILHOUETTE=true the silhouette column comes back as -1, which does not mean a poor fit, and the silhouette is expensive on large data sets. Without RANDOMSEED, each run samples other centres and the group numbers change.
Parameters:
NODE_PROPERTY→nodeProperty(String; required)K→k(Integer; default10)MAX_ITERATIONS→maxIterations(Integer; default10)DELTA_THRESHOLD→deltaThreshold(Float; default0.05)NUMBER_OF_RESTARTS→numberOfRestarts(Integer; default1)RANDOM_SEED→randomSeed(Integer)INITIAL_SAMPLER→initialSampler(String; default"uniform")SEED_CENTROIDS→seedCentroids(List of List of Float; default[])COMPUTE_SILHOUETTE→computeSilhouette(Boolean; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, communityId, distanceFromCentroid, silhouette
MODULARITY
Groups companies by maximising modularity on a single level, without building Louvain's hierarchy of groups.
What it does: In each iteration, companies move to the neighbouring group that most increases modularity, and the process stops when the gain between iterations falls below TOLERANCE or at MAX_ITERATIONS. To move many companies at the same time without neighbours deciding on each other, it first colours the network and processes one colour at a time. Unlike Louvain, the groups it forms are not collapsed and reprocessed in levels.
When to use: Use it when you want groupings at the finest level, close to each company's immediate neighbourhood. For larger, steadier groups that merge smaller blocks, use LOUVAIN or LEIDEN; for an even faster split, LABEL_PROPAGATION.
In business language:
- Which small groups of companies revolve around the same people?
- Which local clumps of shared partners exist, without merging everything into large blocks?
- Where are the pockets of companies with shared partners, at the most detailed level?
- How can the city's companies be split into groups of immediate ownership affinity?
How to use: Project the companies in NODES and the tie in RELS (in the example, a shared individual partner in Maceió/AL, with the number of shared partners as weight). Use RELATIONSHIPWEIGHTPROPERTY for the weight, MAXITERATIONS and TOLERANCE for the stopping rule, SEEDPROPERTY to start from an existing split, CONSECUTIVEIDS=true to number the groups from 0 and MINCOMMUNITY_SIZE to hide small groups. The output has nodeId and communityId; group by communityId to see each group.
Example:
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 ;Caveats: Since the projection is directed, return each pair in both directions so the tie counts equally for both companies. Processing is parallel and groups may vary between runs, and communityId is only a label. SEEDPROPERTY and CONSECUTIVEIDS=true cannot be used together.
Parameters:
MAX_ITERATIONS→maxIterations(Integer; default10)TOLERANCE→tolerance(Float; default0.0001)SEED_PROPERTY→seedProperty(String)CONSECUTIVE_IDS→consecutiveIds(Boolean; defaultfalse)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, communityId
Graph: directed or undirected; accepts relationship weights.
LEIDEN
Finds groups of companies like Louvain does, but guarantees that each group is well connected inside.
What it does: It moves companies between neighbouring groups to gain modularity and then collapses each group into a point, like Louvain, but adds a refinement step before collapsing: each group is split into parts that must be genuinely connected inside, and only those parts are merged. This avoids groups stitched together by a single tie. GAMMA sets the resolution (higher values yield smaller and more numerous groups) and THETA controls how random the choices in the refinement are.
When to use: Prefer it to LOUVAIN when poorly connected groups get in the way of the analysis or when you want to control group size with GAMMA. For a fast, approximate split, use LABEL_PROPAGATION; for companies in more than one group, SLLPA.
In business language:
- Which de facto business groups appear when we require each group to be truly cohesive?
- Which tighter subgroups exist inside the large conglomerates?
- Which companies form compact ownership blocks, without merging groups that touch through a single partner?
- How can a large conglomerate be broken into parts tightly bound by shared partners?
- Which business families form compact cores of companies?
How to use: Project the companies in NODES and the tie in RELS (in the example, a shared individual partner in Maceió/AL, each pair once, with the shared partners as weight in RELATIONSHIPWEIGHTPROPERTY). GAMMA controls group size, THETA the randomness of the refinement, MAXLEVELS and TOLERANCE the depth and the stopping point, SEEDPROPERTY starts from an earlier split, MINCOMMUNITYSIZE hides small groups and INCLUDEINTERMEDIATECOMMUNITIES=true returns the group at every level. The output has nodeId, communityId and intermediateCommunityIds; group by communityId to see the size of each group.
Example:
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 ;Caveats: The algorithm treats ties as having no direction (undirected): DATTAX hands GDS the projection without direction, so it is enough to return each pair once, like the id(e1) < id(e2) in the example, without repeating the reversed pair. The refinement involves random draws, so groups may vary between runs, and communityId is only a label.
Parameters:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)MAX_LEVELS→maxLevels(Integer; default10)GAMMA→gamma(Float; default1.0)THETA→theta(Float; default0.01)TOLERANCE→tolerance(Float; default0.0001)INCLUDE_INTERMEDIATE_COMMUNITIES→includeIntermediateCommunities(Boolean; defaultfalse)SEED_PROPERTY→seedProperty(String)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, intermediateCommunityIds, communityId
Graph: undirected only — the platform treats the relationships as undirected automatically; accepts relationship weights.
SLLPA
Finds overlapping groups, allowing the same company to belong to more than one group.
What it does: It is label propagation with memory: each company keeps every label it has ever heard. In each round, every company passes each neighbour a label drawn at random from its own memory and adds to its memory the most frequent label among those it received. At the end it keeps the labels whose frequency in memory exceeds MINASSOCIATIONSTRENGTH — which is why a company may come out with several groups.
When to use: Use it when companies naturally take part in more than one group — a partner tied to two families, an office serving two conglomerates. If each company must sit in a single group, use LABEL_PROPAGATION, LOUVAIN or LEIDEN.
In business language:
- Which companies belong to more than one business group at the same time?
- Which companies bridge two ownership groups?
- Where do groups of companies overlap?
- Which companies have a double allegiance, tied to different business families?
- Which companies move across several conglomerates and deserve closer attention?
How to use: Project the companies in NODES and the tie in RELS in both directions (in the example, a shared individual partner in Maceió/AL). MAXITERATIONS is required and sets how many rounds run and the size of the memory; MINASSOCIATION_STRENGTH (default 0.2) is the minimum frequency for a label to stay, and PARTITIONING ("RANGE", "DEGREE" or "AUTO", in quotes) chooses how the work is split among threads. The output has nodeId and values, a map whose communityIds entry lists the company's groups; more than one id means overlap.
Example:
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 ;Caveats: The random draws are seeded from the clock and there is no option to fix them, so each run may give different groups (DATTAX reuses for 30 minutes the result of the same projection with the same options). Labels follow the direction of relationships: return each pair in both directions, otherwise some companies never hear their neighbours. A low MINASSOCIATIONSTRENGTH spreads each company across many groups, and a high one brings the result close to a split without overlap.
Parameters:
MAX_ITERATIONS→maxIterations(Integer; required)MIN_ASSOCIATION_STRENGTH→minAssociationStrength(String; default0.2)PARTITIONING→partitioning(String; default"RANGE")CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, values
Graph: directed or undirected; no relationship weights.
K1COLORING
Assigns each company a colour so that linked companies get different colours, using few colours.
What it does: It colours the companies greedily and in parallel: each company takes the smallest colour no neighbour is using. Since neighbours processed at the same time may pick the same colour, a validation step finds those conflicts and only the conflicting companies are recoloured in the next round, until no conflict is left or MAX_ITERATIONS is reached. The number of colours it obtains is small, but not necessarily the smallest possible.
When to use: Use it when you need to split companies into batches in which none is tied to another in the same batch, accepting whatever number of batches the algorithm finds. If the number of lots is fixed and some ties may stay inside the same lot, use APPROXMAXK_CUT.
In business language:
- How can companies be split into audit rounds so that no two linked companies fall in the same round?
- What is the smallest number of visit shifts that never approaches companies with shared partners together?
- How can lots of companies be formed with no ownership tie among them?
- How can independent samples be built, where no company shares a partner with another in the same lot?
How to use: Project the companies in NODES and, in RELS, the tie that must not repeat inside a batch, in both directions (in the example, a shared individual partner in Maceió/AL). MAXITERATIONS caps the correction rounds and MINCOMMUNITY_SIZE hides colours with few companies. The output has nodeId and color; each colour is a batch, and the number of distinct colours is the number of rounds needed.
Example:
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 ;Caveats: Return each pair in both directions: the projection is directed and each company only avoids the colour of the neighbours its relationships point to. If the run stops at MAX_ITERATIONS before converging, some neighbours may keep the same colour; raise the limit. The colour is only a label, companies with no ties all get the smallest colour, and the assignment changes between runs because of parallel processing.
Parameters:
MAX_ITERATIONS→maxIterations(Integer; default10)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, color
Graph: directed or undirected; no relationship weights.
APPROXMAXK_CUT
Splits the companies into K groups so that as many ties as possible run between different groups rather than inside them.
What it does: It is a randomised heuristic: in each of the ITERATIONS attempts it spreads the companies at random across the K groups and improves the split by local search, moving companies between groups while that increases the total (or weight) of cut ties. It keeps the best attempt. With VNSMAXNEIGHBORHOOD_ORDER above 0 it perturbs the solution in ever larger neighbourhoods to escape local optima.
When to use: Use it when the number of groups is fixed (teams, lots, shifts) and the goal is to keep related companies apart as much as possible, accepting that some ties stay inside the same group. If no tie may stay inside a group and the number of groups is free, use K1COLORING; to bring related companies together rather than apart, use LOUVAIN.
In business language:
- How can companies be distributed among review teams so that linked companies go to different teams?
- How can suppliers be split into lots so that companies with shared partners do not bid for the same lot?
- Which split into three groups leaves most ownership ties between different groups?
- How can companies be divided into batches that mix related companies as little as possible?
How to use: Project the companies in NODES and, in RELS, the tie that should be split apart, weighted when some ties count more (in the example, shared individual partners in Maceió/AL, weighted through RELATIONSHIPWEIGHTPROPERTY). K sets the number of groups (default 2), ITERATIONS the number of attempts, VNSMAXNEIGHBORHOODORDER turns on the neighbourhood search and MINCOMMUNITY_SIZE hides from the output the companies of small groups. The output has nodeId and communityId, from 0 to K-1, giving each company's group.
Example:
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 ;Caveats: The result is approximate and changes on every run; to repeat it, GDS only accepts RANDOMSEED together with CONCURRENCY=1 (DATTAX reuses for 30 minutes the result of the same projection with the same options). More ITERATIONS and the neighbourhood search improve the cut but multiply the running time. MINCOMMUNITY_SIZE only filters the output; it does not impose a minimum group size.
Parameters:
K→k(Integer; default2)ITERATIONS→iterations(Integer; default8)VNS_MAX_NEIGHBORHOOD_ORDER→vnsMaxNeighborhoodOrder(Integer; default0 (VNS off))RANDOM_SEED→randomSeed(Integer)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)MIN_COMMUNITY_SIZE→minCommunitySize(Integer; default0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, communityId
Graph: directed or undirected; accepts relationship weights.
HDBSCAN
Groups companies by the density of a numeric profile, works out on its own how many groups exist and sets atypical ones aside as noise.
What it does: For each company it measures the distance to its SAMPLES-th nearest neighbour in the NODEPROPERTY vector (the core distance) and links all companies through a minimum spanning tree, with distances adjusted by that local density. It then cuts the tree at increasing density levels and picks the groups that stay stable and hold at least MINCLUSTERSIZE companies; companies that fit none of them get the label -1 (noise). The neighbour search uses a k-d tree whose leaf size is LEAFSIZE.
When to use: Use it when you do not know how many profiles exist, when groups have different densities or when the very goal is to find the companies that stand out. If the number of groups is known and every company must be classified, use KMEANS, which is simpler to interpret.
In business language:
- Which companies stand apart from all the others and fit no profile?
- Are there natural concentrations of companies with very similar profiles?
- Which companies have atypical characteristics that deserve individual review?
- How many natural company profiles emerge without me having to say how many I want?
- Which companies break the capital, branch and partner pattern of the rest of the city?
How to use: Return in NODES the numeric vector of each company and name that column in NODEPROPERTY, which is required (in the example, perfil = [log10 of share capital, log10 of the number of establishments, log10 of the number of individual partners] in Maceió/AL); RELS only satisfies the syntax, since the algorithm ignores relationships. MINCLUSTERSIZE is the smallest accepted group, SAMPLES sets how conservative the algorithm is (higher values flag more companies as noise) and LEAFSIZE tunes the search tree. The output has nodeId and label: the group number, or -1 for noise.
Example:
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 ;Caveats: The vector's columns must be on comparable scales, and every company must have the vector (use coalesce in NODES). The label -1 is not a group but the atypical companies; to filter them in DATTAX use label < 0, because the language does not accept the negative literal -1. Cost grows with the number of companies and of vector dimensions: cut the data by municipality or CNAE.
Parameters:
NODE_PROPERTY→nodeProperty(String; required)MIN_CLUSTER_SIZE→minClusterSize(Integer; default5)SAMPLES→samples(Integer; default10)LEAF_SIZE→leafSize(Integer; default1)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, label
CONDUCTANCE
Measures, for each group of an existing split, what share of its companies' ties goes out to companies in other groups.
What it does: It reads each company's group from the property named in COMMUNITYPROPERTY and walks the ties leaving each company, adding them up as internal (the neighbour is in the same group) or external (it is in another one). A group's conductance is external ÷ (external + internal): 0 means a closed group with no tie going out, and 1 means every tie goes out. With RELATIONSHIPWEIGHT_PROPERTY it sums weights instead of counting ties.
When to use: Use it to assess a split you already have — economic sector, municipality, accounting firm — and see which groups are well bounded and which leak. For a score that compares the split with chance and adds up over the whole set, use MODULARITY_METRIC; to discover the groups, use LOUVAIN or LEIDEN.
In business language:
- In which sectors do partners also keep companies in other sectors?
- Which sectors form ownership ecosystems closed in on themselves?
- Does splitting by economic activity isolate the groups of partners well, or does it leak?
- Which segments have the most ties with companies outside their own segment?
- Is the grouping we use in audits well bounded, or are companies too tied to other groups?
How to use: Return in NODES, besides the id, the integer group number of each company and name that column in COMMUNITYPROPERTY, which is required — in the example, setor = the first two digits of the main CNAE (the division), 0 when missing, for companies in Maceió/AL. RELS carries the ties to be assessed, in both directions, and RELATIONSHIPWEIGHT_PROPERTY is optional. The output has one row per group, with community (the property value) and conductance; sort in ascending order to see the most closed groups.
Example:
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 ;Caveats: The count starts from the relationships leaving each company: with a pair in a single direction, a tie between two groups only enters the count of one of them, so return both directions. The group property must be an integer; companies with no value or a negative value are left out of the count, and groups with no tie at all do not appear in the output.
Parameters:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)COMMUNITY_PROPERTY→communityProperty(String; required)CONCURRENCY→concurrency(Integer; default4)
Returned columns: community, conductance
Graph: directed or undirected; accepts relationship weights.
MODULARITY_METRIC
Computes the modularity of an existing split, group by group, showing how much each group concentrates internal ties beyond what chance would produce.
What it does: It reads each company's group from COMMUNITY_PROPERTY and, for each group, compares the weight of internal ties with what would be expected if ties were spread at random while keeping how much each company connects: (internal - group total² ÷ network total) ÷ network total. The result is each group's contribution, and the sum of all contributions is the modularity of the whole split. Positive values mean groups more cohesive than chance would produce.
When to use: Use it to score an existing split (sector, municipality, accountant portfolio) or to compare two splits over the same projection. CONDUCTANCE shows how much each group leaks on its own; modularity compares with chance and adds up over the whole set, which is the same criterion LOUVAIN and LEIDEN optimise.
In business language:
- Does the classification by economic sector explain how partners group together?
- Are the groups we defined by sector, municipality or office cohesive or arbitrary?
- Which sectors concentrate internal ownership ties more than chance would explain?
- How much does each sector contribute to the overall cohesion of the ties between companies?
- Is it worth analysing companies by sector, or do the real groups cut across sectors?
How to use: Return in NODES the group number of each company and name that column in COMMUNITYPROPERTY, which is required — in the example, setor = the first two digits of the main CNAE of the companies in Maceió/AL. RELS carries the ties in both directions, with an optional weight in RELATIONSHIPWEIGHT_PROPERTY (in the example, shared partners). The output has one row per group, with communityId (the property value) and modularity; add up the modularity column to get the score of the whole split.
Example:
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 ;Caveats: Per-group values are small and their sum never exceeds 1; a group with a negative contribution has fewer internal ties than chance would give. Since only relationships leaving each company are counted, return each pair in both directions so the tie counts for both groups. Companies without the property form a separate group, so fill the value with coalesce.
Parameters:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)COMMUNITY_PROPERTY→communityProperty(String; required)CONCURRENCY→concurrency(Integer; default4)
Returned columns: communityId, modularity
Graph: directed or undirected; accepts relationship weights.
Pathfinding
SHORTESTPATHDIJKSTRA
Finds the lowest-cost path between two nodes of the projection; without weights, it is the one with the fewest hops.
What it does: Starting from the source node, it settles one node at a time — always the unresolved node closest to the source — until it reaches the target; at that point the path found is guaranteed to be the cheapest. Each link's cost comes from the RELS weight column; without it every link costs 1 and the answer is the path with the fewest hops. It returns a single row, or none when the target cannot be reached from the source following the direction of the edges.
When to use: For pair questions — known source and target — with positive weights or no weights at all. Prefer A* when nodes have coordinates and the weight is distance, Yen when you need the alternatives, and Bellman-Ford when there are negative weights.
In business language:
- Through how many intermediate companies and partners does person X reach company Y?
- What is the most direct connection between businessman X and company Y?
- Is there any corporate connection between company A and company B? Which one is the shortest?
- How many links separate the suspicious partner X from company Y under investigation?
- What is the shortest chain of shareholdings linking group A to company B?
- Through whom does person X connect to company Y with the fewest hand-offs?
How to use: Project the network with links in both directions when the relationship has no direction (partner and company reach each other) and get source and target as elementId in a previous MATCH (RETURN elementId(o) AS origem): FROM and TO take those strings. RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIP_WEIGHT) points to the RELS weight column, and CONCURRENCY goes up to 4. The returned row has totalCost (total cost; without weights, the number of links), nodeIds (internal ids in route order), costs (accumulated cost at each step), path (the route), plus index, sourceNode and targetNode.
Example:
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 ;Caveats: The projection is directed: without both directions in RELS, the route only moves the way the link was recorded and may miss a connection that exists. Negative weights are not supported (use BELLMAN_FORD), and a null weight becomes NaN in the projection — handle it with coalesce in RELS. The ids in nodeIds are internal (id(n)), not elementId.
Parameters:
SOURCE_NODE→sourceNode(Integer; filled in by the syntax)TARGET_NODE→targetNode(Integer; filled in by the syntax)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Graph: directed or undirected; accepts relationship weights.
ASTAR
Finds the lowest-cost path between two nodes that have coordinates, using the straight-line distance to the target to explore less of the network.
What it does: It works like Dijkstra, but picks the next node to expand by adding the cost travelled so far to an estimate of what is left: the straight-line distance to the target, computed with the haversine formula, in nautical miles, from each node's latitude and longitude. With that estimate the search heads toward the target and visits far fewer nodes in large geographic networks. It returns one row with the route, or none if the target is unreachable.
When to use: When the nodes are places — addresses, establishments, support points — with latitude and longitude, and the link weight is the distance between them. For links with no geographic meaning (corporate ties), use Dijkstra (SHORTEST PATH).
In business language:
- What is the shortest-distance route between the Maceió branch and the Arapiraca branch, stopping only at units of the same group?
- Which itinerary covers the fewest kilometres for the inspection team to go from establishment A to B, passing through neighbouring establishments?
- How do we move goods from warehouse X to store Y using only units at most 50 km apart?
- What sequence of nearby addresses links the head office to the farthest branch?
- Which intermediate units does the lowest-mileage route between units A and B go through?
How to use: Project the nodes with latitude and longitude columns in NODES — in the example, those of each address's postcode (c.latitude AS lat, c.longitude AS lon) — and give their names in the required options LATITUDEPROPERTY and LONGITUDEPROPERTY; FROM and TO take the elementId of source and target. Use the link distance in nautical miles as the weight (RELATIONSHIPWEIGHTPROPERTY) — point.distance(...) / 1852.0 in RELS — so the estimate never exceeds the real cost; CONCURRENCY goes up to 4. The returned row has totalCost (total distance), nodeIds (internal ids in order), costs (accumulated at each step), path, index, sourceNode and targetNode.
Example:
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" ;Caveats: The Receita (Brazilian tax authority) load brings no coordinates: the postcode's latitude and longitude come from a separate geolocation enrichment, and A* only runs after it, with both on every projected node. With weights below the real distance in nautical miles — or no weight, when every link counts 1 — the estimate overshoots what is left and the returned route may not be the cheapest. Project links in both directions, because the projection is directed.
Parameters:
SOURCE_NODE→sourceNode(Integer; filled in by the syntax)TARGET_NODE→targetNode(Integer; filled in by the syntax)LATITUDE_PROPERTY→latitudeProperty(Float; required)LONGITUDE_PROPERTY→longitudeProperty(Float; required)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Graph: directed or undirected; accepts relationship weights.
YENS
Finds the K lowest-cost paths between two nodes, from best to worst, without repeating a node within the same path.
What it does: It first computes the best path and, from it, builds candidates by branching off at each point of the previous route — blocking the link already used on that stretch — always keeping the cheapest candidate as the next one on the list. It repeats until it has K paths or runs out of alternatives. Each path is simple, with no repeated node, and the results are numbered by index, from cheapest to most expensive.
When to use: When a single answer is not enough: alternative routes, redundant ties, or what remains if the main intermediary drops out. For only the best connection, Dijkstra (SHORTEST PATH) is cheaper, and Yen's cost grows with K.
In business language:
- What are the three shortest alternative connections between person X and company Y?
- Besides the most direct connection, through which other intermediaries does group A reach company B?
- If the partner who acts as the main bridge leaves, what is the next connecting route between X and Y?
- List the five shortest corporate chains connecting company A to company B.
- In how many different ways does person X reach control of company Y?
How to use: Project the network as for SHORTEST PATH (links in both directions when the relationship has no direction) and give source and target by elementId in FROM and TO; K is part of the syntax (K 3). RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIP_WEIGHT) sets the cost of each link and CONCURRENCY goes up to 4. Each row is one path: index (0 is the best), totalCost, nodeIds (internal ids in order), costs, path, sourceNode and targetNode.
Example:
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 ;Caveats: Use DISTINCT in RELS: a repeated row becomes a parallel edge, and each parallel edge counts as a different route — alternatives show up that go through the same nodes. Fewer than K rows means there are no more alternatives without repeating a node; negative weights are not supported.
Parameters:
SOURCE_NODE→sourceNode(Integer; filled in by the syntax)TARGET_NODE→targetNode(Integer; filled in by the syntax)K→k(Integer; filled in by the syntax; default1)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Graph: directed or undirected; accepts relationship weights.
ALLSHORTESTPATHS_DELTA
Computes, from a source node, the lowest-cost path to every reachable node, in parallel, grouping distances into buckets.
What it does: It is the parallel version of the one-to-all computation: tentative distances are spread into buckets of width DELTA, and the nodes of the same bucket are processed at the same time by several threads, which keep relaxing until the bucket settles before moving to the next one. The costs match single-source Dijkstra (on ties the chosen route may differ), obtained faster on large weighted projections; there is one row per reachable node.
When to use: On large projections with positive weights, when the question is from one source to all; on small or unweighted projections, ALLSHORTESTPATHS (Dijkstra) gives the same answer without tuning DELTA. Set DELTA to the order of magnitude of a typical link weight.
In business language:
- Taking the strength of each tie into account, how close to company X is each company in Maceió?
- Starting from businessman X, which companies does he reach through the strongest ties and which remain farther away?
- Map the reach of holding X over thousands of companies, from the strongest tie to the weakest.
- Which companies are closest to the one under investigation when we weigh how many partners each pair shares?
- What is the connection cost between company X and every company in the database, considering the weight of each relationship?
How to use: Project the network with the cost column in RELS — in the example, 1.0 / count(DISTINCT p) AS custo, which makes ties with more shared partners cheaper — and give the starting node in the required option SOURCENODE, which takes the internal id: the same id(n) used in the projection, returned by a previous MATCH with RETURN id(e). RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIPWEIGHT) points to the weight, DELTA (default 2.0) sets the bucket width and CONCURRENCY goes up to 4. Each row is one target: targetNode, totalCost, nodeIds (internal ids along the route), costs (accumulated), path, index and sourceNode.
Example:
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 ;Caveats: The output has one row per reachable node: on a large projection, cut it down first or filter and limit afterwards. SOURCE_NODE is a number (internal id), not an elementId, and must be in the projection; weights must be zero or positive. CONCURRENCY above 4 is rejected by the installed Community edition.
Parameters:
SOURCE_NODE→sourceNode(Integer; required)DELTA→delta(Float; default2.0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Graph: directed or undirected; accepts relationship weights.
ALLSHORTESTPATHS_DIJKSTRA
Computes, from a source node, the lowest-cost path to every reachable node.
What it does: It runs Dijkstra from the source without stopping at a target: it settles nodes from the closest to the farthest and, for each one, keeps the route that reached it at the lowest cost. It returns one row per reachable node, with the total cost and the sequence of nodes leading to it. Without weights, the cost is the number of links.
When to use: When the question is from one source to all on a small or medium projection, with positive weights or none. On large weighted projections, ALLSHORTESTPATHSDELTA parallelises the same computation; to just list who is nearby, without the route, BFS with MAXDEPTH is lighter.
In business language:
- How many intermediaries away from businessman X is each company in Maceió?
- Which companies does partner X reach, and through which most direct connection does he get to each one?
- List the people and companies connected to person X, from the closest to the farthest.
- How far does the influence of holding X extend, and through whom does it pass to reach each company?
- Which companies are at most four links away from suspected front man X, and through whom?
How to use: Project the network (links in both directions when the relationship has no direction) and give the source through FROM, with the elementId from a previous MATCH. RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIP_WEIGHT) sets the cost of each link and CONCURRENCY goes up to 4. Each row is one target: targetNode, totalCost (cost, or number of links without weights), nodeIds (internal ids along the route), costs, path, index and sourceNode — filter on totalCost to keep the closest ones.
Example:
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 ;Caveats: The output has one row per reachable node and each row carries the whole route: on a large network that adds up, so cut the projection down (by city, by group) before running it. Negative weights are not supported, and the source must be in the projection.
Parameters:
SOURCE_NODE→sourceNode(Integer; filled in by the syntax)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Graph: directed or undirected; accepts relationship weights.
BFS
Walks the network from a node in layers: first the direct neighbours, then their neighbours, and so on.
What it does: It visits every node one link away from the source before any node two links away, and so on, following edge direction and ignoring weights. The traversal ends when everything reachable is exhausted, when it hits the MAXDEPTH depth, or when it finds the first node in TARGETNODES, which closes the list. It returns a single row with the nodes in visiting order — closest first.
When to use: To map the neighbourhood up to N levels or find out which target of a list shows up first, without weights. With weights, use ALLSHORTESTPATHS; to follow each chain to the end before moving to the next, DFS.
In business language:
- Who is within two links of company X?
- Who are the partners of company X, and in which other companies do they appear?
- Map the corporate neighbourhood of company X down to the third level.
- Starting from person X, which company on the suspect list shows up first?
- Who are the partners of company X's partners?
How to use: Project the network (links in both directions when the relationship has no direction) and give the source through FROM, with the elementId from a previous MATCH. MAXDEPTH (default -1, no limit) caps the depth, TARGETNODES takes comma-separated internal ids (TARGET_NODES="12,57") to stop at the first one found, and CONCURRENCY goes up to 4. The returned row has sourceNode, nodeIds (internal ids in visiting order) and path.
Example:
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 ;Caveats: Without MAXDEPTH or TARGETNODES, the list covers everything reachable — on a whole corporate network, hundreds of thousands of ids in a single row. The path column chains the nodes in visiting order: two neighbours in the list are not necessarily linked to each other.
Parameters:
SOURCE_NODE→sourceNode(Integer; filled in by the syntax)TARGET_NODES→targetNodes(List of Integer; defaultempty list)MAX_DEPTH→maxDepth(Integer; default-1)CONCURRENCY→concurrency(Integer; default4)
Returned columns: sourceNode, nodeIds, path
Graph: directed or undirected; no relationship weights.
DFS
Walks the network from a node following each branch to the end before backing up and trying the next one.
What it does: From the source it goes down one link, then down a link of the node it reached, and so on, until there is nowhere left to go or it hits MAXDEPTH; only then does it back up to the last point with unexplored links. It follows edge direction, ignores weights and stops when it finds the first node in TARGETNODES. It returns one row with the nodes in visiting order, grouped branch by branch.
When to use: To follow chains to the end — shareholdings going down from a holding —, test whether a target appears in some branch, or list the structure branch by branch. The order does not reflect proximity: to know who is closest, use BFS.
In business language:
- Following the shareholdings from holding X, how far does each chain of control go?
- Going down group A's corporate structure, does company Y show up in any branch?
- List the companies controlled by company X, branch by branch, down to the last level.
- Starting from partner X, follow each connection to the end: where does it lead?
- Is there any sequence of shareholdings leading from company A to company B?
How to use: Project the network in the direction the question travels — to go down from controller to controlled, RELS from the shareholder company to the investee — and give the source through FROM, with the elementId from a previous MATCH. TARGETNODES takes comma-separated internal ids to stop at the first one found, MAXDEPTH (default -1, no limit) caps the depth and CONCURRENCY goes up to 4. The returned row has sourceNode, nodeIds (internal ids in visiting order) and path; when the target is found, it is the last id in the list.
Example:
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 ;Caveats: The visiting order does not measure distance, and the path column only chains the nodes in that order, without representing real links between adjacent ids in the list. TARGET_NODES uses the internal id (id(n)), while FROM uses the elementId.
Parameters:
SOURCE_NODE→sourceNode(Integer; filled in by the syntax)TARGET_NODES→targetNodes(List of Integer; defaultempty list)MAX_DEPTH→maxDepth(Integer; default-1)CONCURRENCY→concurrency(Integer; default4)
Returned columns: sourceNode, nodeIds, path
Graph: directed or undirected; no relationship weights.
RANDOM_WALK
Generates random walks through the network from chosen nodes, drawing each step among the neighbours, with probability proportional to the weight when there is one.
What it does: From each start node come WALKSPERNODE walks of up to WALKLENGTH nodes; at each step the next neighbour is drawn, with probability proportional to the link weight when there is one. Two factors tilt the draw: a high RETURNFACTOR makes going back to the previous node less likely, and a high INOUTFACTOR keeps the walk close to where it is, while a low one pushes it farther away. A walk ends before the requested length when it reaches a node with no outgoing link.
When to use: To sample the neighbourhood of a set of nodes — how often other nodes show up nearby, or as input to embedding and similarity models — when enumerating everything would be too expensive. It does not answer exact connection questions: use the path algorithms for that.
In business language:
- Which companies often show up around company X when we wander through the network at random?
- Generate sample trails through the partner network from these companies to train a model.
- Which companies tend to appear near the suspicious group when browsing the ties at random?
- Build sequences of neighbouring companies from these front men for a pattern analysis.
- How easily does someone leaving company X and wandering through the ties bump into company Y?
How to use: Project the network (links in both directions when the relationship has no direction) and pass the start nodes in SOURCENODES, as comma-separated internal ids (`SOURCENODES="4211,5873"`); without this option, walks start from every node. WALKLENGTH (default 80), WALKSPERNODE (default 10), RETURNFACTOR and INOUTFACTOR (default 1.0), RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIPWEIGHT), RANDOMSEED (fix it to repeat the result), WALKBUFFERSIZE (default 1000) and CONCURRENCY (up to 4) tune the sampling. Each row is one walk: nodeIds (internal ids in order) and path.
Example:
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 ;Caveats: Without SOURCENODES, the output has WALKSPER_NODE rows for every node of the projection — millions of rows on a large network. Negative weights are rejected, and a node with no outgoing link produces no walk.
Parameters:
SOURCE_NODES→sourceNodes(List of Integer; defaultList of all nodes)WALK_LENGTH→walkLength(Integer; default80)WALKS_PER_NODE→walksPerNode(Integer; default10)IN_OUT_FACTOR→inOutFactor(Float; default1.0)RETURN_FACTOR→returnFactor(Float; default1.0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)RANDOM_SEED→randomSeed(Integer; defaultrandom)WALK_BUFFER_SIZE→walkBufferSize(Integer; default1000)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeIds, path
Graph: directed or undirected; accepts relationship weights.
BELLMAN_FORD
Computes, from a source node, the lowest-cost path to every reachable node while accepting negative weights, and reports negative cycles when they exist.
What it does: It repeats relaxation rounds: in each round, the nodes whose distance improved pass the improvement on to their neighbours, until nothing changes anymore. Since it does not rely on settling the closest node, it works with negative weights. If it finds a cycle whose weights add up to a negative number — going around it would lower the cost forever —, it returns only those cycles, flagged with isNegativeCycle true, instead of the paths.
When to use: When there really are negative weights (credits, discounts, gains) or when you want to detect negative-balance circuits; with all weights positive, Dijkstra or Delta give the same answer faster. With weight -1 on every link, any reachable cycle becomes a negative cycle — a way to get the circuits themselves, which SCC only groups.
In business language:
- Is there circular ownership — A a partner of B, B of C and C of A — starting from company X?
- Are there companies that control each other, closing a circle of shareholdings?
- Is there any circuit of transactions between companies that generates credit on each round?
- Considering credits and debits on each tie, what is the accumulated balance from company X to each connected company?
- Which cross-shareholdings involve the companies controlled by group X?
How to use: Project the network with the weight column in RELS — in the example, -1.0 AS peso on every company-to-company shareholding — and give the starting node in the required option SOURCENODE, which takes the internal id (the same id(n) used in the projection). RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIPWEIGHT) points to the weight and CONCURRENCY goes up to 4. Each row has index, sourceNode, targetNode, totalCost, nodeIds (internal ids), costs, route (the route) and isNegativeCycle, which tells whether the row is a negative cycle rather than a path.
Example:
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 ;Caveats: A single reachable negative cycle is enough for the answer to bring only the cycles and no ordinary path. It is slower than Dijkstra, because it repeats rounds until things settle: cut the projection down. SOURCE_NODE is the internal id, not the elementId.
Parameters:
SOURCE_NODE→sourceNode(Integer; required)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, route, isNegativeCycle
Graph: directed or undirected; accepts relationship weights.
STEINER_TREE
Builds, from a root node, a low-cost tree that reaches every chosen target node, going only through the intermediaries it needs.
What it does: It approximately solves the directed Steiner problem: repeatedly, it looks for the not yet connected target that is cheapest to reach from the tree built so far, along lowest-cost paths, and adds the stretch that links it. With APPLY_REROUTING, it then tries to reconnect parts of the tree through cheaper stretches. The result is the list of tree nodes, each with its parent and the cost of the link between them.
When to use: When you want to connect a specific set of nodes — the root and the targets — at the lowest total cost, accepting intermediaries; it differs from the spanning tree, which covers every reachable node, and from the pair path, which links only two. The result is an approximation, with no guarantee of being optimal.
In business language:
- What is the smallest network of ties linking company X to companies A, B and C at the same time?
- Which intermediaries are enough to connect businessman X to every company on the list?
- What is the minimal structure of partners and companies that joins these suspicious companies, starting from company X?
- Which people sit at the centre of the scheme that links these CNPJs to one another?
- How does holding X reach all these companies with the fewest links in total?
How to use: Project the network (links in both directions when the relationship has no direction) and give the required options SOURCENODE (the root) and TARGETNODES (the targets), both with internal ids — the list as comma-separated text (TARGET_NODES="5873,6120,7302"). Optional: RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIPWEIGHT), DELTA (default 2.0, bucket width of the internal search), APPLYREROUTING (lowercase boolean, default false) and CONCURRENCY (up to 4). Each row is one tree node: nodeId, parentId (the node it is reached from; for the root, itself) and weight (cost of the link to the parent).
Example:
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" ;Caveats: A target the root cannot reach is silently left out — check that every target appears in nodeId. Negative weights are not supported, and the ids are internal (id(n)), not elementId.
Parameters:
SOURCE_NODE→sourceNode(Integer; required)TARGET_NODES→targetNodes(List of Integer; required)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)DELTA→delta(Float; default2.0)APPLY_REROUTING→applyRerouting(Boolean; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, parentId, weight
Graph: directed or undirected; accepts relationship weights.
SPANNING_TREE
Builds, from a node, the tree that connects every reachable node with the lowest — or the highest — total weight.
What it does: It uses Prim's algorithm: it starts at the source and, at each step, adds the lowest-weight link (or the highest, with OBJECTIVE="maximum") leading to a node still outside the tree, until it covers the source's whole component. The result has no cycle and no redundant link: each node appears once, with its parent and the weight of the chosen link.
When to use: To find the backbone of a group: with OBJECTIVE="maximum", the strongest links that keep everyone connected; with "minimum", the cheapest way to link them all. To connect only a few chosen nodes, use the Steiner tree.
In business language:
- What is the backbone of strongest ties that keeps company X's group connected?
- Which ties form the spine of the companies linked to X, without redundancy?
- How do we link all the companies in the group using only the connections with the most shared partners?
- What is the most economical way to connect every company reachable from X?
- Draw the minimal chart of ties among the Maceió companies connected to company X.
How to use: Project the network with one row per linked pair and the weight in RELS — in the example, count(DISTINCT p) AS socios, the shared partners — and give the source in the required option SOURCENODE, with the node's internal id. RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIPWEIGHT) points to the weight, OBJECTIVE picks "minimum" (default) or "maximum" and CONCURRENCY goes up to 4. Each row is one tree node: nodeId, parentId (for the source, itself) and weight (weight of the link to the parent).
Example:
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" ;Caveats: The algorithm treats links as having no direction — each linked pair counts both ways —, so one row per pair in RELS is enough. The tree covers only what the source reaches: companies with no connection to it are left out of the answer.
Parameters:
SOURCE_NODE→sourceNode(Integer; required)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)OBJECTIVE→objective(String; default'minimum')CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, parentId, weight
Graph: undirected only — the platform treats the relationships as undirected automatically; accepts relationship weights.
TOPOLOGICAL_SORT
Orders the nodes of a cycle-free network so that every node comes before the ones it points to.
What it does: It starts with the nodes that have no incoming link and, as they leave the queue, releases the nodes whose incoming links have all been served, until the network is exhausted. Nodes that are part of a cycle — or that can only be reached through one — are never released and stay out of the order. With COMPUTEMAXDISTANCEFROMSOURCE, it also computes, for each node, the number of links in the longest chain reaching it from a node with no incoming link.
When to use: To order dependencies — controlling company before controlled company — and find each node's layer in a hierarchy; it requires an acyclic graph. For the route of the longest chain, not just the order, use DAGLONGESTPATH; to locate the cycles in the way, SCC.
In business language:
- In what order should we analyse the companies of group X, from the controlling ones down to the controlled?
- At which layer of group X's corporate structure is each company?
- Which companies sit at the top of the shareholding pyramid and which at the bottom?
- How many levels of holdings are there above each company in the group?
- Is the group's shareholding structure a clean hierarchy, or are there companies controlling each other in a circle?
How to use: Project only the dependency links, in the direction of the hierarchy — from the shareholder company to the investee —, over a slice of the group; in the example, everything below the holding. There is no required option: COMPUTEMAXDISTANCEFROMSOURCE=true (lowercase boolean; default false) turns on the layer computation, and CONCURRENCY goes up to 4. Rows come out in topological order, with nodeId (internal id) and maxDistanceFromSource (the node's layer; null when the computation is off).
Example:
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 ;Caveats: The graph must be acyclic: nodes in a cycle, and everything that depends on them, silently drop out of the order — run SCC first on the same slice, and any component with more than one node is a cycle. Weights are not used: the layer counts links.
Parameters:
COMPUTE_MAX_DISTANCE_FROM_SOURCE→computeMaxDistanceFromSource(Boolean; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, maxDistanceFromSource
Graph: directed only (relationships must have a direction); no relationship weights.
DAGLONGESTPATH
In a cycle-free network, finds for each node the longest — highest-cost — chain ending at it.
What it does: It walks the network in topological order, starting from the nodes with no incoming link, and for each node keeps the predecessor that gives it the highest accumulated cost; since there is no cycle, this solves in linear time a problem that has no practical solution on networks with cycles. It returns one row per node reached, with the longest chain ending at it; sourceNode is where that chain starts, a node with no incoming link. Without weights, each link counts 1 and the cost is the number of links.
When to use: To measure the depth of hierarchies and dependency chains — layers of holdings, the critical sequence of steps — on an acyclic graph. For just the order and the layer, TOPOLOGICAL_SORT is enough; on a network with cycles, the result is not valid.
In business language:
- What is the longest chain of shareholder companies above company X?
- How many layers of holdings separate the top controller from company X?
- Which companies sit at the end of the longest shareholding chains?
- Where is the corporate structure deepest, with the most intermediate companies stacked up?
- What is the full sequence of controlling companies, from top to bottom, down to company X?
How to use: Project the links in the direction of the hierarchy — from the shareholder company to the investee — over an acyclic slice; in the example, every company above company X. There is no required option: RELATIONSHIPWEIGHTPROPERTY (legacy name RELATIONSHIP_WEIGHT) replaces the link count with a weight, and CONCURRENCY goes up to 4. Each row is one end node: targetNode, sourceNode (start of the chain), totalCost, nodeIds (internal ids, top to bottom), costs, path and index; sort by totalCost to see the longest ones.
Example:
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 ;Caveats: It requires an acyclic graph: circular ownership makes the cycle's nodes, and those depending on them, drop out of the answer or come with an incomplete chain — check with SCC first. Nodes with no incoming link show up as a one-node chain with cost 0.
Parameters:
RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: index, sourceNode, targetNode, totalCost, nodeIds, costs, path
Graph: directed only (relationships must have a direction); accepts relationship weights.
Topology
ARTICULATION_POINTS
Finds the nodes whose removal would break the network into disconnected pieces.
What it does: The algorithm walks the network depth-first a single time, recording the order in which each node is visited and the earliest point its descendants can reach by another path. When no descendant can get back to before a node without going through it, that node is an articulation point. For each one, the algorithm reports how many pieces its group would split into and the size of the largest and the smallest piece.
When to use: Use it to find single points of failure, the nodes that on their own hold groups together. For that question it is more direct than BETWEENNESS, because it gives a yes-or-no answer and the size of the damage; to find the tie (rather than the node) that holds the connection, use BRIDGES.
In business language:
- Which partners are the only link between groups of construction companies that would otherwise be separate?
- If a person leaves the partner roster, which groups of companies lose touch with each other?
- Who is the linchpin whose exit would take the company network apart?
- Which companies single-handedly hold together different partner cores?
- Where are the single points of failure in a sector's corporate web?
How to use: Project the relationship once per pair (in the example, each tie between a person and a construction company in Campinas, with DISTINCT). The only option is CONCURRENCY. The output lists only the nodes that are articulation points, with nodeId and resultingComponents, a map with count (how many pieces the group splits into), max and min (size of the largest and the smallest piece); the cases that matter most are those with a high min, where the exit separates large groups.
Example:
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 ;Caveats: GDS only runs this algorithm on an undirected projection, in which each tie counts both ways; return each pair only once in RELS. In sparse networks such as partner networks, almost every node that links a loose end to the rest is an articulation point with min equal to 1; focus on those that separate large pieces. The walk is linear in the size of the network but runs sequentially, so the main cost is usually the query that builds the projection.
Parameters:
CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, resultingComponents
Graph: undirected only — the platform treats the relationships as undirected automatically; no relationship weights.
BRIDGES
Finds the ties that, if undone, would split the network in two.
What it does: The algorithm runs a depth-first search and, for each relationship it follows, checks whether the lower side can get back to the upper side by another path; if it cannot, the relationship is a bridge. For each bridge, it reports both ends and the size of the two groups that would remain without it.
When to use: Use it when the question is about the tie rather than the node: which stake or partnership is the only link between two groups. To find the node whose exit splits the network, use ARTICULATION_POINTS.
In business language:
- Which corporate tie is the only link between two groups of road-construction companies?
- If this partnership is dissolved, which groups of companies lose contact?
- Which stakes single-handedly hold up the connection between two economic cores?
- Where does a sector's partner network hang by a single thread between two sides?
- Which relationships between companies, if cut, would isolate an entire group?
How to use: Project the relationship once per pair (in the example, road-construction companies linked by shared partners, with id(e1) < id(e2) and DISTINCT). The only option is CONCURRENCY. The output has one row per bridge, with from and to (the internal ids of the two ends) and remainingSizes, the size of the two groups left without it; the second number is the side of to, and the bridges that matter are those that leave two large numbers.
Example:
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 ;Caveats: GDS only runs this algorithm on an undirected projection; return each pair only once, because two relationships between the same pair form a double link and are never a bridge. In sparse networks almost every tie with a loose end is a bridge, with one of the sizes equal to 1; focus on those that separate large groups. The walk is linear in the size of the network, and the main cost is usually the query that builds the projection.
Parameters:
CONCURRENCY→concurrency(Integer; default4)
Returned columns: from, to, remainingSizes
Graph: undirected only — the platform treats the relationships as undirected automatically; no relationship weights.
Similarity
NODE_SIMILARITY
Points out the most alike pairs of entities by the set of neighbors they share, such as companies with the same partners.
What it does: Every node with outgoing relationships is represented by the set of neighbors those relationships reach, and each pair gets a score from 0 to 1 for how much the two sets overlap: by default, shared neighbors divided by all distinct neighbors of the two (JACCARD), with OVERLAP and COSINE as alternatives and an optional relationship weight. Pairs with no neighbor in common are left out. Each node keeps only its TOPK best pairs, or its BOTTOMK worst, and TOP_N caps the whole answer. The comparison is exact, pair by pair, with no sampling.
When to use: Use it when likeness lies in the structure — entities pointing to the same neighbors, such as companies with the same partners or suppliers with the same customers —, ideally on a bipartite graph. If the comparison is by attributes (capital, counts, a set of activity codes) or by a precomputed embedding, prefer KNN, which is approximate and scales better; to compare a single entity or group against the rest, use FILTEREDNODESIMILARITY.
In business language:
- Which pairs of companies in the city share almost all of their partners?
- Are there companies with exactly the same ownership, as if they were one operation under different tax IDs?
- Which companies are twins of each other in terms of who owns them?
- Which partners keep showing up together in the same companies?
- Which suppliers serve practically the same customer base?
How to use: Project relationships going out of the entity being compared toward what it shares — from (Empresa) to (Pessoa), with RETURN id(e) AS source, id(p) AS target — and put both ends in NODES: only nodes with outgoing relationships are compared. No option is mandatory: TOPK (default 10) sets how many pairs each node gets, SIMILARITYCUTOFF drops low scores, DEGREECUTOFF skips nodes with few neighbors, SIMILARITYMETRIC switches JACCARD to OVERLAP or COSINE, and RELATIONSHIPWEIGHTPROPERTY uses a numeric RELS column as weight. The output has node1 and node2 — the NODES id values — and similarity, from 0 to 1; the same pair may appear in both orders.
Example:
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 ;Caveats: Cost grows with the square of the number of compared nodes: project a slice (city, activity code, group) instead of the whole database. Single-partner companies tie at 1.0 with every other company of the same partner, and DEGREE_CUTOFF=2 removes them from the comparison. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
SIMILARITY_CUTOFF→similarityCutoff(Float; default1e-42)DEGREE_CUTOFF→degreeCutoff(Integer; default1)UPPER_DEGREE_CUTOFF→upperDegreeCutoff(Integer; default2147483647)TOP_K→topK(Integer; default10)BOTTOM_K→bottomK(Integer; default10)TOP_N→topN(Integer; default0)BOTTOM_N→bottomN(Integer; default0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SIMILARITY_METRIC→similarityMetric(String; defaultJACCARD)USE_COMPONENTS→useComponents(Boolean or String; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: node1, node2, similarity
Graph: directed or undirected; accepts relationship weights.
FILTEREDNODESIMILARITY
Runs the same shared-neighbor comparison as NODE_SIMILARITY, but only between a chosen source group and a chosen target group.
What it does: It uses the NODE_SIMILARITY score — overlap of the neighbor sets, JACCARD by default — but only computes pairs whose first node passes the source filter and whose second node passes the target filter. Each filter takes an internal id (the NODES id) or a projection label, created by the labels column in NODES. Neighbors are still all projected nodes: the filter only narrows who gets compared, so the number of comparisons shrinks with the groups.
When to use: Use it when the question starts from one entity or a defined group — who resembles company X, which new companies repeat the partners of those already investigated — instead of everyone against everyone. For all pairs of a slice, use NODESIMILARITY; if likeness is about attributes rather than neighbors, use FILTEREDKNN.
In business language:
- Which companies have an ownership structure like that of company X?
- Which other companies are run by practically the same people as company X?
- Does company X have a mirror company with the same group of partners?
- Among companies opened this year, which ones repeat the partners of companies already under investigation?
- Which suppliers compete for the same customer base as supplier Y?
How to use: Project as in NODESIMILARITY — relationships from the compared entity to what it shares — and add to NODES the labels column, a list of text per node, which creates the labels the filters use (for instance, CASE WHEN n.empresaCnpj = '12345678' THEN ['Referencia'] ELSE labels(n) END AS labels). SOURCENODEFILTER and TARGETNODEFILTER are mandatory: each takes an internal id (a number) or a label in quotes. TOPK, SIMILARITYCUTOFF, DEGREECUTOFF and SIMILARITYMETRIC behave as in NODESIMILARITY, and the output is node1 (from the source group), node2 (from the target group) and similarity, from 0 to 1.
Example:
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 ;Caveats: The label used in a filter must exist in the projection: without the labels column in NODES, nodes have no label and a filter by name is rejected. Bring into NODES and RELS every partner of each candidate company, not only those shared with the source, otherwise the neighbor total shrinks and the score is inflated. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
SIMILARITY_CUTOFF→similarityCutoff(Float; default1e-42)DEGREE_CUTOFF→degreeCutoff(Integer; default1)UPPER_DEGREE_CUTOFF→upperDegreeCutoff(Integer; default2147483647)TOP_K→topK(Integer; default10)BOTTOM_K→bottomK(Integer; default10)TOP_N→topN(Integer; default0)BOTTOM_N→bottomN(Integer; default0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)SIMILARITY_METRIC→similarityMetric(String; defaultJACCARD)USE_COMPONENTS→useComponents(Boolean or String; defaultfalse)SOURCE_NODE_FILTER→sourceNodeFilter(Integer or List of Integer or String; required)TARGET_NODE_FILTER→targetNodeFilter(Integer or List of Integer or String; required)CONCURRENCY→concurrency(Integer; default4)
Returned columns: node1, node2, similarity
Graph: directed or undirected; accepts relationship weights.
KNN
Finds, for each entity, the K most alike ones by a numeric property or list of numbers, with no need for shared neighbors.
What it does: It compares nodes by the value of a projected property: a single number (score 1/(1 + difference)), a list of decimals (cosine) or a list of integers, treated as a set (JACCARD). Instead of testing every pair, it starts from randomly drawn neighbors and improves each node's list by looking at neighbors of neighbors, until changes fall below DELTATHRESHOLD or MAXITERATIONS runs out. The result is approximate, but cost stays close to linear in the number of nodes; projected relationships do not enter the score and only feed the initial draw when INITIAL_SAMPLER="randomWalk".
When to use: Use it when likeness lies in attributes — capital, counts, the set of activity codes, an embedding — rather than in shared neighbors; being approximate, it is the choice for large slices. If alike means pointing to the same neighbors, use NODESIMILARITY, which is exact; to compare only one group against another, use FILTEREDKNN.
In business language:
- Which companies in the same group carry out practically the same activities, suggesting revenue split across several tax IDs?
- Which companies have the closest profile to each other in capital, branches and number of partners?
- Which pairs of companies look like clones of each other in the registry?
- Which establishments resemble each other most by their set of economic activities?
- Which companies look alike even without any partner in common?
How to use: Project into NODES only the nodes to compare, with the property as a column (RETURN id(e) AS id, [...] AS atividades); NODEPROPERTIES is mandatory and takes the name of ONE projected column, which must have a value on every node. PROJECT GRAPH requires a RELS, and any link among the projected nodes will do, because the score ignores relationships. TOPK sets how many neighbors each node gets, SIMILARITYCUTOFF drops low scores, SAMPLERATE and MAXITERATIONS trade accuracy for time, and RANDOMSEED is only accepted with CONCURRENCY=1. The output has node1 and node2 — the NODES id values — and similarity, from 0 to 1.
Example:
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 ;Caveats: The result is approximate and may change between runs: to repeat it, set RANDOM_SEED with CONCURRENCY=1. Decimal lists must have the same length on every node, and integer lists are compared as sets, so remove repeats with DISTINCT; a node without a value for the property makes the run fail. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
NODE_PROPERTIES→nodeProperties(String or Map or List of Strings / Maps; required)TOP_K→topK(Integer; default10)SAMPLE_RATE→sampleRate(Float; default0.5)DELTA_THRESHOLD→deltaThreshold(Float; default0.001)MAX_ITERATIONS→maxIterations(Integer; default100)RANDOM_JOINS→randomJoins(Integer; default10)INITIAL_SAMPLER→initialSampler(String; default"uniform")RANDOM_SEED→randomSeed(Integer)SIMILARITY_CUTOFF→similarityCutoff(Float; default0.0)PERTURBATION_RATE→perturbationRate(Float; default0.0)CONCURRENCY→concurrency(Integer; default4)
Returned columns: node1, node2, similarity
FILTERED_KNN
A KNN variant that looks for the most alike neighbors only for the chosen source nodes, and only among the chosen target nodes.
What it does: It uses the same score and the same approximate process as KNN — neighbors drawn at random and refined through neighbors of neighbors —, but only returns results for nodes that pass the source filter, and only with neighbors that pass the target filter. Each filter takes an internal id (the NODES id) or a label created by the labels column in NODES. With SEEDTARGETNODES on, each source list starts already filled with target nodes, and if there are at most TOP_K targets the answer is exact.
When to use: Use it when the question has a starting point — the companies most like company X, the new suppliers that resemble problematic ones — and the comparison is by attributes. For all pairs of a slice, use KNN; if likeness comes from shared neighbors, use FILTEREDNODESIMILARITY.
In business language:
- Which companies at the same postal code as company X carry out the same activities?
- Which companies in the city have a registry profile like that of company X?
- Among new suppliers, which ones resemble those that already caused trouble?
- Is there another company at the same address copying company X's line of business?
- Which companies resemble, by size and activities, those already fined?
How to use: Project the nodes with the property as a column and add the labels column (a list of text) to mark source and target, for instance CASE WHEN e.empresaCnpj = '12345678' THEN ['Referencia'] ELSE ['Empresa'] END AS labels. NODEPROPERTIES, SOURCENODEFILTER and TARGETNODEFILTER are mandatory: the first names the compared column, and the filters take an internal id (a number) or a label in quotes. TOPK, SIMILARITYCUTOFF and SEEDTARGET_NODES=true (boolean in lowercase) tune the answer, which has node1 (source), node2 (target) and similarity, from 0 to 1.
Example:
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 ;Caveats: As in KNN, the result is approximate and only repeats with RANDOMSEED and CONCURRENCY=1; when targets are few compared with all nodes, turn on SEEDTARGET_NODES so they are not left out of the draw. The filter label must exist in the projection, and the property must have a value on every node, including those that are neither source nor target. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
NODE_PROPERTIES→nodeProperties(String or Map or List of Strings / Maps; required)TOP_K→topK(Integer; default10)SAMPLE_RATE→sampleRate(Float; default0.5)DELTA_THRESHOLD→deltaThreshold(Float; default0.001)MAX_ITERATIONS→maxIterations(Integer; default100)RANDOM_JOINS→randomJoins(Integer; default10)INITIAL_SAMPLER→initialSampler(String; default"uniform")RANDOM_SEED→randomSeed(Integer)SIMILARITY_CUTOFF→similarityCutoff(Float; default0.0)PERTURBATION_RATE→perturbationRate(Float; default0.0)SOURCE_NODE_FILTER→sourceNodeFilter(Integer or List of Integer or String; required)TARGET_NODE_FILTER→targetNodeFilter(Integer or List of Integer or String; required)SEED_TARGET_NODES→seedTargetNodes(Boolean; defaultfalse)CONCURRENCY→concurrency(Integer; default4)
Returned columns: node1, node2, similarity
Embeddings
FASTRP
Quickly produces, with no training, one embedding per node that summarizes its neighborhood in the network, to compare or group entities by the position they hold.
What it does: It draws a sparse random vector for each node and, round after round, replaces it with the average of its neighbors' vectors, reaching one hop further each round; the final embedding adds up the rounds using the ITERATIONWEIGHTS weights (default 0, 1 and 1: it skips the first hop and uses the second and third). Nodes with similar neighborhoods end up with nearby vectors, without any training step. Numeric attributes join the mix through FEATUREPROPERTIES and PROPERTYRATIO, and a negative NORMALIZATIONSTRENGTH reduces the influence of highly connected nodes.
When to use: It is the starting point for representing each entity's position in the network: the fastest of the four embeddings, reproducible with RANDOM_SEED and good on large graphs. Prefer NODE2VEC to choose between same circle and same role, HASHGNN when categorical attributes and relationship types should weigh in, and GRAPHSAGE when you need a model that applies to new nodes or slices.
In business language:
- Which companies hold a similar position in the partner network, even without a partner in common?
- How can the city's companies be split into groups by the way they connect to people and to other companies?
- Which partners play a similar role in the city's web of companies?
- Which companies are surrounded by the same kind of ownership neighborhood as a known shell company?
- Which companies in the city sit in the same pocket of the ownership network?
How to use: Project relationships in both directions — the DATTAX projection is directed and FASTRP only follows outgoing relationships —, for instance with UNWIND [[id(e), id(p)], [id(p), id(e)]] AS par RETURN par[0] AS source, par[1] AS target. EMBEDDINGDIMENSION is mandatory and sets the vector size; RANDOMSEED makes the result reproducible and RELATIONSHIPWEIGHTPROPERTY uses a RELS weight. The output is nodeId (the NODES id) and embedding, a list of numbers that can be compared, grouped or materialized.
Example:
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 ;Caveats: Without relationships in both directions, many nodes end up with an all-zero vector and the comparison loses meaning. Coordinates are only valid within the same run: another slice or another seed yields vectors that cannot be compared with earlier ones. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
PROPERTY_RATIO→propertyRatio(Float; default0.0)FEATURE_PROPERTIES→featureProperties(List of String; default[])EMBEDDING_DIMENSION→embeddingDimension(Integer; required)ITERATION_WEIGHTS→iterationWeights(List of Float; default[0.0, 1.0, 1.0])NODE_SELF_INFLUENCE→nodeSelfInfluence(Float; default0.0)NORMALIZATION_STRENGTH→normalizationStrength(Float; default0.0)RANDOM_SEED→randomSeed(Integer)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, embedding
Graph: directed or undirected; accepts relationship weights.
HASHGNN
Produces, with no training, one embedding per node that mixes the node's own binary attributes with those of its neighbors through hashing.
What it does: It starts from each node's binary attributes — 0/1 NODES columns in FEATUREPROPERTIES, continuous attributes turned into bits by BINARIZEFEATURES, or random bits from GENERATEFEATURES — and, in each of the ITERATIONS, merges the node's bits with its neighbors' and picks EMBEDDINGDENSITY of them by min-hashing. NEIGHBORINFLUENCE sets how much neighbors weigh against the node itself, and HETEROGENEOUS=true treats each relationship type separately. The result is binary, with the same dimension as the input attributes, or, with OUTPUTDIMENSION, projected into a dense vector of that size.
When to use: Use it when nodes have categorical attributes (size class, legal nature, tax regime) that should weigh together with the structure, or when there are several relationship types: it is fast like FASTRP and needs no training like GRAPHSAGE. Without attributes, GENERATE_FEATURES yields a purely structural embedding, but then FASTRP is usually the simpler choice.
In business language:
- Which companies share the same size class, the same legal nature and the same kind of ownership neighborhood?
- How can the city's companies be grouped by combining what they are with how they connect?
- Which companies under the small-business tax regime are surrounded by companies with a profile like that of an investigated group?
- Which companies have an equivalent ownership surrounding, considering the profile of each neighbor?
- Which companies look alike both in the registry and in their ties to other companies?
How to use: Project relationships in both directions and put the attributes as 0/1 NODES columns (a list such as [toInteger(porte = '01'), ...] AS perfil works). ITERATIONS and EMBEDDINGDENSITY are mandatory, and exactly one source of bits must be chosen: FEATUREPROPERTIES (0/1 columns, or continuous ones with BINARIZEFEATURES="dimension:64,threshold:0.0") or GENERATEFEATURES="dimension:64,densityLevel:1". The output is nodeId (the NODES id) and embedding, binary or dense with OUTPUT_DIMENSION.
Example:
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 ;Caveats: A FEATUREPROPERTIES column with values other than 0 and 1 is rejected unless BINARIZEFEATURES is set, and it must have a value on every node (use coalesce). Using FEATUREPROPERTIES and GENERATEFEATURES together, or neither, is also rejected. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
FEATURE_PROPERTIES→featureProperties(List of String; default[])ITERATIONS→iterations(Integer; required)EMBEDDING_DENSITY→embeddingDensity(Integer; required)HETEROGENEOUS→heterogeneous(Boolean; defaultfalse)NEIGHBOR_INFLUENCE→neighborInfluence(Float; default1.0)BINARIZE_FEATURES→binarizeFeatures(Map)GENERATE_FEATURES→generateFeatures(Map)OUTPUT_DIMENSION→outputDimension(Integer)RANDOM_SEED→randomSeed(Integer)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, embedding
Graph: directed or undirected; no relationship weights.
NODE2VEC
Learns one embedding per node from random walks over the network, so that nodes appearing on the same paths end up close together.
What it does: It launches WALKSPERNODE walks of WALKLENGTH steps from each node and trains, over those sequences, a word2vec-style model (skip-gram with negative sampling) in which nodes that appear near each other on the paths get nearby vectors. INOUTFACTOR and RETURNFACTOR shape the walk: INOUTFACTOR below 1 pushes the walk outward and highlights nodes in the same circle, above 1 keeps it near the start and highlights nodes with a similar role, and RETURNFACTOR below 1 favors going back to the previous node. WINDOWSIZE, ITERATIONS and the learning rates control the training.
When to use: Use it when you want to choose the kind of closeness — same circle (INOUTFACTOR < 1) or same structural role (INOUTFACTOR > 1) — and can afford slower processing than FASTRP. It does not cover nodes that arrive after the computation: for that, GRAPHSAGE; for categorical attributes together with structure, HASHGNN.
In business language:
- Which companies move in the same circle of partners, even without a direct tie between them?
- Which companies play the same role — bridge between groups, parent company, satellite — in different groups?
- Which partners move through the same business circles?
- How can the city's companies be grouped by how close they are along ownership paths?
- Which companies are just a few steps from each other in the ownership web?
How to use: Project relationships in both directions: the walk only follows outgoing relationships and stops at nodes with none. No option is mandatory; EMBEDDINGDIMENSION (default 128), WALKLENGTH (80), WALKSPERNODE (10), INOUTFACTOR and RETURNFACTOR (1.0) change the result the most, and RELATIONSHIPWEIGHT_PROPERTY makes the walk prefer heavier relationships. The output is nodeId (the NODES id) and embedding, a list of numbers of the requested size.
Example:
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 ;Caveats: It costs much more than FASTRP, because the work grows with walks per node times length times nodes: project a slice and start with smaller WALKLENGTH and WALKSPERNODE. Parallel training makes results vary between runs; to repeat them, set RANDOMSEED and use CONCURRENCY=1. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
WALK_LENGTH→walkLength(Integer; default80)WALKS_PER_NODE→walksPerNode(Integer; default10)IN_OUT_FACTOR→inOutFactor(Float; default1.0)RETURN_FACTOR→returnFactor(Float; default1.0)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)WINDOW_SIZE→windowSize(Integer; default10)NEGATIVE_SAMPLING_RATE→negativeSamplingRate(Integer; default5)POSITIVE_SAMPLING_FACTOR→positiveSamplingFactor(Float; default0.001)NEGATIVE_SAMPLING_EXPONENT→negativeSamplingExponent(Float; default0.75)EMBEDDING_DIMENSION→embeddingDimension(Integer; default128)EMBEDDING_INITIALIZER→embeddingInitializer(String; defaultNORMALIZED)ITERATIONS→iterations(Integer; default1)INITIAL_LEARNING_RATE→initialLearningRate(Float; default0.01)MIN_LEARNING_RATE→minLearningRate(Float; default0.0001)RANDOM_SEED→randomSeed(Integer; defaultrandom)WALK_BUFFER_SIZE→walkBufferSize(Integer; default1000)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, embedding
Graph: directed or undirected; accepts relationship weights.
GRAPHSAGE
Generates one embedding per node by applying a GraphSAGE model already trained with GRAPHSAGE_TRAIN, including to nodes the model has never seen.
What it does: It fetches from the GDS catalog the model named in MODEL "name" and, for each node of the projection, combines its attributes with those of a sample of neighbors using the functions learned in training. Because the model learned to aggregate attributes rather than memorize nodes, it works on another slice with the same attributes — another city, companies opened after the training. The result is one vector per node, with the size set in training.
When to use: Use it after a GRAPHSAGE_TRAIN, when you want embeddings that are comparable across slices or over time, produced by the same model. With no trained model, or no attributes that matter, FASTRP or NODE2VEC do the job without training.
In business language:
- How do the companies of the neighboring city fit the profile learned in the capital?
- Which known profiles do newly opened companies already resemble?
- Which companies in this new slice have a structure like the ones we analyzed before?
- Can companies from different cities be compared with the same yardstick?
How to use: Project the graph with the SAME attribute columns used in training, with the same names, and relationships in both directions. The model name goes in the syntax itself (MODEL "name") and is not repeated as an option; BATCHSIZE and CONCURRENCY only affect performance. The output is nodeId (the NODES id) and embedding, a list of numbers with the EMBEDDINGDIMENSION size set in training.
Example:
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 ;Caveats: The model exists only in GDS memory: it disappears when Neo4j restarts, because the installed Community edition does not store models on disk, and it must then be trained again with GRAPHSAGE_TRAIN. A missing attribute, or one with a different name in the projection, makes the run fail, and the graph needs at least one relationship. On the installed Community edition, CONCURRENCY above 4 is rejected.
Parameters:
BATCH_SIZE→batchSize(Integer; default100)MODEL_NAME→modelName(String; filled in by the syntax)CONCURRENCY→concurrency(Integer; default4)
Returned columns: nodeId, embedding
Graph: directed or undirected; accepts relationship weights.
GRAPHSAGE_TRAIN
Trains, and keeps in GDS memory, a GraphSAGE model that learns to combine each node's attributes with its neighbors', so embeddings can later be generated with GRAPHSAGE.
What it does: It learns aggregation functions (mean or pool) that, layer by layer, join the node's numeric FEATUREPROPERTIES attributes with those of a sample of neighbors (SAMPLESIZES, default 25 and 10) and produce a vector of EMBEDDINGDIMENSION positions. Training needs no labels: nodes that appear close together on short walks (SEARCHDEPTH) should end up with nearby vectors, and randomly drawn nodes with distant ones. The model goes into the GDS model catalog under the MODEL_NAME name, and the run returns modelInfo, configuration and trainMillis.
When to use: Use it when nodes have numeric attributes that matter and you want a reusable model: trained on one slice, it generates embeddings for new nodes or for another slice with the same attributes, such as training on one city and applying to another. If there are no attributes and no need for reuse, FASTRP or NODE2VEC are simpler and faster.
In business language:
- Can we learn the typical profile of companies in one city and use it to assess those in another?
- How do we build a single profile yardstick — capital, partners and ties — to compare companies that have yet to be opened?
- Can we prepare a comparison baseline for the capital's companies and reuse it in future analyses?
- How do we teach the platform to recognize companies with a similar structure from their registry data and ties?
How to use: Project nodes with the attributes as numeric columns (COUNT {...} AS grau, log10(...) AS capital) and relationships in both directions, with at least one relationship. MODELNAME (unique, no spaces) and FEATUREPROPERTIES (the columns separated by commas, in one quoted text) are mandatory; EMBEDDINGDIMENSION (default 64), EPOCHS, SAMPLESIZES and AGGREGATOR (mean or pool) tune the model. The output is one row with modelInfo (name and training metrics, a map shown as is), configuration and trainMillis; the embeddings come later, with GRAPHSAGE MODEL "name".
Example:
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 ;Caveats: On the installed Community edition the model lives only in memory: it disappears when Neo4j restarts, STOREMODELTO_DISK is rejected and at most 3 GraphSAGE models fit — since DATTAX does not delete models, the fourth training fails until the next restart. The catalog belongs to the technical user the engine uses to reach Neo4j and is shared by the whole platform, so reusing a name is rejected. Training costs much more than FASTRP: project a slice and start with few EPOCHS.
Parameters:
EMBEDDING_DIMENSION→embeddingDimension(Integer; default64)AGGREGATOR→aggregator(String; default"mean")ACTIVATION_FUNCTION→activationFunction(String; default"sigmoid")SAMPLE_SIZES→sampleSizes(List of Integer; default[25, 10])PROJECTED_FEATURE_DIMENSION→projectedFeatureDimension(Integer)BATCH_SIZE→batchSize(Integer; default100)TOLERANCE→tolerance(Float; default1e-4)LEARNING_RATE→learningRate(Float; default0.1)EPOCHS→epochs(Integer; default1)MAX_ITERATIONS→maxIterations(Integer; default10)BATCH_SAMPLING_RATIO→batchSamplingRatio(Float; defaultconcurrency * batchSize / nodeCount)SEARCH_DEPTH→searchDepth(Integer; default5)NEGATIVE_SAMPLE_WEIGHT→negativeSampleWeight(Integer; default20)RELATIONSHIP_WEIGHT_PROPERTY→relationshipWeightProperty(String)RANDOM_SEED→randomSeed(Integer; defaultrandom)PENALTY_L2→penaltyL2(Float; default0.0)STORE_MODEL_TO_DISK→storeModelToDisk(Boolean; defaultfalse)MODEL_NAME→modelName(String; required)FEATURE_PROPERTIES→featureProperties(List of String; required)CONCURRENCY→concurrency(Integer; default4)
Returned columns: modelInfo, configuration, trainMillis
Graph: directed or undirected; accepts relationship weights.
Link prediction
LINKPREDICTIONADAMIC_ADAR
Scores the chance of a link between two database entities by adding up their shared neighbors, giving more weight to neighbors with few connections.
What it does: It reads from the database, with no projection, the direct neighbors the two nodes share and adds up, for each one, 1 divided by the logarithm of that neighbor's number of connections. A shared neighbor that links few people — a partnership with two partners, an accountant with three clients — is worth more than a pass-through point that links everyone. RELATIONSHIP_QUERY and DIRECTION limit the type and direction of the relationships considered, including when counting each neighbor's connections.
When to use: Use it when the network has pass-through points that would inflate the plain count and what matters is the exclusive tie, a typical sign of a personal relationship or a joint operation. RESOURCEALLOCATION penalizes those points even harder (1/connections), and COMMONNEIGHBORS treats every neighbor the same.
In business language:
- Are partners A and B likely acting together, given that they share small partnerships?
- Is there a sign of a hidden link between these two people, giving more weight to rare ties?
- Do two companies using the same little-used accountant have a concealed connection?
- How strong is the relationship between A and B, discounting intermediaries that connect everyone?
- Do these two names cross paths in exclusive places or only at common pass-through points?
How to use: Build the pair with the FROM GRAPH MATCH, returning the elementIds (RETURN elementId(a) AS socio1, elementId(b) AS socio2), and pass both columns to BETWEEN; there is no PROJECT GRAPH and only the first row is used. RELATIONSHIP_QUERY and DIRECTION are optional. The output is one row with score: 0 with no shared neighbor, growing with no fixed ceiling as shared neighbors get more numerous and rarer.
Example:
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 ;Caveats: Keep DIRECTION at BOTH (the default): with OUTGOING or INCOMING, a neighbor with a single connection in the chosen direction causes a division by log(1) = 0 and the score becomes infinite — in the cnpj database, the Sociedade node has only one outgoing relationship. Only direct neighbors count: in the cnpj database, two companies almost never share a direct neighbor, and between two people the shared neighbor is the Sociedade they entered together or the same legal representative.
Parameters:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; defaultBOTH)
Returned columns: score
LINKPREDICTIONCOMMON_NEIGHBORS
Counts how many direct neighbors two database entities have in common: the more there are, the greater the chance of a link between them.
What it does: It reads straight from the database, with no projection, the immediate neighbors, one hop away, of each of the two nodes and returns the size of the intersection. RELATIONSHIPQUERY restricts the count to one relationship type, and DIRECTION chooses whether outgoing, incoming or both kinds of relationships count (the default). It is the simplest measure of local closeness; ADAMICADAR and RESOURCE_ALLOCATION start from the same set and weight each neighbor.
When to use: Use it for a direct reading that is easy to explain — the two share N ties — when every shared neighbor is worth the same. If some neighbors are pass-through points linking many people, such as an office serving thousands of companies, prefer ADAMICADAR or RESOURCEALLOCATION; to compare many pairs at once, use NODE_SIMILARITY on a projection.
In business language:
- In how many partnerships did partners A and B enter together?
- How many contacts in common do these two people have in the registry?
- Judging by the ties they share, how likely is it that A and B are connected?
- Do these two people often appear together in the same companies?
- Do the two partners have the same legal representative or joint entries into partnerships?
How to use: There is no PROJECT GRAPH: the FROM GRAPH MATCH returns both nodes by elementId (RETURN elementId(a) AS socio1, elementId(b) AS socio2) and BETWEEN takes those columns — only the first row is used. RELATIONSHIPQUERY (for instance "TEMSOCIEDADE_PESSOA") and DIRECTION (BOTH, OUTGOING or INCOMING) are optional. The output is one row with score, the count of shared neighbors, which is 0 when there are none.
Example:
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" ;Caveats: Only direct neighbors count: in the cnpj database, partner and company are linked through the Sociedade node, so two companies almost never share a direct neighbor — for companies with partners in common, use NODE_SIMILARITY on an Empresa→Pessoa projection. Between two people, the shared neighbor is the Sociedade (entry into the same company, on the same date and with the same role) or the same legal representative.
Parameters:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; defaultBOTH)
Returned columns: score
LINKPREDICTIONPREFERENTIAL_ATTACHMENT
Multiplies the number of connections of two database entities: highly connected pairs tend to link up, even with no neighbor in common.
What it does: It reads from the database, with no projection, how many relationships each node has — of the type and direction chosen in RELATIONSHIP_QUERY and DIRECTION — and returns the product of the two counts. It does not check for shared neighbors: it expresses the idea that whoever already has many ties attracts new ones, and that two big players tend to meet. It is useful as a baseline or as an ingredient next to the shared-neighbor metrics.
When to use: Use it to measure the link potential between very active entities — partners in dozens of companies, companies with large ownership structures — or as a control term that separates the effect of size from the effect of closeness. It does not tell whether A and B know each other; for that, COMMONNEIGHBORS, ADAMICADAR or RESOURCE_ALLOCATION.
In business language:
- Do two companies with large ownership structures tend to end up linked?
- What is the connection potential between two partners who take part in many companies?
- Is this pair of large groups big enough to already be interconnected?
- How much of the expected tie between A and B is explained just by how large each one's network is?
- Do the city's most active partners tend to meet in some partnership?
How to use: Build the pair as in the other functions: the FROM GRAPH MATCH returns the elementIds and BETWEEN takes the columns (only the first row counts; there is no PROJECT GRAPH). RELATIONSHIPQUERY defines what counts as a connection — with "ASSOCIADAA_EMPRESA", each company's number of ownership ties — and DIRECTION the direction (BOTH by default). The output is score, the product of the two counts: 0 if either has no relationship, and no ceiling.
Example:
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" ;Caveats: Without RELATIONSHIP_QUERY the count adds up every relationship type — in the cnpj database, branches, ownership ties, small-business tax regime and individual micro-entrepreneur records together —, mixing different things; choose the type that answers the question. The score grows with size, not with closeness: compare it with the shared-neighbor metrics before drawing conclusions.
Parameters:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; defaultBOTH)
Returned columns: score
LINKPREDICTIONRESOURCE_ALLOCATION
Scores the chance of a link between two database entities by adding up, for each shared neighbor, 1 divided by that neighbor's number of connections.
What it does: It reads from the database, with no projection, the direct neighbors the two nodes share and adds up 1/connections for each one: it is as if each neighbor split one unit among everyone it links, and the share reaching the pair is larger when it links few people. It penalizes pass-through points harder than ADAMICADAR, which divides by the logarithm. RELATIONSHIPQUERY and DIRECTION limit the type and direction of the relationships, including when counting connections.
When to use: Use it on large, sparse networks full of pass-through points (partnerships with dozens of partners, virtual-office addresses), when only the truly exclusive tie should weigh. For a milder penalty, ADAMICADAR; for the plain count, COMMONNEIGHBORS.
In business language:
- How likely is it that partners A and B are linked, disregarding partnerships with many members?
- Do these two companies share any exclusive point of contact that is not an office serving everyone?
- Is there a sign of a close relationship between these two people in the ties only they share?
- How much of what A and B have in common is truly particular to the two of them?
- Are these two people under investigation linked through restricted channels or only through mass intermediaries?
How to use: Build the pair with the FROM GRAPH MATCH, returning the elementIds, and pass both columns to BETWEEN; there is no PROJECT GRAPH and only the first row is used. RELATIONSHIPQUERY (for instance "TEMSOCIEDADE_PESSOA", to count only partnership entries) and DIRECTION are optional. The output is one row with score, which is 0 with no shared neighbor and adds up 1/connections for each shared neighbor.
Example:
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" ;Caveats: Keep DIRECTION at BOTH (the default): with OUTGOING or INCOMING, a neighbor with no connection in the chosen direction causes a division by zero and an infinite score. Only direct neighbors count: in the cnpj database, two companies almost never share a direct neighbor, and between two people the shared neighbor is the Sociedade they entered together or the same legal representative.
Parameters:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; defaultBOTH)
Returned columns: score
LINKPREDICTIONTOTAL_NEIGHBORS
Counts how many distinct direct neighbors two database entities add up to together: the size of their combined circle.
What it does: It reads from the database, with no projection, the direct neighbors of each node and returns the size of the union of the two sets, with each neighbor counted once. RELATIONSHIPQUERY and DIRECTION limit the type and direction of the relationships. On its own it shows the pair's combined reach; used to divide the COMMONNEIGHBORS of the same pair, it gives the share of the circle the two have in common.
When to use: Use it to normalize the shared-neighbor metrics — COMMONNEIGHBORS divided by TOTALNEIGHBORS gives the shared proportion, which does not favor whoever simply has many ties — or to measure the combined reach of two people under investigation. On its own, it does not indicate closeness.
In business language:
- How many distinct ownership ties do partners A and B add up to together?
- How large is the combined circle of these two people?
- What share of A's and B's relationships is shared between the two?
- If we merge the networks of these two companies, how many different contacts appear?
- What is the combined reach of these two people under investigation in the registry?
How to use: Build the pair with the FROM GRAPH MATCH, returning the elementIds, and pass both columns to BETWEEN; there is no PROJECT GRAPH and only the first row is used. RELATIONSHIPQUERY and DIRECTION are optional and should match those of COMMONNEIGHBORS when the two scores are combined. The output is one row with score, the number of distinct neighbors of the two together.
Example:
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" ;Caveats: The count does not discount what is shared and, on its own, favors whoever has many ties; for the shared proportion, divide the COMMON_NEIGHBORS of the same pair by it. Only direct neighbors count: in the cnpj database, a person's neighbors are the Sociedade nodes they take part in and their legal-representation ties.
Parameters:
RELATIONSHIP_QUERY→relationshipQuery(String)DIRECTION→direction(String; defaultBOTH)
Returned columns: score
LINKPREDICTIONSAME_COMMUNITY
Says whether two database entities have the same value in a grouping property: 1 if yes, 0 if not.
What it does: It reads straight from the database, with no projection, the property named in COMMUNITY_PROPERTY (default community) on both nodes and returns 1 when the values are equal and 0 when they differ or when either node lacks the property. It does not compute groups: it only compares a group label that is already stored — a previously computed community, a postal code, a legal nature. It serves as an ingredient to combine with the shared-neighbor metrics.
When to use: Use it when the database already holds a property that defines groups (community, segment, postal code, state) and belonging to the same group is a sign of a link; combine it with COMMONNEIGHBORS or ADAMICADAR for a stronger signal. To discover the groups, use LOUVAIN, LEIDEN or WCC on a projection.
In business language:
- Are the headquarters of these two companies at the same postal code?
- Do these two companies belong to the same registered segment?
- Are the two partners in the same group already identified by the team?
- Do the two companies have the same legal nature?
- Does this pair fall in the same region or in the same registered economic group?
How to use: Build the pair with the FROM GRAPH MATCH, returning the elementIds, and pass both columns to BETWEEN (only the first row counts; there is no PROJECT GRAPH). COMMUNITY_PROPERTY says which property to compare — the cnpj database has no community, so name one that exists, such as "enderecoCEP" on Endereco or "empresaNjcod" on Empresa. The output is score: 1.0 if equal, 0.0 if different or missing.
Example:
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" ;Caveats: DATTAX is read-only: a community computed by LOUVAIN or WCC on a projection is not written back to the database, and this function cannot see it. Without COMMUNITY_PROPERTY the function looks for community, which the cnpj database lacks, and always returns 0.
Parameters:
COMMUNITY_PROPERTY→communityProperty(String; defaultcommunity)
Returned columns: score
Projection
PROJECT_GRAPH
Builds in memory, from two Cypher queries, the slice of the network that graph algorithms run on.
What it does: It runs the NODES query, which returns one node per row, and the RELS query, which returns one link per row, and uses them to create an in-memory projection on the Neo4j server — every graph algorithm works on it, not on the database. Numeric columns beyond the required ones become properties: in NODES, node properties (coordinates, capital); in RELS, link properties (weight, cost). Under the hood DATTAX calls gds.graph.project.cypher, flagged as deprecated in GDS 2.27, but still the route used today.
When to use: Always, before any graph algorithm: every algorithm requires a projection made in the same run, after a FROM GRAPH that picks the database (without MATCH it reads no node). Project only the slice the question needs — a city, a group, one kind of tie — and, if the script compares slices, create projections with different names and pick one with ON or IN.
In business language:
- Build the network of Maceió companies linked by shared partners for me to analyse.
- I want to study only companies with capital above R$ 10 million and the partners they share.
- Set aside the shareholdings between companies, only corporate shareholders, in a slice of their own.
- Take into account how many partners each pair of companies shares when analysing the network.
- Analyse only the companies of one sector and the people linked to them.
How to use: NODES (GDS key nodeQuery) must return the id column with each node's numeric id(n) — elementId does not work —, and RELS (relationshipQuery) the source and target columns with the ids of both ends; extra numeric columns become properties, such as coalesce(e.empresaCsocial, 0.0) AS capital or count(DISTINCT p) AS socios, and those are the names you cite in options like RELATIONSHIPWEIGHTPROPERTY. The projection name is valid only within the run: in the GDS catalog it gets the run's suffix, so simultaneous runs never collide, repeating the name in the same run replaces the previous one, and everything is dropped at the end; an algorithm without ON (or IN) uses the latest projection. The step passes on the rows it received; what GDS reports (graphName, nodeQuery, relationshipQuery, nodeCount, relationshipCount, projectMillis) is for the engine, which warns PROJECAO_VAZIA when the projection is born without nodes or without links.
Example:
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 ;Caveats: Every source and target in RELS must be among the NODES ids, or the projection fails: filter both queries with the same criteria, and never cut only NODES with LIMIT. A repeated RELS row becomes a parallel link and a null weight becomes NaN — use DISTINCT or aggregate, and coalesce on the weight. Links come out directed and the path algorithms follow that direction: for a tie with no direction, return both directions.
Parameters:
NODE_QUERY→nodeQuery(String; filled in by the syntax)RELATIONSHIP_QUERY→relationshipQuery(String; filled in by the syntax)
Returned columns:
<!-- gerado:gds-catalogo:fim -->