Gremlin:调用 fold() 或 count() 后无法返回上一步
Gremlin: Cannot go back to a previous step after calling fold() or count()
这个查询没有return任何东西,因为调用 fold() 删除了所有以前存储的 as():
g.V()
.hasLabel('user')
.project("user")
.by(
as("singleUser")
.V()
.fold()
.choose(
count(local).is(gt(1)),
select('singleUser'),
unfold()
)
)
当然,我总是可以以牺牲性能为代价来解决这类问题,通过搜索 2 次来复制成本,我正在寻找比这更好的解决方案。
同时将 as() 替换为 store() 会给出不同的输出,因此这也不是解决方案。 store() 存活到 fold() 但使用相同的字符串多次调用将每次调用添加到列表中,并且 as() 将第一次调用替换为第二次调用,而不是同一个工具。
你可以自己试试:https://gremlify.com/tgq24psdfri
编辑:
一个更接近我的真实查询的例子是:
g.V()
.hasLabel('user')
.project("u")
.by(
as("appUser")
.both("friend")
.project("result")
.by(
as("appUserFriend")
.choose(
both("friend").where(bothE('friend').where(bothV().as('appUser'))).count().is(lt(2)),
constant("too small").fold(),
union(
both("friend").where(bothE('friend').where(bothV().as('appUser'))),
select("appUserFriend")
).order().by("name").values("name").fold()
)
).select(values).unfold()
).select(values).unfold().dedup()
此查询查找所有可能的“朋友组”。要形成一个朋友组,每个成员需要成为至少 2 个其他朋友用户(至少一个三角形)的朋友。该查询有效,但也会生成 2 个总成员的组,即当不满足 2 个朋友的条件时,这些组因“太小”而被忽略。
您可以运行这里查询:
https://gremlify.com/lu64acieuw
查询 运行s 和输出是正确的,但请注意第 11 行和第 14 行(在 gremlify 中)搜索是相同的,为了提高性能我想调用 select( ) 回去而不是写相同的搜索,但由于这个问题的问题,这是不可能的。欢迎任何其他不编写相同搜索 2 次的技巧。
这是对它如何一步步工作的描述:
- Select 应用程序的所有用户,我们称他们为“appUser”
- Select所有appUser的好友,暂且称他们为“appUserFriend”
- Select“appUserFriend”的朋友也是“appUser”的朋友,将他们添加到数组中
- 在数组中包含“appUserFriend”和“appUser”
- 删除重复项
考虑到你写问题的方式,我假设你并不真正关心“太小”的组,并且这个问题是关于你在最后的步骤枚举中描述的算法.考虑到这个假设,我注意到您基本上是在检测三角形,然后尝试对它们进行相应的分组。 Gremlin Recipes here 中讨论了循环检测,模式基本上是:
g.V().as("a").repeat(both().simplePath()).times(2).where(both().as("a")).path()
或删除重复路径:
g.V().as("a").repeat(both().simplePath()).times(2).where(both().as("a")).path().
dedup().by(unfold().order().by(id).dedup().fold())
以此为基础,您只需将这些结果转换为您要查找的组即可。如果您发现效率更高,您可能可以在 Gremlin 之外的自己的应用程序代码中执行此操作,但是使用 Gremlin 执行此操作的一种方法是将三角形内的所有对分组,然后组合这些分组路径的元素:
g.V().as('a').
repeat(both().simplePath()).
times(2).
where(both().as('a')).
path().
map(unfold().limit(3).order().by(id).dedup().fold())
dedup().
group('m').
by(limit(local,2)).
group('m').
by(tail(local,2)).
group('m').
by(union(limit(local,1),tail(local,1)).fold()).
cap('m').
unfold().
map(select(values).unfold().unfold().order().by(id).dedup().fold()).
dedup().
map(unfold().values('name').fold())
可能还有更好的方法,但我认为这个查询至少可以避免您一次又一次地查询和重新查询相同的路径。我也认为它更容易理解,因为一旦 reader 注意到三角形计数模式,剩下的就是 free of backtracking 和很多层次结构。很好奇是否在 Gremlin 或您的应用程序代码中更好地处理三角形处理成组。这可能值得探索。
我不确定你的图表有多大,但这个特定的查询可能更适合使用 Spark 和 custom VertexProgram
, something perhaps similar to connectedComponent()
.
的 OLAP 样式处理
查询不需要 choose() 步骤,创建组时不需要检查组大小,可以稍后在创建所有组时使用 where() 步骤完成:
g.V()
.hasLabel('user')
.project("userGroups")
.by(
as("appUser")
.both("friend")
.project("group")
.by(
union(
both('friend').where(neq('appUser')).where(both('friend').where(eq('appUser'))),
identity(),
select("appUser")
).order().by("name").values("name").fold()
).select(values).fold()
).select(values)
.repeat(unfold()).times(3)
// Here the groups of 2 members are removed:
.where(count(local).is(gt(2)))
.dedup()
这个查询没有return任何东西,因为调用 fold() 删除了所有以前存储的 as():
g.V()
.hasLabel('user')
.project("user")
.by(
as("singleUser")
.V()
.fold()
.choose(
count(local).is(gt(1)),
select('singleUser'),
unfold()
)
)
当然,我总是可以以牺牲性能为代价来解决这类问题,通过搜索 2 次来复制成本,我正在寻找比这更好的解决方案。
同时将 as() 替换为 store() 会给出不同的输出,因此这也不是解决方案。 store() 存活到 fold() 但使用相同的字符串多次调用将每次调用添加到列表中,并且 as() 将第一次调用替换为第二次调用,而不是同一个工具。
你可以自己试试:https://gremlify.com/tgq24psdfri
编辑:
一个更接近我的真实查询的例子是:
g.V()
.hasLabel('user')
.project("u")
.by(
as("appUser")
.both("friend")
.project("result")
.by(
as("appUserFriend")
.choose(
both("friend").where(bothE('friend').where(bothV().as('appUser'))).count().is(lt(2)),
constant("too small").fold(),
union(
both("friend").where(bothE('friend').where(bothV().as('appUser'))),
select("appUserFriend")
).order().by("name").values("name").fold()
)
).select(values).unfold()
).select(values).unfold().dedup()
此查询查找所有可能的“朋友组”。要形成一个朋友组,每个成员需要成为至少 2 个其他朋友用户(至少一个三角形)的朋友。该查询有效,但也会生成 2 个总成员的组,即当不满足 2 个朋友的条件时,这些组因“太小”而被忽略。
您可以运行这里查询: https://gremlify.com/lu64acieuw
查询 运行s 和输出是正确的,但请注意第 11 行和第 14 行(在 gremlify 中)搜索是相同的,为了提高性能我想调用 select( ) 回去而不是写相同的搜索,但由于这个问题的问题,这是不可能的。欢迎任何其他不编写相同搜索 2 次的技巧。
这是对它如何一步步工作的描述:
- Select 应用程序的所有用户,我们称他们为“appUser”
- Select所有appUser的好友,暂且称他们为“appUserFriend”
- Select“appUserFriend”的朋友也是“appUser”的朋友,将他们添加到数组中
- 在数组中包含“appUserFriend”和“appUser”
- 删除重复项
考虑到你写问题的方式,我假设你并不真正关心“太小”的组,并且这个问题是关于你在最后的步骤枚举中描述的算法.考虑到这个假设,我注意到您基本上是在检测三角形,然后尝试对它们进行相应的分组。 Gremlin Recipes here 中讨论了循环检测,模式基本上是:
g.V().as("a").repeat(both().simplePath()).times(2).where(both().as("a")).path()
或删除重复路径:
g.V().as("a").repeat(both().simplePath()).times(2).where(both().as("a")).path().
dedup().by(unfold().order().by(id).dedup().fold())
以此为基础,您只需将这些结果转换为您要查找的组即可。如果您发现效率更高,您可能可以在 Gremlin 之外的自己的应用程序代码中执行此操作,但是使用 Gremlin 执行此操作的一种方法是将三角形内的所有对分组,然后组合这些分组路径的元素:
g.V().as('a').
repeat(both().simplePath()).
times(2).
where(both().as('a')).
path().
map(unfold().limit(3).order().by(id).dedup().fold())
dedup().
group('m').
by(limit(local,2)).
group('m').
by(tail(local,2)).
group('m').
by(union(limit(local,1),tail(local,1)).fold()).
cap('m').
unfold().
map(select(values).unfold().unfold().order().by(id).dedup().fold()).
dedup().
map(unfold().values('name').fold())
可能还有更好的方法,但我认为这个查询至少可以避免您一次又一次地查询和重新查询相同的路径。我也认为它更容易理解,因为一旦 reader 注意到三角形计数模式,剩下的就是 free of backtracking 和很多层次结构。很好奇是否在 Gremlin 或您的应用程序代码中更好地处理三角形处理成组。这可能值得探索。
我不确定你的图表有多大,但这个特定的查询可能更适合使用 Spark 和 custom VertexProgram
, something perhaps similar to connectedComponent()
.
查询不需要 choose() 步骤,创建组时不需要检查组大小,可以稍后在创建所有组时使用 where() 步骤完成:
g.V()
.hasLabel('user')
.project("userGroups")
.by(
as("appUser")
.both("friend")
.project("group")
.by(
union(
both('friend').where(neq('appUser')).where(both('friend').where(eq('appUser'))),
identity(),
select("appUser")
).order().by("name").values("name").fold()
).select(values).fold()
).select(values)
.repeat(unfold()).times(3)
// Here the groups of 2 members are removed:
.where(count(local).is(gt(2)))
.dedup()