使用OrientDB SQL查找“朋友的朋友”

时间:2015-02-23 22:57:39

标签: orientdb

虽然用于演示图形数据库功能的最常见用例之一,但我似乎找不到使用OrientDB SQL获取“朋友的朋友”的好例子或最佳实践。

让我们假设一个社交网络并尝试使用“用户”顶点和“is_friend_with”边缘对其进行建模。


定义

顶级类用户,其属性为 uuid (自定义唯一ID)和名称

边缘类 is_friend_with ,属性状态,可以是“待定”或“已批准

用户使用单向边缘相互连接。方向并不重要;只要status =“approved”,这两个用户就是朋友。


这是我提出的一个解决方案:

select from (
    select expand($all) let
        $a = (select expand(outE('is_friend_with')[status='approved'].inV('user').outE('is_friend_with')[status='approved'].inV('user')) from (select from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24')),
        $b = (select expand(outE('is_friend_with')[status='approved'].inV('user').inE('is_friend_with')[status='approved'].outV('user')) from (select from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24')),
        $c = (select expand(inE('is_friend_with')[status='approved'].outV('user').inE('is_friend_with')[status='approved'].outV('user')) from (select from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24')),
        $d = (select expand(inE('is_friend_with')[status='approved'].outV('user').outE('is_friend_with')[status='approved'].inV('user')) from (select from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24')),
        $all = unionall($a, $b, $c, $d)
) where uuid <> '95920a96a60c4d40a8f70bde98ae1a24'

(使用uuid ='95920a96a60c4d40a8f70bde98ae1a24'的用户是起点。)

但是,我发现它并不优雅。我可以立即发现的一些问题是:

  • 重复select from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'。不幸的是,我找不到将其分配给变量的方法,然后在“from”子句
  • 中使用它
  • 我被迫制作传入/传出边/顶点的所有组合,而不是同时使用两个(),因为我想检查status="approved"
  • 的每个边缘
  • 此查询还会返回直接的朋友,而不仅仅是朋友的朋友

我尝试使用遍历,但无济于事(再次,在遍历时没有找到如何检查status="approved"边缘的方法。)

请您为此问题提出一些OSQL解决方案吗?提前谢谢。

3 个答案:

答案 0 :(得分:2)

可能有更好的方法可以做到,但我认为这可以为您提供您所寻找的内容:

<强>设定:

create class User extends V
create class IsFriendsWith extends E
create property User.name string
create property User.uuid string
create property IsFriendsWith.status string

create vertex User set uuid = "1", name = "Bob"
create vertex User set uuid = "2", name = "Sally"
create vertex User set uuid = "3", name = "Eric"
create vertex User set uuid = "4", name = "Jenny"
create vertex User set uuid = "5", name = "Dennis"
create vertex User set uuid = "6", name = "Mary"
create vertex User set uuid = "7", name = "John"

create edge IsFriendsWith from (select from User where uuid = "1") to (select from User where uuid = "2") set status = "approved"
create edge IsFriendsWith from (select from User where uuid = "1") to (select from User where uuid = "3") set status = "pending"
create edge IsFriendsWith from (select from User where uuid = "2") to (select from User where uuid = "4") set status = "approved"
create edge IsFriendsWith from (select from User where uuid = "5") to (select from User where uuid = "2") set status = "pending"
create edge IsFriendsWith from (select from User where uuid = "3") to (select from User where uuid = "4") set status = "approved"
create edge IsFriendsWith from (select from User where uuid = "6") to (select from User where uuid = "1") set status = "approved"
create edge IsFriendsWith from (select from User where uuid = "6") to (select from User where uuid = "7") set status = "approved"

<强>查询:

select from (
    select 
      expand(unionall(
        outE("IsFriendsWith")[status="approved"].inV(), 
        inE("IsFriendsWith")[status="approved"].outV()
      ))
      from (
        select 
            expand(unionall(
              outE("IsFriendsWith")[status="approved"].inV(), 
              inE("IsFriendsWith")[status="approved"].outV()
            ))
        from (
          select from User where uuid = "1"
        )
      )
  )
) where uuid <> "1"

恩里科的回答并不能完全解决你所追求的问题,因为只有当它需要双向工作时,它才会考虑到一个方向的边缘。

修改:要排除用户恰好是朋友的人,请使用以下内容(请注意,此示例假定用户的RID为#26:0

  select from (
      select 
        expand(unionall(
          outE("IsFriendsWith")[status="approved"].inV(), 
          inE("IsFriendsWith")[status="approved"].outV()
        ))
        from (
          select 
              expand(unionall(
                outE("IsFriendsWith")[status="approved"].inV(), 
                inE("IsFriendsWith")[status="approved"].outV()
              ))
          from #26:0          
        )
    )
  )
  where @rid <> #26:0 and @rid NOT IN (select both("IsFriendsWith") from #26:0)

编辑2:改为使用变量。

 select from (
      select 
        expand(unionall(
          outE("IsFriendsWith")[status="approved"].inV(), 
          inE("IsFriendsWith")[status="approved"].outV()
        ))
        from (
          select 
              expand(unionall(
                outE("IsFriendsWith")[status="approved"].inV(), 
                inE("IsFriendsWith")[status="approved"].outV()
              ))
          from (
             select expand($u) let $u = first((select from User where uuid = "1"))
          )
        )
    )
  )
  where @rid <> $u and @rid NOT IN $u.both("IsFriendsWith")

答案 1 :(得分:1)

这应该更简单:

select expand(
  bothE('is_friend_with')[status = 'approved'].bothV()
  .bothE('is_friend_with')[status = 'approved'].bothV()
) from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'

使用()和然后两个(),您将获得边/顶级别的输入/输出连接。

为了排除当前用户,您可以使用:

select expand(
  bothE('is_friend_with')[status = 'approved'].bothV()
  .bothE('is_friend_with')[status = 'approved'].bothV()
  .remove( @this )
) from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'

答案 2 :(得分:0)

你试过这个吗?

select 
      expand(bothE('is_friend_with')[status = 'approved'].inV().bothE('is_friend_with')[status = 'approved'].inV()) 
from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'