JanusGraph通用Gremlin Upsert触发冗余存储更新:原因与修复
JanusGraph Gremlin Upsert通用查询触发不必要更新的问题
我有一个针对顶点的Gremlin Upsert查询,当age或city属性值未变化时,不会向JanusGraph存储后端发起更新,代码如下:
Vertex marko = (Vertex) traversalSource.V() .has("person", "name", name) .fold() .coalesce(__.unfold(), __.addV("person").property("name", name)) .choose(__.not(__.has("age", age)), __.property("age", age)) .choose(__.not(__.has("city", city)), __.property("city", city)) .next();
为实现通用逻辑,我将其重构为用Map指定待更新属性的版本,代码如下:
Map<String, Object> propsToUpdate = new HashMap<>(); propsToUpdate.put("age", age); propsToUpdate.put("city", city); Vertex marko = (Vertex) traversalSource.withSideEffect("propsToUpdate", propsToUpdate) .V() .has("person", "name", name) .fold() .coalesce(__.unfold(), __.addV("person").property("name", name)) .as("v") .select("propsToUpdate") .unfold() .as("updateProp") .select("v") .choose(__.not(__.has(__.select("updateProp").by(Column.keys).toString(), __.select("updateProp").by(Column.values))), __.property(__.select("updateProp").by(Column.keys), __.select("updateProp").by(Column.values))) .select("v") .next();
但该通用查询即使属性值未变,仍会触发存储后端更新。请问两个查询行为差异的原因是什么?如何修改通用查询避免不必要的存储更新?(使用JanusGraph 0.6.2版本)
行为差异原因
- 硬编码属性键的直接解析:原始查询中,
__.has("age", age)的属性名是字符串字面量,Gremlin能直接将其解析为JanusGraph的PropertyKey对象,准确对比顶点当前属性值与传入值,仅当不一致时执行property操作。 .toString()的错误用法:重构后的查询中,__.select("updateProp").by(Column.keys).toString()并没有获取到实际的属性键名称,而是把Traversal对象转换成了类似[SelectStep(updateProp,[value(key)])]的字符串。这导致__.has(...)的判断逻辑永远为真(顶点不可能存在这个字符串命名的属性),因此每次都会执行property操作,触发存储更新。
修正后的通用查询
要解决这个问题,需移除.toString()调用,让Gremlin正确解析动态属性键和值的遍历。修正后的代码如下:
Map<String, Object> propsToUpdate = new HashMap<>(); propsToUpdate.put("age", age); propsToUpdate.put("city", city); Vertex marko = (Vertex) traversalSource.withSideEffect("propsToUpdate", propsToUpdate) .V() .has("person", "name", name) .fold() .coalesce(__.unfold(), __.addV("person").property("name", name)) .as("v") .sideEffect( __.select("propsToUpdate").unfold() .as("kv") .select("v") .choose( __.not(__.has(__.select("kv").by(Column.keys), __.select("kv").by(Column.values))), __.property(__.select("kv").by(Column.keys), __.select("kv").by(Column.values)) ) ) .next();
关键修改说明
- 直接使用
__.select("kv").by(Column.keys)作为has方法的属性键参数,Gremlin会正确执行该遍历并获取实际的属性键名称,而非将遍历对象转成字符串。 - 用
sideEffect包裹属性更新逻辑,既保证了遍历流中始终保留顶点对象,也避免了重复select("v")的冗余操作。
修改后,通用查询会和原始查询保持一致,仅当属性值实际发生变化时,才会向JanusGraph存储后端发起更新。
内容的提问来源于stack exchange,提问作者rpg
相关产品推荐
相关产品推荐

