Skip to main content

Overview

This guide shows production query patterns used in BR-ACC to detect risk signals, explore networks, and analyze relationships.

Network Exploration

Get Entity Connections

Retrieve all relationships up to a specified depth from a central entity:
MATCH (center)
WHERE elementId(center) = $entity_id
  AND (center:Person OR center:Partner OR center:Company OR center:Contract 
       OR center:Sanction OR center:Election OR center:Amendment 
       OR center:Finance OR center:Embargo OR center:Health 
       OR center:Education OR center:Convenio OR center:LaborStats 
       OR center:PublicOffice)
OPTIONAL MATCH p=(center)-[:SOCIO_DE|DOOU|CANDIDATO_EM|VENCEU|
                                AUTOR_EMENDA|SANCIONADA|OPERA_UNIDADE|
                                DEVE|RECEBEU_EMPRESTIMO|EMBARGADA|
                                MANTEDORA_DE|BENEFICIOU|GEROU_CONVENIO|
                                SAME_AS|POSSIBLE_SAME_AS*1..4]-(connected)
WHERE length(p) <= $depth
  AND all(x IN nodes(p) WHERE NOT (x:User OR x:Investigation OR x:Annotation OR x:Tag))
WITH center, p
UNWIND CASE WHEN p IS NULL THEN [] ELSE relationships(p) END AS r
WITH DISTINCT center, r, startNode(r) AS src, endNode(r) AS tgt
WHERE coalesce($include_probable, false) OR type(r) <> "POSSIBLE_SAME_AS"
RETURN center AS e,
       r,
       CASE WHEN elementId(src) = elementId(center) THEN tgt ELSE src END AS connected,
       labels(center) AS source_labels,
       CASE WHEN elementId(src) = elementId(center) THEN labels(tgt) ELSE labels(src) END AS target_labels,
       type(r) AS rel_type,
       elementId(startNode(r)) AS source_id,
       elementId(endNode(r)) AS target_id,
       elementId(r) AS rel_id
From entity_connections.cypher - Used by the graph visualization API

Expand Graph from Entity

Get full subgraph including all nodes and relationships:
MATCH (center)
WHERE elementId(center) = $entity_id
  AND (center:Person OR center:Company OR center:Contract OR center:Sanction 
       OR center:Election OR center:Amendment OR center:Finance 
       OR center:Embargo OR center:Health OR center:Education 
       OR center:Convenio OR center:LaborStats OR center:PublicOffice)
OPTIONAL MATCH p=(center)-[:SOCIO_DE|DOOU|CANDIDATO_EM|VENCEU|
                                AUTOR_EMENDA|SANCIONADA|OPERA_UNIDADE|
                                DEVE|RECEBEU_EMPRESTIMO|EMBARGADA|
                                MANTEDORA_DE|BENEFICIOU|GEROU_CONVENIO|
                                SAME_AS|POSSIBLY_SAME_AS*1..4]-(n)
WHERE length(p) <= $depth
  AND all(x IN nodes(p) WHERE NOT (x:User OR x:Investigation OR x:Annotation OR x:Tag))
WITH center, collect(p) AS paths
WITH center,
     reduce(ns = [center], p IN paths | ns + CASE WHEN p IS NULL THEN [] ELSE nodes(p) END) AS raw_nodes,
     reduce(rs = [], p IN paths | rs + CASE WHEN p IS NULL THEN [] ELSE relationships(p) END) AS raw_rels
UNWIND raw_nodes AS n
WITH center, collect(DISTINCT n) AS nodes, raw_rels
UNWIND CASE WHEN size(raw_rels) = 0 THEN [NULL] ELSE raw_rels END AS r
WITH center, nodes, collect(DISTINCT r) AS rels
RETURN nodes,
       [x IN rels WHERE x IS NOT NULL] AS relationships,
       elementId(center) AS center_id
From graph_expand.cypher - Returns complete subgraph for visualization

Relationship Types

