You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gremlin遍历顶点边时动态加常量属性返回路径树方法

AWS Neptune Gremlin 查询动态追加临时属性实现方案

使用搭载最新版Gremlin查询语言的AWS Neptune服务,查询通过Jupyter SageMaker Notebook发起。

示例测试数据

写入测试数据的Gremlin语句如下:

g.addV('test_report').property('name', 'REF').property('creationDateTime','2022-07-01 00:00:00.000000')
g.addV('test_reportrelease').property('name', 'A').property('creationDateTime','2022-07-01 01:00:00.000000')
g.addV('test_reportrelease').property('name', 'B').property('creationDateTime','2022-07-01 02:00:00.000000')
g.addV('test_reportrelease').property('name', 'C').property('creationDateTime','2022-07-01 03:00:00.000000')

g.addE('test_has').property('creationDateTime','2022-07-02 01:00:00.000000')
.from(V().hasLabel('test_report').has('name', 'REF'))
.to(V().hasLabel('test_reportrelease').has('name', 'A'))

g.addE('test_has').property('creationDateTime','2022-07-02 02:00:00.000000')
.from(V().hasLabel('test_report').has('name', 'REF'))
.to(V().hasLabel('test_reportrelease').has('name', 'B'))

g.addE('test_has').property('creationDateTime','2022-07-02 03:00:00.000000')
.from(V().hasLabel('test_report').has('name', 'REF'))
.to(V().hasLabel('test_reportrelease').has('name', 'C'))

需求说明

  • 第一步:查询标签为test_report的顶点
  • 后续通过union分支执行多组遍历逻辑:
    • 分支1:遍历所有标签为test_has的出边(连接test_report与test_reportrelease顶点),沿边遍历至入顶点,给遍历过程中经过的每条边追加key为ref、value为test_has的常量属性
    • 分支2:遍历test_has出边,按creationDateTime升序排序后取第一条结果,沿边遍历至入顶点,给经过的边追加key为ref、value为test_first的常量属性
    • 分支3:遍历test_has出边,按creationDateTime降序排序后取第一条结果,沿边遍历至入顶点,给经过的边追加key为ref、value为test_last的常量属性
  • 最终调用tree()步骤,将所有遍历到的顶点、边以树结构返回

核心约束:追加的ref属性仅存在于查询结果中,不得写入数据库持久化存储。

现有问题

当前已编写的基础查询缺少追加ref常量的逻辑:

g.V().hasLabel('test_report')
.union(optional(
outE().hasLabel('test_has').order().by('creationDateTime').inV()),
optional(outE().hasLabel('test_has').order().by('creationDateTime').limit(1).inV()),
optional(outE().hasLabel('test_has').order().by(coalesce(values('creationDateTime'), constant('')), desc).limit(1).store('last').inV())
).valueMap(true).path()

之前尝试的两种写法都会在调用valueMap时中断图遍历,无法继续后续逻辑:

  1. 第一种失败尝试
g.V().hasLabel('test_report').outE().hasLabel('test_has')
.order().by('creationDateTime').limit(1).valueMap(true).unfold().inject(['ref':'test_first']).fold()
  1. 第二种失败尝试
g.V().hasLabel('test_report').outE().hasLabel('test_has')
.order().by('creationDateTime').limit(1).union(valueMap(true).unfold(), project('ref').by(constant('test_first'))).fold()

预期返回格式

测试阶段优先返回可读性更强的path格式,最终输出tree结构,预期path结果示例:

1   path[v[70c0dcd5-a6b9-4532-28bf-85705e94697e], e[66c0dcd7-31ed-e381-5331-e8c73bb91be1][70c0dcd5-a6b9-4532-28bf-85705e94697e-test_has->c0c0dcd5-c102-4031-d739-b5bd8fe161bc], v[c0c0dcd5-c102-4031-d739-b5bd8fe161bc], {<T.id: 1>: 'c0c0dcd5-c102-4031-d739-b5bd8fe161bc', <T.label: 4>: 'test_reportrelease', 'name': ['A'], 'creationDateTime': ['2022-07-01 01:00:00.000000'], 'ref': ['test_has']}]
2   path[v[70c0dcd5-a6b9-4532-28bf-85705e94697e], e[b0c0dcd7-4d99-1f3c-077d-decd2e251c46][70c0dcd5-a6b9-4532-28bf-85705e94697e-test_has->68c0dcd5-c5c5-c869-d789-96acbb88131f], v[68c0dcd5-c5c5-c869-d789-96acbb88131f], {<T.id: 1>: '68c0dcd5-c5c5-c869-d789-96acbb88131f', <T.label: 4>: 'test_reportrelease', 'name': ['B'], 'creationDateTime': ['2022-07-01 02:00:00.000000'], 'ref': ['test_has']}]
3   path[v[70c0dcd5-a6b9-4532-28bf-85705e94697e], e[70c0dcd7-72ac-204f-1341-cc843d165a38][70c0dcd5-a6b9-4532-28bf-85705e94697e-test_has->5ac0dcd5-cb10-f392-7d80-69541c4f22eb], v[5ac0dcd5-cb10-f392-7d80-69541c4f22eb], {<T.id: 1>: '5ac0dcd5-cb10-f392-7d80-69541c4f22eb', <T.label: 4>: 'test_reportrelease', 'name': ['C'], 'creationDateTime': ['2022-07-01 03:00:00.000000'], 'ref': ['test_has']}]
4   path[v[70c0dcd5-a6b9-4532-28bf-85705e94697e], e[66c0dcd7-31ed-e381-5331-e8c73bb91be1][70c0dcd5-a6b9-4532-28bf-85705e94697e-test_has->c0c0dcd5-c102-4031-d739-b5bd8fe161bc], v[c0c0dcd5-c102-4031-d739-b5bd8fe161bc], {<T.id: 1>: 'c0c0dcd5-c102-4031-d739-b5bd8fe161bc', <T.label: 4>: 'test_reportrelease', 'name': ['A'], 'creationDateTime': ['2022-07-01 01:00:00.000000'], 'ref': ['test_first']}]
5   path[v[70c0dcd5-a6b9-4532-28bf-85705e94697e], e[70c0dcd7-72ac-204f-1341-cc843d165a38][70c0dcd5-a6b9-4532-28bf-85705e94697e-test_has->5ac0dcd5-cb10-f392-7d80-69541c4f22eb], v[5ac0dcd5-cb10-f392-7d80-69541c4f22eb], {<T.id: 1>: '5ac0dcd5-cb10-f392-7d80-69541c4f22eb', <T.label: 4>: 'test_reportrelease', 'name': ['C'], 'creationDateTime': ['2022-07-01 03:00:00.000000'], 'ref': ['test_last']}]

实现方案

Gremlin不能直接修改遍历中元素的原生属性且不写入库,需要通过as打标签+project动态组装属性Map的方式实现,全程不会触发数据库写入,也不会中断遍历链路。

测试阶段:path格式输出语句

可直接在SageMaker Notebook中运行,返回和预期格式一致的path结果:

g.V().hasLabel('test_report')
.union(
  // 分支1:所有test_has边,ref=test_has
  outE('test_has').as('e')
  .project('edge', 'refVal')
    .by()
    .by(constant('test_has'))
  .as('edgeWithRef')
  .inV().as('v')
  .project('vertexProps')
    .by(valueMap(true))
  .select('v', 'edgeWithRef'),

  // 分支2:最早创建的边,ref=test_first
  outE('test_has').order().by('creationDateTime', asc).limit(1).as('e')
  .project('edge', 'refVal')
    .by()
    .by(constant('test_first'))
  .as('edgeWithRef')
  .inV().as('v')
  .project('vertexProps')
    .by(valueMap(true))
  .select('v', 'edgeWithRef'),

  // 分支3:最晚创建的边,ref=test_last
  outE('test_has').order().by('creationDateTime', desc).limit(1).as('e')
  .project('edge', 'refVal')
    .by()
    .by(constant('test_last'))
  .as('edgeWithRef')
  .inV().as('v')
  .project('vertexProps')
    .by(valueMap(true))
  .select('v', 'edgeWithRef')
)
.path()

最终输出:tree结构语句

替换上述语句的结尾部分即可返回tree结构结果:

g.V().hasLabel('test_report').as('root')
.union(
  outE('test_has').as('edgeAll')
  .project('edge', 'ref').by().by(constant('test_has')).as('edgeMeta')
  .inV().as('vertexAll'),

  outE('test_has').order().by('creationDateTime', asc).limit(1).as('edgeFirst')
  .project('edge', 'ref').by().by(constant('test_first')).as('edgeMeta')
  .inV().as('vertexFirst'),

  outE('test_has').order().by('creationDateTime', desc).limit(1).as('edgeLast')
  .project('edge', 'ref').by().by(constant('test_last')).as('edgeMeta')
  .inV().as('vertexLast')
)
.tree()
    .by(valueMap(true))
    .by(
      choose(label(),
        is('test_has'), union(valueMap(true), project('ref').by(select('edgeMeta').select('ref'))).fold(),
        valueMap(true)
      )
    )

方案说明

  • 全程未调用会写入数据库的property()步骤,所有ref属性都是查询时动态拼接的临时结果,不会持久化到Neptune存储
  • 用as给遍历到的边、常量值打标签,后续通过select取出组装,不会像直接调用valueMap那样中断遍历器流转
  • 完全兼容AWS Neptune的Gremlin语法,无版本依赖,老版本Neptune也可直接运行

内容的提问来源于stack exchange,提问作者Cwellan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:48:11