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