BR-ACC uses these relationship types to connect entities:
  • SOCIO_DE - Partner/shareholder of company
  • VENCEU - Won contract
  • GEROU_CONVENIO - Generated agreement
  • SANCIONADA - Company sanctioned
  • EMBARGADA - Company embargoed
  • DEVE - Owes debt
  • RECEBEU_EMPRESTIMO - Received loan
  • DOOU - Made political donation
  • CANDIDATO_EM - Candidate in election
  • AUTOR_EMENDA - Author of budget amendment
  • BENEFICIOU - Benefited from amendment
  • SAME_AS - Confirmed same entity
  • POSSIBLE_SAME_AS - Probable match
  • OPERA_UNIDADE - Operates facility
  • MANTEDORA_DE - Maintains institution

Risk Pattern Queries

Sanctioned Company Still Receiving Contracts

Detect companies that won contracts during active sanction periods:
MATCH (c:Company)
WHERE elementId(c) = $company_id
   OR c.cnpj = $company_identifier
CALL {
  WITH c
  MATCH (c)-[:SANCIONADA]->(s:Sanction)
  WHERE s.date_start IS NOT NULL
    AND trim(s.date_start) <> ''
  RETURN collect(DISTINCT {
    sanction_id: s.sanction_id,
    date_start: s.date_start,
    date_end: s.date_end
  }) AS sanctions
}
WITH c, sanctions
WHERE size(sanctions) > 0
MATCH (c)-[:VENCEU]->(ct:Contract)
WHERE ct.date IS NOT NULL
  AND trim(ct.date) <> ''
  AND any(s IN sanctions WHERE
    ct.date >= s.date_start
    AND (s.date_end IS NULL OR ct.date <= s.date_end)
  )
WITH c,
     [s IN sanctions | s.sanction_id] AS sanction_ids,
     collect(DISTINCT ct.contract_id) AS contract_ids,
     sum(coalesce(ct.value, 0.0)) AS amount_total,
     min(ct.date) AS window_start,
     max(ct.date) AS window_end
WITH c,
     sanction_ids,
     [x IN contract_ids WHERE x IS NOT NULL] AS contract_ids,
     amount_total,
     window_start,
     window_end,
     [x IN sanction_ids + contract_ids WHERE x IS NOT NULL] AS evidence_refs
WHERE size(sanction_ids) > 0
  AND size(contract_ids) > 0
RETURN 'sanctioned_still_receiving' AS pattern_id,
       c.cnpj AS cnpj,
       c.razao_social AS company_name,
       toFloat(size(sanction_ids) + size(contract_ids)) AS risk_signal,
       amount_total AS amount_total,
       window_start AS window_start,
       window_end AS window_end,
       evidence_refs AS evidence_refs,
       size(evidence_refs) AS evidence_count
From public_pattern_sanctioned_still_receiving.cypher

Contract Concentration

Find companies with excessive market share in specific government organizations:
MATCH (c:Company)
WHERE elementId(c) = $company_id
MATCH (c)-[:VENCEU]->(ct:Contract)
WHERE ct.contracting_org IS NOT NULL
  AND ct.value IS NOT NULL
WITH c, ct.contracting_org AS org, 
     sum(coalesce(ct.value, 0.0)) AS company_org_total
CALL {
  WITH org
  MATCH (:Company)-[:VENCEU]->(org_ct:Contract)
  WHERE org_ct.contracting_org = org
    AND org_ct.value IS NOT NULL
  RETURN sum(coalesce(org_ct.value, 0.0)) AS org_total
}
WITH c, org, company_org_total, org_total
WHERE org_total > 0
  AND (company_org_total / org_total) >= toFloat($pattern_share_threshold)
WITH c, collect(DISTINCT org) AS risky_orgs
WHERE size(risky_orgs) > 0
MATCH (c)-[:VENCEU]->(risk_ct:Contract)
WHERE risk_ct.contracting_org IN risky_orgs
RETURN 'contract_concentration' AS pattern_id,
       c.cnpj AS cnpj,
       c.razao_social AS company_name,
       toFloat(size(risky_orgs)) AS risk_signal,
       sum(coalesce(risk_ct.value, 0.0)) AS amount_total
From public_pattern_contract_concentration.cypher - Threshold typically set at 0.5 (50%)

Embargoed Company Receiving Funds

