交易欺诈圈 - 关系版本
1. 建模
本节将展示示例图上的 Cypher 查询示例。目的是说明查询的外观,并提供在实际设置中如何构造数据的指南。我们将在一个小型交易网络图上进行此操作,该图包含以环形结构连接的多个节点。
示例图将基于以下数据模型
1.2. 演示数据
以下 Cypher 语句将在 Neo4j 数据库中创建示例图
// Create all accounts
CREATE (a1:Account {accountNumber: 1})
CREATE (a2:Account {accountNumber: 2})
CREATE (a3:Account {accountNumber: 3})
CREATE (a4:Account {accountNumber: 4})
// Create relationships between accounts
CREATE (a1)-[:TRANSACTION {amount: 1000, currency: "gbp", date: datetime()-duration({days: 3})}]->(a2)
CREATE (a2)-[:TRANSACTION {amount: 900, currency: "gbp", date: datetime()-duration({days: 2})}]->(a3)
CREATE (a3)-[:TRANSACTION {amount: 810, currency: "gbp", date: datetime()-duration({days: 1})}]->(a4)
CREATE (a4)-[:TRANSACTION {amount: 729, currency: "gbp", date: datetime()}]->(a1)
2. Cypher 查询
2.1. 简单交易环
在此查询中,我们将识别一个符合以下要求的环
-
Account
节点应通过TRANSACTION
关系连接。 -
确保遵循交易方向(不是双向查询)。
-
查找交易长度大于 3 且小于 7 的环。
// Identify simple transaction ring
MATCH path=(a:Account)-[:TRANSACTION*3..6]->(a)
RETURN path
2.2. 不含重复账户的交易环
在此查询中,我们将识别一个符合以下要求的环
-
Account
节点应通过TRANSACTION
关系连接。 -
确保遵循交易方向(不是双向查询)。
-
查找交易长度大于 3 且小于 7 的环。
-
确保环由唯一账户组成。
// Identify transaction ring with no duplicate accounts
MATCH path=(a:Account)-[:TRANSACTION*3..6]->(a)
// Here we ensure that one path has unique people involved in the chain
WHERE size(apoc.coll.toSet(nodes(path))) = size(nodes(path)) - 1
// Return all paths
RETURN path
2.3. 按时间顺序排列交易的交易环
在此查询中,我们将识别一个符合以下要求的环
-
Account
节点应通过TRANSACTION
关系连接。 -
确保遵循交易方向(不是双向查询)。
-
查找交易长度大于 3 且小于 7 的环。
-
确保环由唯一账户组成
-
确保
TRANSACTION
关系按时间顺序排列
// Identify transaction ring where dates are in chronological order
MATCH path=(a:Account)-[rel:TRANSACTION*3..6]->(a)
// Here we ensure that one path has unique people involved in the chain
WHERE size(apoc.coll.toSet(nodes(path))) = size(nodes(path)) - 1
// Relationship validation
AND ALL(idx in range(0, size(rel)-2)
// Ensures the dates are in chronological order
WHERE (rel[idx]).date < (rel[idx+1]).date
)
// Return all paths
RETURN path
2.4. 扣除 20% 金额的交易环
当资金通过欺诈圈时,账户之间转移的金额通常会因高达 20% 的手续费而减少。考虑到这一点,我们的查询将允许每次交易减少高达 20%。
在此查询中,我们将识别一个符合以下要求的环
-
Account
节点应通过TRANSACTION
关系连接。 -
确保遵循交易方向(不是双向查询)。
-
查找交易长度大于 3 且小于 7 的环。
-
确保环由唯一账户组成
-
确保
TRANSACTION
关系按时间顺序排列 -
检查
TRANSACTION
金额是否在前一笔交易的 20% 范围内。
// Identify transaction ring where amounts are within 20% of each other
MATCH path=(a:Account)-[rel:TRANSACTION*3..6]->(a)
// Here we ensure that one path has unique people involved in the chain
WHERE size(apoc.coll.toSet(nodes(path))) = size(nodes(path)) - 1
// Relationship validation
AND ALL(idx in range(0, size(rel)-2)
// Ensures the dates are in chronological order
WHERE (rel[idx]).date < (rel[idx+1]).date
// Checks that there is less than a 20% difference from the last `TRANSACTION` amount to the next
AND (rel[idx+1].amount / rel[idx].amount) * 100 <= 20
)
// Return all paths
RETURN path
2.4.1. 此查询的作用是什么?
给定的 Cypher 查询旨在识别图数据库中账户通过交易连接的可疑交易环。该查询查找符合特定条件的交易循环,然后返回这些循环。让我们逐步分解查询。
1 - 查找循环路径
MATCH path=(a:Account)-[rel:TRANSACTION*3..6]→(a)
此行启动 MATCH 子句,查找账户 (a:Account)
通过 3 到 6 个 TRANSACTION
关系 (rel:TRANSACTION*3..6)
连接到自身的路径。这些路径形成循环,代表一个交易“环”。
2 - 确保账户唯一
WHERE size(apoc.coll.toSet(nodes(path))) = size(nodes(path)) - 1
此函数将路径中的节点列表转换为集合,从而有效地删除任何重复项。
size(nodes(path)) - 1
这计算了路径中节点列表的大小,减去 1 以考虑循环中起始节点和结束节点相同的情况。
WHERE 子句确保循环中的所有账户都是唯一的。
3 - 关系验证
AND ALL(idx in range(0, size(rel)-2) WHERE (rel[idx]).date < (rel[idx+1]).date AND (rel[idx+1].amount / rel[idx].amount) * 100 ⇐ 20 )
ALL(idx in range(0, size(rel)-2))
这使用从 0 到 size(rel) - 2
的索引迭代路径中的每个关系。
(rel[idx]).date < (rel[idx+1]).date
检查交易日期是否按时间顺序排列。
(rel[idx+1].amount / rel[idx].amount) * 100 ⇐ 20
检查每笔后续交易的金额是否在前一笔交易金额的 20% 范围内。
4 - 返回路径
RETURN path
此行返回满足上述所有条件的路径。
摘要:该查询识别由 3 到 6 笔唯一账户间交易组成的交易环。它通过确保交易金额变化不超过 20% 且交易按时间顺序排列来进一步验证这些环。