图遍历查询 #
一、图遍历概述 #
图遍历是在图中从一个或多个起点出发,沿着边访问顶点的过程。
1.1 遍历语法 #
aql
FOR v[, e[, p]] IN [min..max] DIRECTION startVertex edgeCollection
RETURN result
1.2 遍历参数 #
| 参数 | 说明 |
|---|---|
| v | 当前顶点 |
| e | 当前边(可选) |
| p | 完整路径(可选) |
| min…max | 遍历深度范围 |
| DIRECTION | 遍历方向 |
| startVertex | 起始顶点 |
| edgeCollection | 边集合 |
1.3 遍历方向 #
| 方向 | 说明 |
|---|---|
| OUTBOUND | 从_from到_to方向 |
| INBOUND | 从_to到_from方向 |
| ANY | 双向遍历 |
二、基本遍历 #
2.1 单跳遍历 #
aql
FOR v IN 1..1 OUTBOUND "users/user_001" follows
RETURN v.name
2.2 多跳遍历 #
aql
FOR v IN 2..2 OUTBOUND "users/user_001" follows
RETURN v.name
2.3 范围遍历 #
aql
FOR v IN 1..3 OUTBOUND "users/user_001" follows
RETURN v.name
2.4 获取边信息 #
aql
FOR v, e IN 1..2 OUTBOUND "users/user_001" follows
RETURN {
user: v.name,
edgeType: e.type,
createdAt: e.createdAt
}
2.5 获取完整路径 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
RETURN {
user: v.name,
path: p.vertices[*].name,
depth: LENGTH(p.edges)
}
三、遍历方向 #
3.1 OUTBOUND遍历 #
查找用户关注的人:
aql
FOR v IN 1..1 OUTBOUND "users/user_001" follows
RETURN v.name
3.2 INBOUND遍历 #
查找关注用户的人:
aql
FOR v IN 1..1 INBOUND "users/user_001" follows
RETURN v.name
3.3 ANY遍历 #
双向遍历:
aql
FOR v, e IN 1..1 ANY "users/user_001" follows
RETURN {
name: v.name,
direction: e._from == "users/user_001" ? "following" : "follower"
}
3.4 多方向遍历 #
aql
FOR v, e, p IN 1..2 ANY "users/user_001" follows
RETURN {
name: v.name,
depth: LENGTH(p.edges),
path: p.vertices[*].name
}
四、遍历选项 #
4.1 uniqueVertices #
顶点去重策略:
aql
FOR v IN 1..3 OUTBOUND "users/user_001" follows
OPTIONS { uniqueVertices: "global" }
RETURN v.name
| 值 | 说明 |
|---|---|
| “path” | 路径内不重复(默认) |
| “global” | 全局不重复 |
| “none” | 允许重复 |
4.2 uniqueEdges #
边去重策略:
aql
FOR v IN 1..3 OUTBOUND "users/user_001" follows
OPTIONS { uniqueEdges: "path" }
RETURN v.name
| 值 | 说明 |
|---|---|
| “path” | 路径内不重复(默认) |
| “none” | 允许重复 |
4.3 bfs #
广度优先搜索:
aql
FOR v IN 1..3 OUTBOUND "users/user_001" follows
OPTIONS { bfs: true, uniqueVertices: "global" }
RETURN v.name
4.4 完整选项示例 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
OPTIONS {
bfs: true,
uniqueVertices: "global",
uniqueEdges: "path",
parallelism: 4
}
RETURN {
name: v.name,
depth: LENGTH(p.edges)
}
五、过滤遍历 #
5.1 顶点过滤 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
FILTER v.status == "active"
RETURN v.name
5.2 边过滤 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
FILTER e.type == "friend"
RETURN v.name
5.3 路径过滤 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
FILTER LENGTH(p.edges) == 2
AND p.vertices[1].status == "active"
RETURN v.name
5.4 深度过滤 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
LET depth = LENGTH(p.edges)
FILTER depth >= 2
RETURN {
name: v.name,
depth: depth
}
5.5 复杂过滤 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
FILTER v.status == "active"
FILTER e.type IN ["friend", "colleague"]
FILTER DATE_DIFF(e.createdAt, DATE_NOW(), "days") < 365
RETURN {
name: v.name,
type: e.type,
daysSinceFollow: DATE_DIFF(e.createdAt, DATE_NOW(), "days")
}
六、最短路径 #
6.1 单条最短路径 #
aql
FOR v, e IN OUTBOUND K_SHORTEST_PATHS "users/user_001" TO "users/user_005" follows
RETURN {
vertex: v.name,
edge: e.type
}
6.2 多条最短路径 #
aql
FOR p IN OUTBOUND K_PATHS "users/user_001" TO "users/user_005" follows
LIMIT 3
RETURN {
path: p.vertices[*].name,
length: LENGTH(p.edges)
}
6.3 最短路径带权重 #
aql
FOR v, e, p IN OUTBOUND K_SHORTEST_PATHS "users/user_001" TO "users/user_005" follows
LET weight = SUM(p.edges[*].weight)
RETURN {
path: p.vertices[*].name,
weight: weight
}
6.4 双向最短路径 #
aql
FOR v, e IN ANY K_SHORTEST_PATHS "users/user_001" TO "users/user_005" follows
RETURN {
vertex: v.name,
edge: e.type
}
七、图遍历聚合 #
7.1 统计各深度数量 #
aql
FOR v, e, p IN 1..3 OUTBOUND "users/user_001" follows
COLLECT depth = LENGTH(p.edges) WITH COUNT INTO count
RETURN {
depth: depth,
count: count
}
7.2 统计边类型 #
aql
FOR v, e, p IN 1..2 OUTBOUND "users/user_001" follows
COLLECT type = e.type WITH COUNT INTO count
RETURN {
type: type,
count: count
}
7.3 路径聚合 #
aql
FOR v, e, p IN 1..2 OUTBOUND "users/user_001" follows
COLLECT depth = LENGTH(p.edges) AGGREGATE
avgAge = AVG(v.age),
count = COUNT()
RETURN {
depth: depth,
avgAge: avgAge,
count: count
}
八、多起点遍历 #
8.1 多起点语法 #
aql
FOR start IN ["users/user_001", "users/user_002"]
FOR v IN 1..2 OUTBOUND start follows
RETURN {
start: start,
reached: v.name
}
8.2 从查询结果遍历 #
aql
FOR user IN users
FILTER user.city == "北京"
FOR v IN 1..1 OUTBOUND user._id follows
RETURN {
from: user.name,
to: v.name
}
8.3 多起点聚合 #
aql
FOR user IN users
LET followers = (
FOR v IN 1..1 INBOUND user._id follows
RETURN v
)
RETURN {
user: user.name,
followerCount: LENGTH(followers),
followers: followers[*].name
}
九、实战示例 #
9.1 社交网络分析 #
aql
LET userId = "users/user_001"
LET followers = (
FOR v IN 1..1 INBOUND userId follows
RETURN v
)
LET following = (
FOR v IN 1..1 OUTBOUND userId follows
RETURN v
)
LET mutualFollows = INTERSECTION(
followers[*]._key,
following[*]._key
)
LET friendsOfFriends = (
FOR v IN 2..2 OUTBOUND userId follows
FILTER v._key NOT IN followers[*]._key
FILTER v._key NOT IN following[*]._key
COLLECT user = v WITH COUNT INTO count
SORT count DESC
LIMIT 5
RETURN { user: user.name, mutualFriends: count }
)
RETURN {
userId: userId,
followerCount: LENGTH(followers),
followingCount: LENGTH(following),
mutualFollowCount: LENGTH(mutualFollows),
recommendations: friendsOfFriends
}
9.2 商品推荐 #
aql
LET userId = "users/user_001"
LET purchasedProducts = (
FOR v IN 1..1 OUTBOUND userId purchased
RETURN v._key
)
LET recommendedProducts = (
FOR v, e, p IN 2..2 OUTBOUND userId purchased, purchased
FILTER v._key NOT IN purchasedProducts
COLLECT product = v WITH COUNT INTO count
SORT count DESC
LIMIT 10
RETURN {
product: product,
recommendationScore: count
}
)
RETURN recommendedProducts
9.3 知识图谱查询 #
aql
LET concept = "concepts/machine_learning"
LET relatedConcepts = (
FOR v, e, p IN 1..2 ANY concept is_a, related_to
OPTIONS { uniqueVertices: "global" }
RETURN DISTINCT {
concept: v.name,
relation: e.type,
depth: LENGTH(p.edges)
}
)
LET subConcepts = (
FOR v IN 1..1 INBOUND concept is_a
RETURN v.name
)
LET superConcepts = (
FOR v IN 1..1 OUTBOUND concept is_a
RETURN v.name
)
RETURN {
concept: concept,
subConcepts: subConcepts,
superConcepts: superConcepts,
relatedConcepts: relatedConcepts
}
9.4 欺诈检测 #
aql
LET suspiciousAccount = "accounts/account_001"
LET relatedAccounts = (
FOR v, e, p IN 1..3 ANY suspiciousAccount transferred, shared_device
OPTIONS { uniqueVertices: "global" }
COLLECT account = v WITH COUNT INTO connections
FILTER connections >= 3
RETURN {
account: account._key,
connections: connections,
risk: connections >= 5 ? "high" : "medium"
}
)
RETURN {
suspiciousAccount: suspiciousAccount,
relatedAccounts: relatedAccounts
}
9.5 组织架构查询 #
aql
LET employee = "employees/emp_001"
LET managers = (
FOR v, e, p IN 1..5 INBOUND employee reports_to
RETURN {
name: v.name,
level: LENGTH(p.edges),
position: v.position
}
)
LET subordinates = (
FOR v, e, p IN 1..5 OUTBOUND employee reports_to
RETURN {
name: v.name,
level: LENGTH(p.edges),
position: v.position
}
)
LET colleagues = (
FOR v IN 1..1 INBOUND employee reports_to
FOR colleague IN 1..1 OUTBOUND v._id reports_to
FILTER colleague._key != employee._key
RETURN colleague.name
)
RETURN {
employee: employee,
managers: managers,
subordinates: subordinates,
colleagues: colleagues
}
十、性能优化 #
10.1 限制遍历深度 #
aql
FOR v IN 1..2 OUTBOUND "users/user_001" follows
RETURN v.name
10.2 使用过滤条件 #
aql
FOR v, e IN 1..3 OUTBOUND "users/user_001" follows
FILTER v.status == "active"
LIMIT 100
RETURN v.name
10.3 使用遍历选项 #
aql
FOR v IN 1..3 OUTBOUND "users/user_001" follows
OPTIONS {
bfs: true,
uniqueVertices: "global",
parallelism: 4
}
RETURN v.name
10.4 使用索引 #
javascript
db.follows.ensureHashIndex(["_from"]);
db.follows.ensureHashIndex(["_to"]);
10.5 分析查询计划 #
javascript
db._explain(`
FOR v IN 1..3 OUTBOUND "users/user_001" follows
RETURN v.name
`);
十一、总结 #
图遍历查询要点:
- 基本遍历:FOR v IN min…max DIRECTION startVertex edges
- 遍历方向:OUTBOUND、INBOUND、ANY
- 遍历选项:uniqueVertices、bfs、parallelism
- 过滤条件:顶点过滤、边过滤、路径过滤
- 最短路径:K_SHORTEST_PATHS、K_PATHS
下一步,让我们学习索引管理!
最后更新:2026-03-27