Detect companies with environmental embargoes still receiving contracts or loans:
MATCH (c:Company)
WHERE elementId(c) = $company_id
CALL {
  WITH c
  MATCH (c)-[:EMBARGADA]->(emb:Embargo)
  WHERE emb.date IS NOT NULL
  RETURN collect(DISTINCT emb.embargo_id) AS embargo_ids,
         min(emb.date) AS embargo_start
}
WITH c, embargo_ids, embargo_start
WHERE size(embargo_ids) > 0
CALL {
  WITH c, embargo_start
  OPTIONAL MATCH (c)-[:VENCEU]->(ct:Contract)
  WHERE ct.date >= embargo_start
  RETURN collect(DISTINCT ct.contract_id) AS contract_ids,
         sum(coalesce(ct.value, 0.0)) AS contract_total
}
CALL {
  WITH c, embargo_start
  OPTIONAL MATCH (c)-[:RECEBEU_EMPRESTIMO]->(loan:Finance)
  WHERE loan.date >= embargo_start
  RETURN collect(DISTINCT loan.finance_id) AS loan_ids,
         sum(coalesce(loan.value, 0.0)) AS loan_total
}
RETURN 'embargoed_receiving' AS pattern_id,
       c.cnpj AS cnpj,
       c.razao_social AS company_name,
       toFloat(size(embargo_ids) + size(contract_ids) + size(loan_ids)) AS risk_signal,
       coalesce(contract_total, 0.0) + coalesce(loan_total, 0.0) AS amount_total
From public_pattern_embargoed_receiving.cypher

Entity Timeline

Get chronological events connected to an entity:
MATCH (e)
WHERE elementId(e) = $entity_id
WITH e
OPTIONAL MATCH (e)-[:SAME_AS*1..2]-(other)
WITH e, collect(DISTINCT other) AS others
WITH [e] + others AS equivs
UNWIND equivs AS eq
MATCH (eq)-[r]-(n)
WHERE type(r) <> 'SAME_AS'
  AND (n:Contract OR n:Sanction OR n:Amendment OR n:Election 
       OR n:Finance OR n:Embargo OR n:Convenio)
WITH DISTINCT n, labels(n) AS lbls,
     COALESCE(n.date, n.date_start, n.date_published, 
              toString(n.year)) AS event_date
WHERE event_date IS NOT NULL AND event_date <> ''
  AND ($cursor IS NULL OR event_date < $cursor)
RETURN elementId(n) AS id, event_date, lbls, properties(n) AS props
ORDER BY event_date DESC
LIMIT $limit
From entity_timeline.cypher - Supports cursor-based pagination and includes SAME_AS equivalents

Aggregation Patterns

Entity Score Calculation

Calculate exposure metrics aggregated across equivalent entities:
MATCH (e)
WHERE elementId(e) = $entity_id
WITH e, labels(e) AS lbls
// Collect equivalent nodes via SAME_AS
OPTIONAL MATCH (e)-[:SAME_AS*1..2]-(other)
WITH e, lbls, collect(DISTINCT other) AS others
WITH e, lbls, [e] + others AS equivs
// Count connections
UNWIND equivs AS eq
OPTIONAL MATCH (eq)-[r]-(connected) WHERE type(r) <> 'SAME_AS'
WITH e, lbls, equivs, count(r) AS connection_count
// Contract volume
UNWIND equivs AS eq2
OPTIONAL MATCH (eq2)-[:VENCEU]->(c:Contract)
WITH e, lbls, connection_count, 
     COALESCE(sum(c.value), 0) AS contract_volume
// Donation volume
UNWIND equivs AS eq3
OPTIONAL MATCH (eq3)-[:DOOU]->(d)
WITH e, lbls, connection_count, contract_volume,
     COALESCE(sum(d.valor), 0) AS donation_volume
// Debt/loan volume
UNWIND equivs AS eq4
OPTIONAL MATCH (eq4)-[:RECEBEU_EMPRESTIMO|DEVE]->(f:Finance)
RETURN
  elementId(e) AS entity_id,
  lbls AS entity_labels,
  connection_count,
  contract_volume + donation_volume + COALESCE(sum(f.value), 0) AS financial_volume
From entity_score.cypher - Aggregates metrics across SAME_AS linked entities

Next Steps

Performance Tips

Learn how to optimize these queries

Cypher Basics

Review fundamental query syntax

Build docs developers (and LLMs) love