[Avg. reading time: 20 minutes]

Sample Transactions

Run them individual blocks, see what went wrong and then run them in bulk to see the difference.


// Create sample nodes
CREATE (rachel:Customer {name: 'Rachel'})
CREATE (ross:Customer {name: 'Ross'})
CREATE (monica:Customer {name: 'Monica'})
CREATE (chandler:Customer {name: 'Chandler'})
CREATE (joey:Customer {name: 'Joey'})
CREATE (phoebe:Customer {name: 'Phoebe'})
CREATE (card1:CreditCard {number: '1234567890123456'})
CREATE (card2:CreditCard {number: '9876543210987654'})
CREATE (card3:CreditCard {number: '2345678901234567'})
CREATE (card4:CreditCard {number: '7890123456789012'})
CREATE (card5:CreditCard {number: '3456789012345678'})
CREATE (card6:CreditCard {number: '6789012345678901'})
CREATE (macys:Merchant {name: 'Macys'})
CREATE (officeDepot:Merchant {name: 'Office Depot'})
CREATE (centralPerk:Merchant {name: 'Central Perk'})
CREATE (pizzaHut:Merchant {name: 'Pizza Hut'})
CREATE (bloomingdales:Merchant {name: 'Bloomingdales'})
// Create relationships
CREATE (rachel)-[:OWNS]->(card1)
CREATE (ross)-[:OWNS]->(card2)
CREATE (monica)-[:OWNS]->(card3)
CREATE (chandler)-[:OWNS]->(card4)
CREATE (joey)-[:OWNS]->(card5)
CREATE (phoebe)-[:OWNS]->(card6)
// Create sample transactions
CREATE (tx1:Transaction {amount: 100, timestamp: datetime()})
CREATE (tx2:Transaction {amount: 200, timestamp: datetime()})
CREATE (tx3:Transaction {amount: 50, timestamp: datetime()})
CREATE (tx4:Transaction {amount: 300, timestamp: datetime()})
CREATE (tx5:Transaction {amount: 75, timestamp: datetime()})
CREATE (tx6:Transaction {amount: 120, timestamp: datetime()})
CREATE (tx7:Transaction {amount: 500, timestamp: datetime()})
CREATE (tx8:Transaction {amount: 40, timestamp: datetime()})
CREATE (tx9:Transaction {amount: 250, timestamp: datetime()})
CREATE (tx10:Transaction {amount: 80, timestamp: datetime()})
CREATE (card1)-[:USED_IN]->(tx1)-[:MADE_AT]->(macys)
CREATE (card1)-[:USED_IN]->(tx2)-[:MADE_AT]->(officeDepot)
CREATE (card2)-[:USED_IN]->(tx3)-[:MADE_AT]->(macys)
CREATE (card2)-[:USED_IN]->(tx4)-[:MADE_AT]->(officeDepot)
CREATE (card3)-[:USED_IN]->(tx5)-[:MADE_AT]->(centralPerk)
CREATE (card3)-[:USED_IN]->(tx6)-[:MADE_AT]->(bloomingdales)
CREATE (card4)-[:USED_IN]->(tx7)-[:MADE_AT]->(macys)
CREATE (card5)-[:USED_IN]->(tx8)-[:MADE_AT]->(pizzaHut)
CREATE (card5)-[:USED_IN]->(tx9)-[:MADE_AT]->(macys)
CREATE (card6)-[:USED_IN]->(tx10)-[:MADE_AT]->(centralPerk)

match(t) return t

Delete All the Nodes

match (t) detach delete t;

CREATE (rachel:Customer {name: 'Rachel'})
CREATE (ross:Customer {name: 'Ross'})
CREATE (monica:Customer {name: 'Monica'})
CREATE (chandler:Customer {name: 'Chandler'})
CREATE (joey:Customer {name: 'Joey'})
CREATE (phoebe:Customer {name: 'Phoebe'})

CREATE (card1:CreditCard {number: '1234567890123456'})
CREATE (card2:CreditCard {number: '9876543210987654'})
CREATE (card3:CreditCard {number: '2345678901234567'})
CREATE (card4:CreditCard {number: '7890123456789012'})
CREATE (card5:CreditCard {number: '3456789012345678'})
CREATE (card6:CreditCard {number: '6789012345678901'})

CREATE (macys:Merchant {name: 'Macys', location: 'New York'})
CREATE (officeDepot:Merchant {name: 'Office Depot', location: 'New Jersey'})
CREATE (centralPerk:Merchant {name: 'Central Perk', location: 'New York'})
CREATE (pizzaHut:Merchant {name: 'Pizza Hut', location: 'Chicago'})
CREATE (bloomingdales:Merchant {name: 'Bloomingdales', location: 'Boston'})

CREATE (rachel)-[:OWNS]->(card1)
CREATE (ross)-[:OWNS]->(card2)
CREATE (monica)-[:OWNS]->(card3)
CREATE (chandler)-[:OWNS]->(card4)
CREATE (joey)-[:OWNS]->(card5)
CREATE (phoebe)-[:OWNS]->(card6)


CREATE (tx1:Transaction {amount: 100, timestamp: datetime("2025-04-08T08:00:00")})
CREATE (tx2:Transaction {amount: 200, timestamp: datetime("2025-04-08T08:03:00")})   
CREATE (tx3:Transaction {amount: 50, timestamp: datetime("2025-04-08T08:00:10")})    
CREATE (tx4:Transaction {amount: 300, timestamp: datetime("2025-04-08T08:05:00")})
CREATE (tx5:Transaction {amount: 75, timestamp: datetime("2025-04-08T09:00:00")})
CREATE (tx6:Transaction {amount: 120, timestamp: datetime("2025-04-08T09:10:00")})
CREATE (tx7:Transaction {amount: 500, timestamp: datetime("2025-04-08T10:00:00")})   
CREATE (tx8:Transaction {amount: 40, timestamp: datetime("2025-04-08T10:10:00")})
CREATE (tx9:Transaction {amount: 250, timestamp: datetime("2025-04-08T10:12:00")})   
CREATE (tx10:Transaction {amount: 80, timestamp: datetime("2025-04-08T11:00:00")})

