路径查询 #

一、路径查询概述 #

1.1 什么是路径查询 #

路径查询是指在图中查找从一个顶点到另一个顶点的路径的过程。

text
路径查询类型:
├── 最短路径:两点间最短路径
├── 所有路径:两点间所有路径
├── 简单路径:无环路径
├── 加权路径:考虑边权重的路径
└── 路径遍历:遍历指定深度的路径

1.2 路径概念 #

text
路径概念:
├── 路径长度:边的数量
├── 路径权重:边权重之和
├── 简单路径:不重复顶点
├── 环路:起点和终点相同
└── 路径方向:有向或无向

二、Gremlin路径查询 #

2.1 基本路径查询 #

gremlin
// 获取遍历路径
g.V('1').out('knows').path()

// 路径属性
g.V('1').out('knows').path().by('name')

// 多跳路径
g.V('1').out('knows').out('knows').path().by('name')

// 带边的路径
g.V('1').outE('knows').inV().path().by('name').by('weight')

2.2 简单路径 #

gremlin
// 无环路径
g.V('1').repeat(out().simplePath()).times(3)

// 排除特定顶点
g.V('1').out('knows').simplePath().where(without('1'))

2.3 最短路径 #

gremlin
// 最短路径(BFS)
g.V('1').repeat(out().simplePath()).until(hasId('2')).limit(1).path()

// 最短路径带属性
g.V().has('name', 'Tom').
  repeat(out().simplePath()).until(has('name', 'Jerry')).
  limit(1).
  path().by('name')

// 指定深度的最短路径
g.V('1').repeat(out().simplePath()).until(hasId('2').or().loops().is(gt(5))).
  hasId('2').
  path()

2.4 所有路径 #

gremlin
// 所有路径(限制深度)
g.V('1').repeat(out().simplePath()).times(3).emit(hasId('2')).path()

// 所有路径(限制数量)
g.V('1').repeat(out().simplePath()).until(hasId('2')).limit(10).path()

// 所有路径带深度限制
g.V('1').repeat(out().simplePath()).until(hasId('2').or().loops().is(gt(5))).
  hasId('2').
  path()

2.5 加权路径 #

gremlin
// 使用sack计算路径权重
g.withSack(0).V('1').
  repeat(outE().sack(sum).by('weight').inV().simplePath()).
  until(hasId('2')).
  limit(1).
  sack()

// 最小权重路径
g.withSack(0).V('1').
  repeat(outE().sack(sum).by('weight').inV().simplePath()).
  until(hasId('2')).
  order().by(sack()).
  limit(1).
  path().by('name').sack()

2.6 路径过滤 #

gremlin
// 按路径长度过滤
g.V('1').out('knows').path().filter(count(local).is(eq(3)))

// 按路径内容过滤
g.V('1').out('knows').path().filter(unfold().has('status', 'active'))

// 排除特定路径
g.V('1').out('knows').path().filter(unfold().has('name', without('Jerry')))

三、SPARQL路径查询 #

3.1 属性路径 #

sparql
PREFIX ex: <http://example.org/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

# 序列路径
SELECT ?friend
WHERE {
  ex:Tom foaf:knows/foaf:knows ?friend .
}

# 零次或多次
SELECT ?ancestor
WHERE {
  ex:Tom ex:parent* ?ancestor .
}

# 一次或多次
SELECT ?descendant
WHERE {
  ex:Tom ex:parent+ ?descendant .
}

3.2 路径操作符 #

sparql
# 路径选择(OR)
SELECT ?related
WHERE {
  ex:Tom (foaf:knows|foaf:follows) ?related .
}

# 反向路径
SELECT ?follower
WHERE {
  ?follower ^foaf:knows ex:Tom .
}

# 路径组合
SELECT ?friend
WHERE {
  ex:Tom (foaf:knows/ex:worksAt/^ex:worksAt) ?friend .
}

3.3 路径长度限制 #

sparql
# 1到3跳
SELECT ?friend
WHERE {
  ex:Tom (foaf:knows{1,3}) ?friend .
}

# 精确跳数
SELECT ?friend
WHERE {
  ex:Tom (foaf:knows{2}) ?friend .
}

# 至少N跳
SELECT ?friend
WHERE {
  ex:Tom (foaf:knows{2,}) ?friend .
}

