Gremlin JS:如何对比遍历中前后顶点属性实现指定图查询?
Gremlin JS 查询朋友的朋友(同姓且非直接好友)异常问题
需求
查询人物属性图中满足以下条件的顶点:
- 是指定用户的朋友的朋友
- 姓氏(
lastName)与指定用户相同 - 不是指定用户的直接好友
尝试的查询代码
我试过几种Gremlin JS写法,均未报错但结果不符合预期:
// 写法1:用sideEffect缓存姓氏 let query = g.V(userId) .sideEffect( __.values("lastName") .as("userLastName") ) .out("isFriendsWith") .out("isFriendsWith") .where( __.has("lastName", __.select("userLastName")) ) .where( __.not( __.in_("isFriendsWith").hasId(userId) ) ) // 写法2:用as("user")保留初始用户引用 g.V(userId) .as("user") .out("isFriendsWith") .out("isFriendsWith") .where( __.has("lastName", __.select("user").values("lastName")) ) .where( __.not( __.in_("isFriendsWith").hasId(userId) ) ) // 写法3:直接对比values g.V(userId) .as("user") .out("isFriendsWith") .out("isFriendsWith") .where( __.values('lastName').is(__.select("user").values("lastName")) ) .where( __.not( __.in_("isFriendsWith").hasId(userId) ) )
测试数据初始化
以下测试数据中,预期返回Billy(而非John、Sarah或Sally):
await g.addV("user").property("firstName", "John").property("lastName", "Doe") .as('john') .addV("user").property("firstName", "Sally").property("lastName", "Doe") .as('sally') .addV("user").property("firstName", "Billy").property("lastName", "Doe") .as('billy') .addV("user").property("firstName", "Sarah").property("lastName", "Johnson") .as('sarah') .addE("isFriendsWith") .from_(__.select('john')) .to(__.select('sally')) .addE("isFriendsWith") .from_(__.select('sally')) .to(__.select('billy')) .addE("isFriendsWith") .from_(__.select('sally')) .to(__.select('sarah'))
问题现象
上述写法要么返回空结果,要么过滤条件失效;但硬编码姓氏(比如直接写has("lastName", "Doe"))时,查询能正常返回Billy,说明数据逻辑没问题,只是属性引用的写法有误。另外Gremlin JS文档较少,对其类型机制不太清楚,不确定Java文档是否适用。
解决方案
可以通过先缓存目标姓氏,再在遍历中对比的方式,避免路径引用的问题,或者使用match语法明确声明遍历关系:
方案1:提前缓存姓氏
const query = g.V(userId) // 先获取并缓存目标用户的姓氏 .values("lastName") .as("targetLastName") // 回到目标用户,开始遍历朋友的朋友 .V(userId) .out("isFriendsWith") .out("isFriendsWith") // 对比姓氏 .where(__.values("lastName").is(__.select("targetLastName"))) // 排除直接好友 .where(__.not(__.in_("isFriendsWith").hasId(userId))) // 去重(避免同一节点被多次匹配) .dedup()
方案2:使用match语法明确关系
match语法更清晰地声明了各节点之间的关联,避免路径引用的歧义:
const query = g.match( __.as("user").hasId(userId), __.as("user").out("isFriendsWith").as("directFriend"), __.as("directFriend").out("isFriendsWith").as("friendOfFriend"), // 匹配姓氏相同的条件 __.as("user").values("lastName").as("userLastName"), __.as("friendOfFriend").values("lastName").is(__.select("userLastName")), // 排除直接好友 __.not(__.as("user").out("isFriendsWith").as("friendOfFriend")) ) .select("friendOfFriend") .dedup()
问题原因
之前的写法中,当遍历到朋友的朋友节点时,select("user")的路径引用可能因Gremlin的遍历上下文丢失,导致无法正确获取初始用户的姓氏。通过提前缓存姓氏或使用match明确节点关系,可以避免这种上下文丢失的问题。
内容的提问来源于stack exchange,提问作者ashissl
相关产品推荐
相关产品推荐

