2015-02-23 91 views
1

雖然是演示圖形數據庫功能的最常見用例之一,但我似乎無法找到一個使用OrientDB SQL獲取「朋友的朋友」的好例子或最佳做法。使用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」子句中使用它
  • 我不得不使用both(),因爲我要檢查每一個邊緣的status="approved"
  • 此查詢還返回直接的朋友,而不是朋友的朋友只有

我試圖用移動,但無濟於事(同樣,沒有發現一種方法如何在遍歷時檢查邊緣的status="approved")。

請問您能爲這個問題提出一些OSQL解決方案嗎?提前致謝。

回答

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" 

Enrico's a nswer將不會給你相當多的東西,因爲它只需要在一個方向上考慮邊緣,當它需要雙向工作時。

編輯:要排除人用戶恰好是朋友已經使用以下(請注意,此示例假設用戶的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") 
+0

感謝您的回答,@ codemix。它工作正常,比我的解決方案更優雅。但是,查詢還會返回我們不感興趣的直接朋友(我通過執行創建邊緣IsFriendsWith(從用戶選擇uuid =「2」)(從用戶選擇uuid =「6」)來嘗試此操作。設置狀態=「批准」)。你可能有一個想法如何篩選出直接的朋友,並只返回建議的朋友(朋友的朋友)? – Bart 2015-02-24 12:35:10

+0

@Zlatko查看我對我的答案所做的修改。 – codemix 2015-02-24 14:05:28

+0

我沒有嘗試,但現在看起來不錯。由於我想遠離@rid,我想我會用'select from uuid =「xxx」'來重寫查詢。雖然會有重複,但索引會受到影響,所以這不會成爲問題。 (你可能知道如何將** select **分配給一個變量,並在**子句的某些**中重用它,如果可能的話)。非常感謝,@ codemix,我會接受這個答案。 – Bart 2015-02-24 14:30:41

0

你試過嗎?

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

感謝您的例子。正如@codemix所指出的那樣,inV()僅限制一個方向上的「遍歷」。 – Bart 2015-02-24 12:39:19

1

這應該是簡單得多:

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

隨着博特(),然後bothV()你在邊緣/頂點水平/輸出連接同時獲得。

爲了排除當前用戶可以使用:

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