四、实际应用示例 #

4.1 社交网络路径 #

gremlin
// 查找两个用户之间的最短路径
g.V().has('userId', 'user_001').
  repeat(both('follows').simplePath()).
  until(has('userId', 'user_002')).
  limit(1).
  path().by('name')

// 查找N度人脉
g.V().has('userId', 'user_001').
  repeat(both('follows').simplePath()).times(3).
  emit().
  dedup().
  path().by('name')

// 查找共同好友路径
g.V().has('userId', 'user_001').as('a').
  out('follows').as('mutual').
  in('follows').as('b').
  where('b', has('userId', 'user_002')).
  select('a', 'mutual', 'b').by('name')

4.2 组织结构路径 #

gremlin
// 查找上级链
g.V().has('userId', 'emp_001').
  repeat(in('manages')).emit().path().by('name')

// 查找下属树
g.V().has('userId', 'ceo').
  repeat(out('manages')).emit().path().by('name')

// 查找跨部门协作路径
g.V().has('userId', 'emp_001').
  repeat(out('works_with').simplePath()).
  until(has('department', 'Engineering')).
  limit(5).
  path().by('name')

4.3 知识图谱路径 #

gremlin
// 查找概念层级路径
g.V().has('name', 'Graph Database').
  repeat(in('subClassOf')).emit().path().by('name')

// 查找相关概念路径
g.V().has('name', 'Neptune').
  repeat(out('related').simplePath()).times(2).
  emit().path().by('name')

// 查找推理路径
g.V().has('name', 'Tom').
  repeat(out('hasProperty').out('implies').simplePath()).
  until(has('name', 'Expert')).
  path().by('name')

4.4 欺诈检测路径 #

gremlin
// 查找可疑交易路径
g.V().has('transactionId', 'tx_001').
  repeat(both('transferred_to').simplePath()).times(3).
  emit(has('flagged', true)).
  path().by('transactionId')

// 查找环路(可能的洗钱)
g.V().hasLabel('account').
  repeat(out('transferred_to')).times(5).
  emit(cyclicPath()).
  path().by('accountId')

// 查找高风险路径
g.withSack(0).V().has('transactionId', 'tx_001').
  repeat(outE('transferred_to').sack(sum).by('riskScore').inV().simplePath()).
  until(sack().is(gt(100))).
  limit(5).
  path().by('transactionId').sack()

五、路径查询优化 #

5.1 性能优化技巧 #

gremlin
// 使用simplePath避免环路
g.V('1').repeat(out().simplePath()).times(3)  // 好
g.V('1').repeat(out()).times(3)  // 可能产生环路

// 限制遍历深度
g.V('1').repeat(out()).times(3)  // 好
g.V('1').repeat(out()).until(...)  // 注意条件

// 使用limit限制结果
g.V('1').repeat(out()).until(hasId('2')).limit(1).path()  // 好

// 使用索引过滤
g.V().has('userId', 'user_001').repeat(out()).times(3)  // 好

5.2 查询策略 #

text
查询策略:
├── 从小集合开始
├── 使用双向搜索
├── 限制路径深度
├── 使用简单路径
└── 分批处理大量结果

六、路径算法 #

6.1 BFS vs DFS #

text
遍历策略:
├── BFS(广度优先)
│   ├── 适合最短路径
│   └── 内存消耗较大
└── DFS(深度优先)
    ├── 适合深度遍历
    └── 可能栈溢出

6.2 Neptune默认使用BFS #

gremlin
// Neptune默认使用BFS
g.V('1').repeat(out()).times(3)

// 最短路径使用BFS
g.V('1').repeat(out().simplePath()).until(hasId('2')).limit(1).path()

七、总结 #

路径查询要点:

操作 Gremlin SPARQL
路径获取 path() 属性路径
简单路径 simplePath() -
最短路径 repeat().until().limit(1) 属性路径
加权路径 sack() -
路径过滤 filter() FILTER

最佳实践:

  1. 使用simplePath避免环路
  2. 限制遍历深度
  3. 使用limit限制结果
  4. 选择合适的遍历策略
  5. 使用索引加速查询

下一步,让我们学习图算法!

最后更新:2026-03-27