CREATE (card1)-[:USED_IN]->(tx1)-[:MADE_AT]->(macys)
CREATE (card1)-[:USED_IN]->(tx2)-[:MADE_AT]->(officeDepot)
CREATE (card2)-[:USED_IN]->(tx3)-[:MADE_AT]->(macys)
CREATE (card2)-[:USED_IN]->(tx4)-[:MADE_AT]->(officeDepot)
CREATE (card3)-[:USED_IN]->(tx5)-[:MADE_AT]->(centralPerk)
CREATE (card3)-[:USED_IN]->(tx6)-[:MADE_AT]->(bloomingdales)
CREATE (card4)-[:USED_IN]->(tx7)-[:MADE_AT]->(macys)
CREATE (card5)-[:USED_IN]->(tx8)-[:MADE_AT]->(pizzaHut)
CREATE (card5)-[:USED_IN]->(tx9)-[:MADE_AT]->(macys)
CREATE (card6)-[:USED_IN]->(tx10)-[:MADE_AT]->(centralPerk)

Find all customers

MATCH (c:Customer)
RETURN c.name

Find all transactions made at Macys

MATCH (tx:Transaction)-[:MADE_AT]->(m:Merchant)
WHERE m.name = 'Macys'
RETURN tx.amount, tx.timestamp

Find all credit cards owned by Ross

MATCH (ross:Customer {name: 'Ross'})-[:OWNS]->(card:CreditCard)
RETURN card.number

Find the customer who made a specific transaction for 120

MATCH (c:Customer)-[:OWNS]->(card:CreditCard)-[:USED_IN]->(tx:Transaction)
WHERE tx.amount = 120 
RETURN c.name

Find the merchants where Monica made transactions

MATCH (monica:Customer {name: 'Monica'})-[:OWNS]->(card:CreditCard)-[:USED_IN]->(tx:Transaction)-[:MADE_AT]->(m:Merchant)
RETURN DISTINCT m.name

Find the total amount spent by Chandler at Macys

MATCH (chandler:Customer {name: 'Chandler'})-[:OWNS]->(card:CreditCard)-[:USED_IN]->(tx:Transaction)-[:MADE_AT]->(macys:Merchant {name: 'Macys'})
RETURN SUM(tx.amount) AS total_spent

Fraud Transactions

  • High-value transactions
MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(tx:Transaction)
WHERE tx.amount > 400
RETURN c.name AS customer, cc.number AS card, tx.amount AS amount, tx.timestamp AS time, 'FRAUD' AS flagged_reason

Logical View

apoc - Awesome procedures on Cypher

MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(tx:Transaction)
WHERE tx.amount > 400
CALL apoc.create.vRelationship(c, "FRAUD", {}, tx) YIELD rel
RETURN c, rel, tx

Making an Update

MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(tx:Transaction)
WHERE tx.amount > 400
MERGE (c)-[:FRAUD]->(tx)
RETURN c,tx
  • Quick back-to-back use of same card at different merchants
MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(t1:Transaction)-[:MADE_AT]->(m1:Merchant),
      (cc)-[:USED_IN]->(t2:Transaction)-[:MADE_AT]->(m2:Merchant)
WHERE m1.name <> m2.name 
  AND abs(datetime(t1.timestamp).epochSeconds - datetime(t2.timestamp).epochSeconds) < 300
  AND t1 <> t2
RETURN 
  c.name AS Customer,
  cc.number AS CardNumber,
  t1.timestamp AS FirstTxTime,
  m1.name AS FirstMerchant,
  t2.timestamp AS SecondTxTime,
  m2.name AS SecondMerchant,
  'Geo-impossible usage (too fast)' AS FraudReason
MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(t1:Transaction)-[:MADE_AT]->(m1:Merchant),
      (cc)-[:USED_IN]->(t2:Transaction)-[:MADE_AT]->(m2:Merchant)
WHERE m1.name <> m2.name 
  AND abs(datetime(t1.timestamp).epochSeconds - datetime(t2.timestamp).epochSeconds) < 300
  AND t1 <> t2
CALL apoc.create.vRelationship(c, "FRAUD", {reason: "Geo-impossible usage"}, t2) YIELD rel
RETURN c, rel, t1, m1, t2, m2

MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(t1:Transaction)-[:MADE_AT]->(m1:Merchant),
      (cc)-[:USED_IN]->(t2:Transaction)-[:MADE_AT]->(m2:Merchant)
WHERE m1.name <> m2.name 
  AND abs(datetime(t1.timestamp).epochSeconds - datetime(t2.timestamp).epochSeconds) < 300
MERGE (c)-[:FRAUD]->(t2)
RETURN c,t1,m1,t2,m2
  • List All fraud transactions
MATCH (c:Customer)-[:FRAUD]->(t:Transaction)
RETURN c.name AS Fraudster, t.amount, t.timestamp
  • List all Genuine Transactions
MATCH (c:Customer)-[:OWNS]->(cc:CreditCard)-[:USED_IN]->(tx:Transaction)
WHERE NOT (c)-[:FRAUD]->(tx)
RETURN c.name AS Customer, cc.number AS CardNumber, tx.amount AS Amount, tx.timestamp AS Time
ORDER BY tx.timestamp

Better Use Case

Bank Fraud Detection

#transaction #neo4jVer 5.5.3

Last change: 2025-10-15