使用 OrientDB SQL 查找 "friends of friends"
Find "friends of friends" with OrientDB SQL
虽然这是演示图形数据库功能的最常见用例之一,但我似乎找不到使用 OrientDB "friends of friends" 获得 SQL 的良好示例或最佳实践。
让我们假设一个社交网络并尝试用 "user" 个顶点和 "is_friend_with" 个边对其建模。
定义:
Vertex class user with properties uuid (custom unique id) and name
Edge class is_friend_with with 属性 status 可以是“待定”或“已批准”
用户通过单向边相互连接。方向并不重要;只要 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" 子句 中使用它的方法
- 我被迫进行 incoming/outgoing edges/vertices 的所有组合,而不是使用 both(),因为我想检查
status="approved"
的每个边
- 此查询还 returns 直接朋友,而不是朋友的朋友
我尝试使用遍历,但无济于事(同样,没有找到一种方法如何在遍历时检查 status="approved"
的边缘)。
请问您能为这个问题提出一些 OSQL 的解决方案吗?提前致谢。
你试过吗?
select
expand(bothE('is_friend_with')[status = 'approved'].inV().bothE('is_friend_with')[status = 'approved'].inV())
from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'
可能有更好的方法,但我认为这可以满足您的需求:
设置:
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"
Enrico 的回答不会给您真正想要的,因为当它需要双向工作时,它只考虑一个方向的边缘。
编辑: 要排除用户碰巧已经是朋友的人,请使用以下内容(请注意,此示例假定用户的 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")
这应该更简单:
select expand(
bothE('is_friend_with')[status = 'approved'].bothV()
.bothE('is_friend_with')[status = 'approved'].bothV()
) from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'
使用 bothE() 和 bothV(),您可以在 edge/vertex 级别获得两个 in/out 连接。
为了排除当前用户,您可以使用:
select expand(
bothE('is_friend_with')[status = 'approved'].bothV()
.bothE('is_friend_with')[status = 'approved'].bothV()
.remove( @this )
) from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'
虽然这是演示图形数据库功能的最常见用例之一,但我似乎找不到使用 OrientDB "friends of friends" 获得 SQL 的良好示例或最佳实践。
让我们假设一个社交网络并尝试用 "user" 个顶点和 "is_friend_with" 个边对其建模。
定义:
Vertex class user with properties uuid (custom unique id) and name
Edge class is_friend_with with 属性 status 可以是“待定”或“已批准”
用户通过单向边相互连接。方向并不重要;只要 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" 子句 中使用它的方法
- 我被迫进行 incoming/outgoing edges/vertices 的所有组合,而不是使用 both(),因为我想检查
status="approved"
的每个边
- 此查询还 returns 直接朋友,而不是朋友的朋友
我尝试使用遍历,但无济于事(同样,没有找到一种方法如何在遍历时检查 status="approved"
的边缘)。
请问您能为这个问题提出一些 OSQL 的解决方案吗?提前致谢。
你试过吗?
select
expand(bothE('is_friend_with')[status = 'approved'].inV().bothE('is_friend_with')[status = 'approved'].inV())
from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'
可能有更好的方法,但我认为这可以满足您的需求:
设置:
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"
Enrico 的回答不会给您真正想要的,因为当它需要双向工作时,它只考虑一个方向的边缘。
编辑: 要排除用户碰巧已经是朋友的人,请使用以下内容(请注意,此示例假定用户的 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")
这应该更简单:
select expand(
bothE('is_friend_with')[status = 'approved'].bothV()
.bothE('is_friend_with')[status = 'approved'].bothV()
) from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'
使用 bothE() 和 bothV(),您可以在 edge/vertex 级别获得两个 in/out 连接。
为了排除当前用户,您可以使用:
select expand(
bothE('is_friend_with')[status = 'approved'].bothV()
.bothE('is_friend_with')[status = 'approved'].bothV()
.remove( @this )
) from user where uuid = '95920a96a60c4d40a8f70bde98ae1a24'