适配JanusGraph:如何聚合Gremlin遍历结果统计时段内唯一车辆数
问题背景
给定以下使用TinkerPop 3.7.2创建的车辆停放图结构:
// 车辆停放图结构 g = TinkerGraph.open().traversal(); // 创建3个停车场,每个停车场3个车位 for (parking in ['Mayfair', 'Euston', 'GareDuNord']) { g.addV('parking').property('name', parking).iterate(); for (slot = 1; slot < 4; slot++) { var slotName = String.format('%s %s', parking, slot); g.addV('slot').property('name', slotName). addE('hasSlot').from(__.V().has('parking', 'name', parking)).iterate(); } } // 关联停车场与所属城市 g.addV('city').property('name', 'London').addE('hasParking').to(__.V().has('parking', 'name', 'Mayfair')); g.V().has('city', 'name', 'London').addE('hasParking').to(__.V().has('parking', 'name', 'Euston')); g.addV('city').property('name', 'Paris').addE('hasParking').to(__.V().has('parking', 'name', 'GareDuNord')); // 查看所有停车场和车位(注释) // g.V().hasLabel('parking').bothE().elementMap() // 创建3辆车及其不同时间的停放记录 locationMaps = [ 'v1': ['Mayfair 1', 'Euston 2', 'GareDuNord 3'], 'v2': ['Mayfair 2', 'Mayfair 3', 'Mayfair 1'], 'v3': ['Mayfair 3', null, 'Euston 1'] ] for (lMap in locationMaps) { vehicleName = lMap.key; g.addV('vehicle').property('name', vehicleName).iterate(); locations = lMap.value; t = 1; for (locationName in locations) { if (locationName != null) { g.V().has('vehicle', 'name', vehicleName). addE('parkedAt').to(__.V().has('slot', 'name', locationName)). property('time', t).iterate(); } t += 1; } } // 查看所有停放事件(注释) // g.E().hasLabel('parkedAt').elementMap()
当前尝试与问题
我写出的Gremlin查询能得到去重的城市-停车场-车辆组合:
// 查询时间区间内各城市停车场的唯一车辆数(当前版本) g.V().hasLabel('city'). out('hasParking'). out('hasSlot'). inE('parkedAt').has('time', P.lte(2)).has('time', P.gte(1)). project('city', 'parking', 'vehicle'). by(inV().in('hasSlot').in('hasParking').values('name')). by(inV().in('hasSlot').values('name')). by(outV().values('name')). dedup()
输出结果:
==>[city:London,parking:Mayfair,vehicle:v1] ==>[city:London,parking:Mayfair,vehicle:v2] ==>[city:London,parking:Mayfair,vehicle:v3] ==>[city:London,parking:Euston,vehicle:v1]
但我需要按城市-停车场分组统计唯一车辆数,预期输出:
==>[city:London,parking:Mayfair,count:3] ==>[city:London,parking:Euston,count:1]
我还尝试用path()方法获取数据:
g.V().hasLabel('city'). out('hasParking'). out('hasSlot'). inE('parkedAt').has('time', P.lte(2)).has('time', P.gte(1)). outV(). path(). by('name'). // 城市名称 by('name'). // 停车场名称 by(constant('')). // 忽略车位 by(constant('')). // 忽略停放边 by('name'). // 车辆名称 dedup()
结果类似,但缺少映射键,仍无法实现按城市和停车场统计唯一车辆数的需求。另外实际场景中有时需要从单个路径步骤里获取多个信息,不知道该怎么操作。
环境限制
查询将在JanusGraph服务器上通过gremlin-python执行,有以下限制:
- 标签未建立索引,不能以
g.V().hasLabel('vehicle')作为查询起点; - 无法使用lambda表达式。
解决方案
以下是符合限制的Gremlin查询,可实现指定时间区间内各城市停车场的唯一车辆停放数量统计:
方案一:利用节点标记+嵌套分组
g.V().hasLabel('city'). out('hasParking').as('parking'). out('hasSlot'). inE('parkedAt').has('time', between(1,2)). outV().as('vehicle'). // 按城市、停车场分组,统计唯一车辆数 group(). by(__.select('parking').in('hasParking').values('name')). // 城市 by(group(). by(__.select('parking').values('name')). // 停车场 by(__.select('vehicle').values('name').dedup().count())). // 展开分组并整理成预期格式 unfold(). select(values).unfold(). project('city', 'parking', 'count'). by(select(keys)). by(select(keys)). by(select(values))
方案二:路径提取+分组统计
g.V().hasLabel('city').as('city'). out('hasParking').as('parking'). out('hasSlot'). inE('parkedAt').has('time', P.gte(1).and(P.lte(2))). outV().as('vehicle'). path().by('name'). // 提取路径中的关键信息 map(select('city', 'parking', 'vehicle')). dedup(). // 按城市、停车场分组统计 group(). by('city'). by(group(). by('parking'). by(count())). // 整理输出格式 unfold().select(values).unfold(). project('city', 'parking', 'count'). by(constant(select(keys).next())). by(select(keys)). by(select(values))
核心逻辑说明
- 节点标记:通过
as()标记城市、停车场、车辆节点,避免重复遍历图结构,提升查询效率; - 去重处理:用
dedup()确保同一车辆在同一城市-停车场下仅被统计一次; - 嵌套分组:通过两层
group()实现先按城市分组,再按停车场分组统计唯一车辆数; - 格式整理:最后用
project()将分组结果转换为预期的键值对格式,完全符合环境限制。
内容的提问来源于stack exchange,提问作者bernied
相关产品推荐
相关产品推荐

