Documentation

Graph Analysis & Fraud Detection

Graph Analysis & Use Cases

Velocity Counting (O(1))

JetGraph pre-computes velocity counts using ring buffers, making time-window queries instant. Query them via the Rust client or via Cypher system procedures:

rust — velocity query
// Count TRANSACTS_AT edges from this card in the last 1 hour let count = graph .get_velocity_count(VelocityQuery { node: card_id, edge_type: "TRANSACTS_AT".into(), window_secs: 3600, // 1 hour }) .await? .count; // 24-hour window let daily_count = graph .get_velocity_count(VelocityQuery { node: card_id, edge_type: "TRANSACTS_AT".into(), window_secs: 86400, }) .await? .count;

Fraud Detection Pattern

Graph databases are uniquely effective for fraud detection because fraud rings are defined by connections. A card that shares a device with a flagged card is suspicious — even if the card itself has no prior fraud history.

rust — fraud scoring (full example)
async fn score_transaction( graph: &GraphClient, tx: &Transaction, ) -> Result<Decision> { // Phase 1: collect signals let card_id = graph.lookup_node("CARD", &tx.card_id).await?; let merchant_id = graph.lookup_node("MERCHANT", &tx.merchant_id).await?; // Velocity: transactions in last 1h and 24h let txn_1h = graph.get_velocity_count(VelocityQuery { node: card_id, edge_type: "TRANSACTS_AT".into(), window_secs: 3600, }).await?.count; // Novelty: is this merchant new for this card? let is_new_merchant = !graph .edge_exists(card_id, merchant_id, "TRANSACTS_AT").await?; // Contagion: max fraud score from 1-hop neighbours let ctx = graph.get_fraud_context(FraudContextQuery { node: card_id, }).await?; // Phase 2: score let mut risk: f32 = 0.0; if is_new_merchant { risk += 0.15; } if txn_1h > 30 { risk += 0.25; } risk += 0.5 * ctx.max_neighbor_fraud_score; let decision = match risk { r if r > 0.7 => Decision::Decline, r if r > 0.4 => Decision::Challenge, _ => Decision::Approve, }; // Phase 3: insert edge (always, even on decline) graph.create_edge(CreateEdgeRequest { edge_type_name: "TRANSACTS_AT".into(), src: card_id, dst: merchant_id, properties: vec![prop("amount", tx.amount), prop("decision", &decision)], }).await?; // Propagate fraud score if declined if decision == Decision::Decline { graph.flag_node(FlagRequest { node: card_id, fraud_score: 0.85, reason: "auto_decline".into(), }).await?; } Ok(decision) }

Ring Fraud Detection (Cypher)

Find cards that share a device with a known-fraudulent card — the classic "fraud ring" pattern:

cypher
// Cards that share a device with card-001 (1-hop via DEVICE) MATCH (seed:CARD {external_id: "card-001"}) -[:USES_DEVICE]->(d:DEVICE) <-[:USES_DEVICE]-(suspect:CARD) WHERE suspect.external_id <> "card-001" RETURN suspect.external_id AS card, d.fingerprint AS shared_device

Recommendation System Pattern

Find merchants popular with other cards that share the same device as the current card — a graph-based collaborative filter:

cypher
MATCH (c:CARD {external_id: "card-001"}) -[:USES_DEVICE]->(d:DEVICE) <-[:USES_DEVICE]-(peer:CARD) -[:TRANSACTS_AT]->(m:MERCHANT) WHERE NOT (c)-[:TRANSACTS_AT]->(m) RETURN m.external_id AS recommended_merchant, COUNT(peer) AS peer_count ORDER BY peer_count DESC LIMIT 5