Walk through a real insurance fraud investigation. Run live queries against a graph database that remembers every claim, every relationship, and every change — at every point in time.
It's October 2025. You're an insurance fraud investigator at Meridian Shield. Claims at one of your body shops — "Acme Auto Body" — have been escalating for months. You suspect organized fraud, but you need proof. The dataset spans 9 months: 67 claimants, 47 claims, 7 doctors, 3 lawyers, and 3 body shops.
Your task: Reconstruct the timeline. Identify the ring. Quantify the loss.
Use point-in-time queries to see how Acme Auto Body looked at three different moments.
Query the body shop at February 15, 2025 (just 30 days after they joined the network).
MATCH (s:BodyShop {id: 'shop:acme-auto-body'}) RETURN s.name AS name, s.status AS status, s.rating AS rating, s.notes AS notes // AT '2025-02-15'
AT parameter resolves the entire query at a historical moment — no audit tables required.Now query the same node at May 1, 2025. Notice the change.
MATCH (s:BodyShop {id: 'shop:acme-auto-body'}) RETURN s.name AS name, s.status AS status, s.rating AS rating, s.notes AS notes // AT '2025-05-01'
Query the current state. See what investigators uncovered.
MATCH (s:BodyShop {id: 'shop:acme-auto-body'}) RETURN s.name AS name, s.status AS status, s.rating AS rating, s.notes AS notes
Trace every change to a node from creation to today — built-in.
One procedure call returns every version, with timestamps and properties.
MATCH (s:BodyShop {id: 'shop:acme-auto-body'}) CALL db.history(id(s)) YIELD version, validFrom, properties RETURN DISTINCT version, validFrom, properties.status AS status, properties.rating AS rating ORDER BY version
_valid_from and _valid_to timestamps. The db.history() procedure walks the entire chain.Find every change that happened between February and August.
Run a snapshot diff between Feb 15 and Aug 15. See exactly what flipped from "Active" to "Under Investigation".
CALL db.diff('2025-02-15', '2025-08-15') YIELD entityId, changeType, label, property, before, after WHERE property = 'status' AND before <> after RETURN label, before, after LIMIT 20
Use graph traversal to find structural fraud signals.
In a normal claim population, no two policyholders share the same exact address. Here, four addresses each have 8–9 claimants. That's the fraud ring.
MATCH (p:Person)-[:FILED]->(c:Claim)-[:REPAIRED_AT]->(s:BodyShop {name: 'Acme Auto Body'}) RETURN p.address AS address, count(DISTINCT p) AS claimants ORDER BY claimants DESC LIMIT 10
In normal operations, claimants pick their own lawyers. Here, one attorney represents nearly every claim.
MATCH (c:Claim)-[:REPAIRED_AT]->(:BodyShop {name: 'Acme Auto Body'}) MATCH (c)-[:REPRESENTED_BY]->(l:Lawyer) RETURN l.name AS lawyer, l.firm AS firm, count(c) AS claims_handled
How much did Meridian Shield lose before catching the ring?
Aggregate every claim that flowed through Acme.
MATCH (c:Claim)-[:REPAIRED_AT]->(:BodyShop {id: 'shop:acme-auto-body'}) RETURN count(c) AS total_claims, sum(c.amount) AS total_exposure, avg(c.amount) AS avg_claim_amount
Every query you just ran is standard Cypher. Every temporal feature is built-in. No audit tables, no ETL pipelines, no custom storage layer.