Neo4j Cypher 查询语言权威指南
本指南基于 Neo4j 官方文档编写,结合本项目中的银行反欺诈场景,提供从入门到进阶的 Cypher 语言教程。Cypher 是 Neo4j 的声明式图查询语言,设计理念类似 SQL,但专门针对图数据的模式匹配进行了优化。
1. 核心概念 (Core Concepts)
在开始编写查询之前,回顾一下图数据库的四个基本构建块:
- 节点 (Nodes): 图中的实体。例如:
(:Customer),(:Account)。 - 关系 (Relationships): 连接两个节点,必须有方向和类型。例如:
-[:OWNS]->,-[:TRANSFER]->。 - 属性 (Properties): 存储在节点或关系上的键值对数据。例如:
{name: "张三", amount: 1000}。 - 标签 (Labels): 用于对节点进行分组和分类。例如:
:VIP,:Suspect。
2. 基础查询 (Reading Data)
2.1 模式匹配 (MATCH)
MATCH 是 Cypher 中最核心的子句,用于描述你想要在图中找到的结构(模式)。
// 1. 查询所有 Customer 节点
MATCH (c:Customer)
RETURN c.name, c.cust_id
// 2. 查询特定属性的节点
MATCH (a:Account {account_id: "A001"})
RETURN a
// 3. 查询特定关系(客户拥有的账户)
// 模式:(客户)-[拥有]->(账户)
MATCH (c:Customer)-[:OWNS]->(a:Account)
RETURN c.name, a.account_id
2.2 过滤条件 (WHERE)
虽然可以在 MATCH 中直接指定属性,但 WHERE 子句提供了更强大的过滤能力(布尔逻辑、范围查询、字符串匹配等)。
MATCH (t:Transfer)
WHERE t.amount > 10000
AND t.currency = "CNY"
AND (t.status = "Failed" OR t.channel = "Mobile")
RETURN t
常用操作符:
- 比较:
=,<>,<,>,<=,>= - 逻辑:
AND,OR,NOT,XOR - 字符串:
STARTS WITH,ENDS WITH,CONTAINS - 空值检查:
IS NULL,IS NOT NULL - 存在性检查:
EXISTS { MATCH ... }(高级用法)
// 查找名字中包含 "王" 且有手机号的客户
MATCH (c:Customer)
WHERE c.name CONTAINS "王" AND c.phone IS NOT NULL
RETURN c
2.3 可选匹配 (OPTIONAL MATCH)
类似于 SQL 中的 LEFT JOIN。如果模式匹配失败,OPTIONAL MATCH 会返回 null 而不是丢弃当前行。
// 查询所有客户,以及他们可能拥有的 "高风险" 标签(如果有的话)
MATCH (c:Customer)
OPTIONAL MATCH (c)-[:HAS_TAG]->(t:RiskTag)
RETURN c.name, t.tag_name
// 结果:如果客户没有风险标签,t.tag_name 将为 null
2.4 排序与分页 (ORDER BY, SKIP, LIMIT)
MATCH (a:Account)
RETURN a.account_id, a.balance
ORDER BY a.balance DESC // 按余额降序
SKIP 10 // 跳过前 10 条
LIMIT 5 // 取接下来的 5 条
2.5 别名与去重 (AS, DISTINCT)
MATCH (c:Customer)-[:OWNS]->(a:Account)
RETURN DISTINCT c.name AS CustomerName, count(a) AS AccountCount
3. 数据写入与更新 (Writing Data)
3.1 创建数据 (CREATE)
CREATE 用于无条件创建新的节点和关系。
// 创建单节点
CREATE (:Customer {name: "李四", cust_id: "C002"})
// 创建带关系的路径
CREATE (c:Customer {name: "王五"})-[:OWNS]->(a:Account {id: "A100", balance: 0})
3.2 智能合并 (MERGE)
MERGE 是 “Match or Create” 的操作。它首先尝试在图中匹配模式,如果存在则重用,不存在则创建。这是保证数据幂等性的关键。
// 确保账户存在
MERGE (a:Account {account_id: "A999"})
ON CREATE SET a.created_at = datetime(), a.balance = 0
ON MATCH SET a.last_accessed = datetime()
实战技巧:在导入 CSV 数据时,务必使用 MERGE 而非 CREATE,以避免重复数据。
3.3 更新属性与标签 (SET)
MATCH (c:Customer {name: "张三"})
// 更新属性
SET c.age = 35, c.risk_level = "Medium"
// 添加标签
SET c:VIP:HighNetWorth
3.4 删除数据 (DELETE / REMOVE)
DELETE: 删除节点或关系。REMOVE: 删除属性或标签。DETACH DELETE: 删除节点及其所有关联关系(强制删除)。
// 删除关系
MATCH (c:Customer)-[r:OWNS]->(a:Account)
WHERE c.name = "已注销用户"
DELETE r
// 强制删除节点及其关系
MATCH (n:RiskTag {name: "过期标签"})
DETACH DELETE n
// 移除标签和属性
MATCH (c:Customer {cust_id: "C001"})
REMOVE c:VIP, c.temp_token
4. 进阶模式与算法 (Advanced Patterns)
4.1 可变长路径 (Variable Length Paths)
在反欺诈中,经常需要查找多跳关系(例如:资金经过多次转账最终流向哪里)。
// 查找从账户 A 到账户 B 的转账路径,长度为 1 到 5 跳
MATCH p = (start:Account {id: "A001"})-[:TRANSFER*1..5]->(end:Account {id: "A002"})
RETURN p
*: 任意长度*2: 固定 2 跳*2..5: 2 到 5 跳
4.2 最短路径 (Shortest Path)
查找两个节点之间的最短连接。
MATCH (start:Customer {name: "张三"}), (end:Customer {name: "李四"})
// 查找两人之间最短的交互路径(无论什么关系)
MATCH p = shortestPath((start)-[*]-(end))
RETURN p
4.3 列表与聚合 (List & Aggregation)
Cypher 提供了强大的列表处理能力。
collect(): 将多行结果聚合为一个列表。UNWIND: 将列表展开为多行(collect的逆操作)。
// 聚合:列出每个客户拥有的所有账户ID列表
MATCH (c:Customer)-[:OWNS]->(a:Account)
RETURN c.name, collect(a.account_id) AS account_ids, count(a) AS total_accounts
// 展开:处理输入的列表数据
WITH ["A001", "A002", "A003"] AS target_ids
UNWIND target_ids AS aid
MATCH (a:Account {account_id: aid})
RETURN a
4.4 WITH 子句 (Piping)
WITH 用于连接查询的多个部分,它可以对中间结果进行过滤、聚合或排序,然后传递给下一个查询部分。
// 查找转账次数超过 10 次的账户,并标记为高频账户
MATCH (a:Account)-[:TRANSFER]->()
WITH a, count(*) AS txn_count
WHERE txn_count > 10
SET a:HighFrequency
RETURN a.account_id, txn_count
5. 常用函数 (Functions)
5.1 字符串函数
toUpper(s),toLower(s)substring(s, start, length)replace(s, search, replace)split(s, delimiter)-> List
5.2 标量与转换函数
coalesce(val1, val2, ...): 返回第一个非空值。head(list),last(list),size(list)toInteger(),toFloat(),toString(),toBoolean()type(r): 返回关系类型。labels(n): 返回节点标签列表。
5.3 时间日期函数
date(): 当前日期。datetime(): 当前日期时间。date("2023-01-01"): 字符串转日期。duration.between(date1, date2): 计算时间差。
// 查询过去 30 天内的转账
MATCH (t:Transfer)
WHERE t.timestamp > datetime() - duration({days: 30})
RETURN t
6. 性能优化 (Performance)
6.1 索引与约束 (Index & Constraint)
在生产环境中,必须为常用的查询字段创建索引。
// 创建唯一性约束(同时也是索引)
CREATE CONSTRAINT cust_id_unique IF NOT EXISTS FOR (c:Customer) REQUIRE c.cust_id IS UNIQUE;
// 创建普通索引
CREATE INDEX account_balance_idx IF NOT EXISTS FOR (a:Account) ON (a.balance);
6.2 PROFILE 与 EXPLAIN
在查询前加上 PROFILE 关键字,可以看到查询的执行计划和实际 DB 命中次数,用于调优。
PROFILE MATCH (c:Customer {name: "张三"}) RETURN c
7. 数据导入 (Loading Data)
LOAD CSV 是 Neo4j 处理 CSV 文件的标准方式。
// 示例:从 CSV 导入交易数据
LOAD CSV WITH HEADERS FROM 'file:///transactions.csv' AS row
// 1. 查找或创建关联节点
MERGE (src:Account {id: row.from_acc})
MERGE (dst:Account {id: row.to_acc})
// 2. 创建关系
CREATE (src)-[:TRANSFER {
amount: toInteger(row.amount),
date: datetime(row.date)
}]->(dst)
8. 实战案例:反欺诈查询
结合项目中的数据结构,以下是一些高级查询示例:
案例 1: 资金回流检测 (Cycle Detection)
检测资金是否转出一圈后又回到了原账户(典型的洗钱模式)。
MATCH path = (a:Account)-[:TRANSFER*2..5]->(a)
WHERE a.account_id = "A001"
RETURN path
案例 2: 关联团伙挖掘 (Community Detection)
查找使用同一设备登录的不同客户。
MATCH (c1:Customer)-[:USED_DEVICE]->(d:Device)<-[:USED_DEVICE]-(c2:Customer)
WHERE c1.cust_id < c2.cust_id // 避免重复对 (A-B, B-A)
RETURN c1.name, c2.name, d.device_fp AS SharedDevice
案例 3: 多跳资金追踪
查找从”黑名单客户”转出的资金,在 3 跳之内进入了哪些账户。
MATCH (suspect:Customer {risk_level: "Blacklist"})-[:OWNS]->(sa:Account)
MATCH p = (sa)-[:TRANSFER*1..3]->(target:Account)
RETURN target.account_id, reduce(s = 0, r in relationships(p) | s + r.amount) AS total_amount
ORDER BY total_amount DESC