Gremlin 小精灵:调用fold()或count()后无法返回上一步

Gremlin 小精灵:调用fold()或count()后无法返回上一步,gremlin,tinkerpop,tinkerpop3,gremlin-server,Gremlin,Tinkerpop,Tinkerpop3,Gremlin Server,此查询不返回任何内容,因为调用fold()将删除所有先前存储的as(): 当然,我总是可以以牺牲性能为代价来解决此类问题,通过搜索两次来重复成本,我正在寻找比这更好的解决方案 用store()替换as()也会产生不同的输出,因此这也不是解决方案。store()保留到fold(),但使用相同的字符串多次调用会将每个调用添加到列表中,as()将第一个调用替换为第二个调用,而不是使用相同的工具 你可以自己试试: 编辑: 更接近我真实查询的示例如下: g.V() .hasLabel('user') .p

此查询不返回任何内容,因为调用fold()将删除所有先前存储的as():

当然,我总是可以以牺牲性能为代价来解决此类问题,通过搜索两次来重复成本,我正在寻找比这更好的解决方案

用store()替换as()也会产生不同的输出,因此这也不是解决方案。store()保留到fold(),但使用相同的字符串多次调用会将每个调用添加到列表中,as()将第一个调用替换为第二个调用,而不是使用相同的工具

你可以自己试试:

编辑:

更接近我真实查询的示例如下:

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个朋友的条件时,这些组将被忽略,因为“太小”

您可以在此处运行查询:

查询运行,输出正确,但请注意第11行和第14行(在gremlify中)中的搜索是相同的,为了提高性能,我想调用select()返回,而不是编写相同的搜索,但由于这个问题,这是不可能的。任何其他不写两次相同搜索的技巧都是受欢迎的

以下是对其工作原理的逐步说明:

  • 选择应用程序的所有用户,我们称他们为“appUser”
  • 选择所有appUser的朋友,让我们称他们为“appUserFriend”
  • 选择“appUserFriend”的好友,这些好友也是“appUser”的好友,并将它们添加到数组中
  • 在数组中包含“appUserFriend”和“appUser”
  • 删除重复项

  • 考虑到你写问题的方式,我假设你并不真的关心“太小”的群体,这个问题是关于你在最后的步骤枚举中描述的算法。考虑到这个假设,我注意到你基本上是在检测三角形,然后尝试将它们相应地分组。周期检测在Gremlin配方中进行了讨论,模式基本上是:

    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之外的应用程序代码中这样做,但使用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())
    
    也许还有更好的方法,但我认为这个查询至少可以避免您一次又一次地查询相同的路径。我还认为它更容易理解,因为一旦读者注意到三角形计数模式,其余的就是三角形计数模式和很多层次结构。很想知道在Gremlin或应用程序代码中是否更好地处理三角形分组处理。这可能值得探索


    我不确定您的图形有多大,但这个特定的查询可能更适合使用Spark和a进行OLAP风格的处理,可能类似于。

    查询不需要选择()步骤,在创建组时不需要检查组大小,稍后可以使用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()
    

    路径历史记录在减少步骤(如
    fold()
    )后丢失,因此通常必须重新编写遍历以避免这种情况。在这种情况下,我不确定如何最好地建议如何重新编写它,因为这个示例感觉有点做作。结果应该是什么?“对于每个用户,如果有多个顶点,则返回该用户,否则返回所有顶点?”也许您可以添加更多详细信息和数据创建脚本我更新了我的问题,现在您有了更真实的查询,以便可以帮助我
    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()