Dgraph-查詢語(yǔ)言

查詢語(yǔ)言

Dgraph的GraphQL+-是一種基于facebook的GraphQL的圖查詢語(yǔ)言。GraphQL并不是專門用于圖數(shù)據(jù)庫(kù)的,但是它像圖一樣的查詢語(yǔ)法,schema驗(yàn)證以及子圖形式的response使它成為一個(gè)非常好的語(yǔ)言選擇。我們修改了這個(gè)語(yǔ)言,并且移除了某些feature,使它可以更好地支持圖操作,更好地適用于圖數(shù)據(jù)庫(kù),并將新的語(yǔ)言命名為“GraphQL+-”

GraphQL+-還在繼續(xù)改進(jìn),我們?cè)谔砑痈嗟膄eature,將來也會(huì)簡(jiǎn)化某些已有的feature

1. GraphQL+-基礎(chǔ)

一個(gè)GraphQL+-查詢會(huì)基于查詢規(guī)范在圖中進(jìn)行匹配,并返回一個(gè)圖作為結(jié)果

查詢從查詢r(jià)oot開始,由嵌套塊組成。root會(huì)找到初始的node集合,供后面的匹配及過濾用

1.1.1 返回值

每個(gè)查詢都有名字,由查詢的root指定,查詢結(jié)果也會(huì)使用這個(gè)名字

2.Facets:邊屬性

Dgraph支持facets(邊上的鍵值對(duì)),來作為RDF三元組的擴(kuò)展。Facets可以將屬性添加到邊上,而不是節(jié)點(diǎn)上。例如,兩個(gè)人之間的friend邊可以加上close屬性,來標(biāo)識(shí)友誼是否終結(jié)。Facets也可以用作邊的權(quán)重

盡管你可能會(huì)學(xué)過很多遍facets,你還是可能會(huì)用錯(cuò)。例如你不應(yīng)該給friend邊加上date_of_birth這樣的facet,而應(yīng)該加上start_of_friendship這樣的facet

注意,F(xiàn)acet不是Dgraph的第一公民

Facet的key是string,value可以是string, bool, int, float 或者 dateTime。對(duì)于int與float,只能是32位有符號(hào)的decimial interger,以及64位單浮點(diǎn)值

下面的mutation在mobile以及car上加了名為since的facet,記錄Alice買車以及開始用這個(gè)手機(jī)號(hào)的時(shí)間

首先添加schema:

//ALTER
name: string @index(exact, term) .
rated: uid @reverse @count .

插入數(shù)據(jù):

//MUTATE
{
  set {

    # -- Facets on scalar predicates
    _:alice <name> "Alice" .
    _:alice <mobile> "040123456" (since=2006-01-02T15:04:05) .
    _:alice <car> "MA0123" (since=2006-02-02T13:01:09, first=true) .

    _:bob <name> "Bob" .
    _:bob <car> "MA0134" (since=2006-02-02T13:01:09) .

    _:charlie <name> "Charlie" .
    _:dave <name> "Dave" .


    # -- Facets on UID predicates
    _:alice <friend> _:bob (close=true, relative=false) .
    _:alice <friend> _:charlie (close=false, relative=true) .
    _:alice <friend> _:dave (close=true, relative=true) .


    # -- Facets for variable propagation
    _:movie1 <name> "Movie 1" .
    _:movie2 <name> "Movie 2" .
    _:movie3 <name> "Movie 3" .

    _:alice <rated> _:movie1 (rating=3) .
    _:alice <rated> _:movie2 (rating=2) .
    _:alice <rated> _:movie3 (rating=5) .

    _:bob <rated> _:movie1 (rating=5) .
    _:bob <rated> _:movie2 (rating=5) .
    _:bob <rated> _:movie3 (rating=5) .

    _:charlie <rated> _:movie1 (rating=2) .
    _:charlie <rated> _:movie2 (rating=5) .
    _:charlie <rated> _:movie3 (rating=1) .
  }
}

2.1 標(biāo)量謂語(yǔ)(value邊)上的facet

查詢Alice的name、mobile以及car返回的結(jié)果與沒有facet是一樣的

{  
  alice(func: eq(name,"Alice")){
     name
     mobile
     car
  }
}

語(yǔ)法@facets(facet-name)用于查詢facet數(shù)據(jù),Alice的sincefacet應(yīng)該按如下的方式查詢:

{
  data(func: eq(name, "Alice")) {
     name
     mobile @facets(since)
     car @facets(since)
  }
}

返回結(jié)果:

{
  "data": {
    "data": [
      {
        "name": "Alice",
        "mobile|since": "2006-01-02T15:04:05Z",
        "mobile": "040123456",
        "car|since": "2006-02-02T13:01:09Z",
        "car": "MA0123"
      }
    ]
  }
}

Facet會(huì)在相應(yīng)邊的統(tǒng)一level返回,key的格式是edgename|facetname

如果要查詢某個(gè)邊上的所有facet,使用@facets不加參數(shù)即可:

{
  data(func: eq(name, "Alice")) {
     name
     mobile @facets
     car @facets
  }
}

2.2 Facet的別名

在查詢某個(gè)謂語(yǔ)時(shí),可以指定別名,格式類似于請(qǐng)求其他謂語(yǔ)的別名。orderascorderdesc不能用作別名,因?yàn)樗鼈冇刑貏e意義,其他的任何字符串都能用作別名

在這里,我們給分別給since, close兩個(gè)facet設(shè)置別名car_since, close_friend

{
   data(func: eq(name, "Alice")) {
     name
     mobile
     car @facets(car_since: since)
     friend @facets(close_friend: close) {
       name
     }
   }
}

2.3 UID謂語(yǔ)(uid邊)的facet

UID邊上的域名類似于value邊

例如,friend是有一個(gè)名為close的facet的邊

查詢friend邊的close facet:

{
   data(func: eq(name, "Alice")) {
     name
     friend @facets(close) {
       name
     }
   }
}

對(duì)于像friend這種的uid邊,facet會(huì)進(jìn)入edge|facet下面相應(yīng)的子節(jié)點(diǎn)。上面的例子中你會(huì)看到,Alice與Bob之間的friend|close與Bob的結(jié)果出現(xiàn)在一起:

{
  "data": {
    "data": [
      {
        "name": "Alice",
        "friend": [
          {
            "name": "Charlie",
            "friend|close": false
          },
          {
            "name": "Dave",
            "friend|close": true
          },
          {
            "name": "Bob",
            "friend|close": true
          }
        ]
      }
    ]
  }
}

Bob有一輛車,并且這個(gè)車有一個(gè)名為since的facet,在下面的例子中我們可以看到car|since也是在Bob的屬性里:

{
  data(func: eq(name, "Alice")) {
    name
    friend @facets {
      name
      car @facets
    }
  }
}
{
  "data": {
    "data": [
      {
        "name": "Alice",
        "friend": [
          {
            "name": "Charlie",
            "friend|close": false,
            "friend|relative": true
          },
          {
            "name": "Dave",
            "friend|close": true,
            "friend|relative": true
          },
          {
            "name": "Bob",
            "car|since": "2006-02-02T13:01:09Z",
            "car": "MA0134",
            "friend|close": true,
            "friend|relative": false
          }
        ]
      }
    ]
  }
}

2.4 在Facet上過濾

Dgraph支持基于facet過濾邊,過濾方式類似于沒有facet但是有與facet同名的函數(shù)

找到Alice已經(jīng)close的friend:

{
  data(func: eq(name, "Alice")) {
    friend @facets(eq(close, true)) {
      name
    }
  }
}

如果要在filter的同時(shí)返回facet,再加上 @facets(<facetname>)即可:

{
  data(func: eq(name, "Alice")) {
    friend @facets(eq(close, true)) @facets(relative) { # filter close friends and give relative status
      name
    }
  }
}

Facet查詢也可以使用AND, OR 以及 NOT組合起來:

{
  data(func: eq(name, "Alice")) {
    friend @facets(eq(close, true) AND eq(relative, true)) @facets(relative) { # filter close friends in my relation
      name
    }
  }
}

2.5 使用facet排序

可以使用uid邊上的facet排序。下面我們把Alice、Bob以及Charlie各自對(duì)電影的評(píng)分對(duì)電影進(jìn)行排序:

{
  me(func: anyofterms(name, "Alice Bob Charlie")) {
    name
    rated @facets(orderdesc: rating) {
      name
    }
  }
}

2.6 將facet的值賦給變量

UID邊上的facet可以以邊到facet值的映射的格式保存到值變量里

{
  var(func: eq(name, "Alice")) {
    friend @facets(a as close, b as relative)
  }

  friend(func: uid(a)) {
    name
    val(a)
  }

  relative(func: uid(b)) {
    name
    val(b)
  }
}

2.7 Facet與值傳播

Facet的int、float類型的值可以被賦給變量,這就是值傳播(values propagate

Alice、Bob以及Charlie每個(gè)人都對(duì)每個(gè)電影評(píng)了分,一個(gè)facetrating上的值變量保存了從電影到評(píng)分的映射。查詢可以從多條路徑到達(dá)一個(gè)電影,并將每條路徑上的評(píng)分加起來。下面的查詢就把Alice、Bob、Charlie對(duì)三個(gè)電影的評(píng)分加起來了:

{
  var(func: anyofterms(name, "Alice Bob Charlie")) {
    num_raters as math(1)
    rated @facets(ra as rating) {     #這里ra直接就是三人對(duì)每部電影的評(píng)分總和map
         #total_rating as math(ra)    total_rating跟ra時(shí)等價(jià)的
        avg_rating as math(ra/num_raters)S
    }
  }    

  data(func: uid(ra)) {
    name
    total_rating : val(ra)
    avg_rating : val(avg_rating)
  }
}

2.8 Facet與聚合

賦給值變量的Facet是可以被聚合的

{
  data(func: eq(name, "Alice")) {
    name
    rated @facets(r as rating) {
      name
    }
    avg(val(r))
  }
}

注意,因?yàn)閞是每部電影到其總評(píng)分的映射,所以下面的想要分別計(jì)算Alice與Bob各自對(duì)所有電影的平均評(píng)分的查詢語(yǔ)句是錯(cuò)誤的:

{
  data(func: anyofterms(name, "Alice Bob")) {
    name
    rated @facets(r as rating) {
      name
    }
    avg(val(r))
  }
}

計(jì)算每個(gè)用戶對(duì)電影的平均評(píng)分需要一個(gè)映射用戶到他們各自的評(píng)分的變量:

{
  var(func: has(~rated)) {
    num_rated as math(1)
    ~rated @facets(r as rating) {#關(guān)鍵是這里,反向邊可以獲得每個(gè)人對(duì)所有電影的總評(píng)分
      avg_rating as math(r / num_rated)
    }
  }

  data(func: uid(avg_rating)) {
    name
    val(avg_rating)
  }
}

3. K-最短路徑查詢

4. 遞歸查詢

遞歸查詢讓你遍歷一系列謂語(yǔ)(通過filter、facet等),直到到達(dá)所有葉子節(jié)點(diǎn),或者到達(dá)depth參數(shù)寫的最大深度

為了獲得某個(gè)有30000部電影的題材下的十部電影,并獲取這些電影中的兩個(gè)演員,我們可以做如下查詢:

{
    me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 1, loop: true) {
        uid
        name@en
        ~genre (first:3) @filter(gt(count(starring), 2)) #十個(gè)太多,三個(gè)
        starring (first: 2)
        performance.actor
    }
}
#這個(gè)查詢的root是個(gè)genre,但是下面的starring、performance.actor顯然不是genre的謂語(yǔ),這就是遞歸的意思

使用遞歸查詢時(shí)注意:

  • 在root之后只能指定一層謂語(yǔ)。這些會(huì)被遞歸查詢。標(biāo)量與節(jié)點(diǎn)都會(huì)被當(dāng)作類似的
  • 每個(gè)查詢只建議有一個(gè)遞歸block
  • 小心結(jié)果集可能會(huì)快速膨脹,如果結(jié)果集太大的話,會(huì)報(bào)錯(cuò)。在這種情況下使用更多的filter、使用分頁(yè)限制條數(shù),或者像上面一樣提供一個(gè)深度參數(shù)

上面的遞歸查詢?cè)赿epth不同的時(shí)候,返回結(jié)果時(shí)不同的

  1. depth=1,查詢到genre
{
  "data": {
    "me": [
      {
        "uid": "0x1de841",
        "name@en": "Drama"
      }
    ]
  }
}
  1. depth=2,genre->movie
{
  "data": {
    "me": [
      {
        "uid": "0x1de841",
        "name@en": "Drama",
        "~genre": [
          {
            "uid": "0x17",
            "name@en": "House of Boys"
          },
          {
            "uid": "0x1e",
            "name@en": "U raskoraku"
          },
          {
            "uid": "0x38",
            "name@en": "I Want You"
          }
        ]
      }
    ]
  }
}
  1. depth=3,genre->movie->starring
{
  "data": {
    "me": [
      {
        "uid": "0x1de841",
        "name@en": "Drama",
        "~genre": [
          {
            "uid": "0x17",
            "name@en": "House of Boys",
            "starring": [
              {
                "uid": "0x1622f"
              },
              {
                "uid": "0x38fa9"
              }
            ]
          },
          {
            "uid": "0x1e",
            "name@en": "U raskoraku",
            "starring": [
              {
                "uid": "0x2a1d57"
              },
              {
                "uid": "0x2f7e3a"
              }
            ]
          },
          {
            "uid": "0x38",
            "name@en": "I Want You",
            "starring": [
              {
                "uid": "0xa6775"
              },
              {
                "uid": "0xad95f"
              }
            ]
          }
        ]
      }
    ]
  }
}
  1. depth=4,genre->movie->starring->performance.actor
{
  "data": {
    "me": [
      {
        "uid": "0x1de841",
        "name@en": "Drama",
        "~genre": [
          {
            "uid": "0x17",
            "name@en": "House of Boys",
            "starring": [
              {
                "uid": "0x1622f",
                "performance.actor": [
                  {
                    "uid": "0x5352a1",
                    "name@en": "Marco Wirges"
                  }
                ]
              },
              {
                "uid": "0x38fa9",
                "performance.actor": [
                  {
                    "uid": "0x2c7490",
                    "name@en": "Mohamed Moulouaa"
                  }
                ]
              }
            ]
          },
          {
            "uid": "0x1e",
            "name@en": "U raskoraku",
            "starring": [
              {
                "uid": "0x2a1d57",
                "performance.actor": [
                  {
                    "uid": "0x1d9bd8",
                    "name@en": "Danilo Stojkovi?"
                  }
                ]
              },
              {
                "uid": "0x2f7e3a",
                "performance.actor": [
                  {
                    "uid": "0x836ab7",
                    "name@en": "Jovan-Burdus Janicijevic"
                  }
                ]
              }
            ]
          },
          {
            "uid": "0x38",
            "name@en": "I Want You",
            "starring": [
              {
                "uid": "0xa6775",
                "performance.actor": [
                  {
                    "uid": "0x97dd22",
                    "name@en": "Antonio Velázquez"
                  }
                ]
              },
              {
                "uid": "0xad95f",
                "performance.actor": [
                  {
                    "uid": "0x29824",
                    "name@en": "Cristina Plazas"
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}
  1. depth=5,genre->movie->starring->performance.actor,查詢結(jié)果跟depth=4一樣

5. Fragments

fragment關(guān)鍵詞允許你定義新的可以被查詢引用的fragment,像GraphQL specification一樣。如果有多個(gè)部分需要查詢相同的字段,可以定義一個(gè)fragment,并多次調(diào)用它。Fragment可以被嵌套,但是不可以組成環(huán)。示例如下:

curl localhost:8080/query -XPOST -d $'
query {
  debug(func: uid(1)) {
    name@en
    ...TestFrag
  }
}
fragment TestFrag {
  initial_release_date
  ...TestFragB
}
fragment TestFragB {
  country
}' | python -m json.tool | less

6. GraphQL變量

7. 用自定義Tokenizers分詞

8. 函數(shù)

9. 連接Filter

10. 別名

11. 分頁(yè)

12. Count

13. 排序

14. 多查詢塊

15. 查詢變量

16. 值變量

17. 聚合

18. 值變量上的Math

19. GroupBy

20. Expand Predicates

21. Cascade命令

22. Normalize命令

23. Debug

24. Schema

對(duì)每個(gè)謂語(yǔ)來說,schema指定它的目標(biāo)的類型。如果一個(gè)謂語(yǔ)p的類型為T,那么對(duì)于所有的主-謂-賓三元組,賓語(yǔ)的類型都是T

  • 在mutation時(shí),會(huì)檢查標(biāo)量類型,并且在當(dāng)這個(gè)值不能轉(zhuǎn)為schema里的類型時(shí)會(huì)報(bào)錯(cuò)
  • 在查詢時(shí),值類型會(huì)根據(jù)schema中謂語(yǔ)的類型返回

如果在執(zhí)行插入一個(gè)triple的mutation時(shí)沒有在schema中添加謂語(yǔ)類型,那么會(huì)根據(jù)第一個(gè)mutation推斷類型,這些類型包括:

  • uid,如果某個(gè)謂語(yǔ)的第一個(gè)mutation的主語(yǔ)跟賓語(yǔ)都是node
  • 根據(jù)rdf type派生,如果賓語(yǔ)是一個(gè)字面量,而且第一個(gè)mutation中有rdf type
  • 其他的都是default類型

24.1 Schema Type

Dgraph支持標(biāo)量類型,以及uid類型

24.1.1 標(biāo)量類型

對(duì)于所有的謂語(yǔ)是標(biāo)量類型的三元組,賓語(yǔ)是字面量

Dgraph Type Go type
default string
int int64
float float
string string
bool bool
dateTime time.Time (RFC3339 format [Optional timezone] eg: 2006-01-02T15:04:05.999999999+10:00 or 2006-01-02T15:04:05.999999999)
geo go-geom
password string (encrypted)

24.1.2 UID類型

uid類型表示一個(gè)node到node的邊,在Dgraph內(nèi)部,每個(gè)node都用一個(gè)uint64類型的id表示

Dgraph Type Go type
uid uint64

24.2 添加或修改Schema

Schema mutation可以添加或者修改schema

如果某個(gè)謂語(yǔ)you多個(gè)標(biāo)量值也可以通過指定一個(gè)類型list,使用一個(gè)S P添加。下面這個(gè)例子中的occupations可以為每個(gè)S P保存一個(gè)字符串list

可以使用@index指定索引類型,并且可以通過參數(shù)指定tokenizer。當(dāng)給一個(gè)謂語(yǔ)指定index之后,必須給這個(gè)index指定類型。例如:

name: string @index(exact, fulltext) @count .
age: int @index(int) .
friend: uid @count .
dob: dateTime .
location: geo @index(geo) .
occupations: [string] @index(term) .    #occupations的值是一個(gè)string數(shù)組

如果某個(gè)謂語(yǔ)沒存數(shù)據(jù),一個(gè)schema mutation會(huì)建立一個(gè)空的schema準(zhǔn)備接收三元組。

如果在mutation之前已經(jīng)有數(shù)據(jù)了,已有的數(shù)據(jù)不會(huì)被核對(duì)來遵從新的schema。但是在查詢時(shí),Dgraph會(huì)試圖將已有的數(shù)據(jù)轉(zhuǎn)化為新的schema中的類型,并且會(huì)忽略任何轉(zhuǎn)換錯(cuò)誤

忽略錯(cuò)誤好像會(huì)導(dǎo)致查詢時(shí)出現(xiàn)某些很異常的問題,比如某些謂語(yǔ)會(huì)丟失

如果已經(jīng)有數(shù)據(jù),但是制定了新的index類型,那么任何原有的但是不在更新后的index類型列表中的inde都會(huì)被丟棄,新的index類型會(huì)自動(dòng)被創(chuàng)建

如果在schema mutation的時(shí)候指定了反向邊,它也會(huì)被計(jì)算出來

24.3 RDF類型

在mutation的時(shí)候,Dgraph支持多種RDF類型

除了在執(zhí)行第一個(gè)mutation的時(shí)候隱式推斷類型,RDF類型還可以覆蓋保存的schema

如果一個(gè)謂語(yǔ)在schema中一個(gè)類型,而一個(gè)mutation中的RDF含有不同Dgraph底層數(shù)據(jù)類型,轉(zhuǎn)換到schema中的type時(shí)拋出了不兼容的異常,但是值被存儲(chǔ)為RDF中的類型相應(yīng)的Dgraph類型。查詢結(jié)果通常會(huì)以schema中的類型返回

例如,如果age這個(gè)謂語(yǔ)沒有在schema中設(shè)置類型,執(zhí)行下面的mutation:

{
 set {
  _:a <age> "15"^^<xs:int> .
  _:b <age> "13" .
  _:c <age> "14"^^<xs:string> .
  _:d <age> "14.5"^^<xs:string> .
  _:e <age> "14.5" .
 }
}

然后查詢:

{
    fgggg(func: has(age)){
    expand(_all_)
  }
}

報(bào)錯(cuò):

: strconv.ParseInt: parsing "14.5": invalid syntax

Dgraph:

  • 根據(jù)第一個(gè)三元組的隱式轉(zhuǎn)換,將schema類型設(shè)置為int
  • 將保存的13載存儲(chǔ)中轉(zhuǎn)換成int
  • 檢查發(fā)現(xiàn)14可以被轉(zhuǎn)換成int,但是會(huì)按string存儲(chǔ)
  • 最后兩個(gè)三元組會(huì)報(bào)錯(cuò),因?yàn)?code>14.5不能被轉(zhuǎn)換成int

24.4 擴(kuò)展類型

24.4.1 Password類型

可以在schema中使用password類型給一個(gè)實(shí)體設(shè)置密碼。不能直接查詢密碼,只能使用checkpwd函數(shù)來判斷一個(gè)密碼是否匹配

首先,定義schema:

//alter
pass: password .

然后設(shè)置密碼:

#mutate
{
  set {
    <0x123> <name> "Password Example"
    <0x123> <pass> "ThePassword" .
  }
}

檢查密碼:

#query
{
  check(func: uid(0x123)) {
    name
    checkpwd(pass, "ThePassword")
  }
}

輸出:

{
  "check": [
    {
      "name": "Password Example",
      "pass": [
        {
          "checkpwd": true
        }
      ]
    }
  ]
}

24.5 索引

當(dāng)使用函數(shù)進(jìn)行過濾的時(shí)候,Dgraph會(huì)使用索引讓大數(shù)據(jù)集的索引更高效

所有的標(biāo)量類型都可以被索引

int, float, bool 以及 geo 都只有一個(gè)默認(rèn)索引類型,tokenizers的名字依次為 int, float, boolgeo

而string與dateTime類型有多個(gè)索引類型

24.5.1 string索引

string類型有如下索引類型:

Dgraph function Required index / tokenizer Notes
eq hash, exact, term, or fulltext 對(duì)于 eq 函數(shù)來說性能最好的索引類型是 hash. 只有相似需要eq的同時(shí)還需要全文檢索的時(shí)候,再考慮使用 termfulltext 索引。如果已經(jīng)在用term了,那就無(wú)需再用hashexact
le, ge, lt, gt exact 允許快速排序
allofterms, anyofterms term 允許根據(jù)語(yǔ)句中的一個(gè)項(xiàng)進(jìn)行查詢
alloftext, anyoftext fulltext 通過指定的詞干以及停止詞匹配語(yǔ)言
regexp trigram 正則表達(dá)式匹配,也可以用于相等驗(yàn)證

警告

不正確的index選擇可能會(huì)極大地增加性能負(fù)載,并且提高事務(wù)沖突的概率。盡量只使用最少的、最簡(jiǎn)單的索引類型

24.5.2 DateTime索引

下列索引類型適用于dateTime類型:

Index name / Tokenizer Part of date indexed
year 對(duì)年建索引(默認(rèn))
month 對(duì)年、月建索引
day 對(duì)年、月、日建索引
hour 對(duì)年、月、日、小時(shí)建索引

24.5.3 可排序索引

不是所有的索引都會(huì)給所有的值建立一個(gè)排序??膳判虻乃饕试S執(zhí)行不等函數(shù)以及排序

  • intfloat索引是可排序的
  • string類型的exact索引是可排序的
  • 所有的dateTime索引是可排序的

例如,對(duì)于string類型的name邊,為了對(duì)name進(jìn)行排序以對(duì)它執(zhí)行不等過濾,必須指定exact索引類型。這種情況下schema查詢將返回至少返回下面的內(nèi)容:

{
  "predicate": "name",
  "type": "string",
  "index": true,
  "tokenizer": [
    "exact"
  ]
}

24.5.4 Count索引

對(duì)于帶有@count的謂語(yǔ),Dgraph會(huì)對(duì)每個(gè)節(jié)點(diǎn)的出邊數(shù)建立索引。這可以使下面這種查詢變的更快:

{
  q(func: gt(count(pred), threshold)) {
    ...
  }
}

24.5 List類型

標(biāo)量類型的謂語(yǔ)也可以存儲(chǔ)值list。標(biāo)量類型需要使用[]包起來,表明它是一個(gè)list類型,這些list使無(wú)序的集合

occupations: [string] .
score: [int] .
  • set操作可以向list中添加一個(gè)值。值的順序不能保證
  • delete操作可以從list中刪除一個(gè)值
  • 對(duì)這些謂語(yǔ)的查詢將按數(shù)組的格式返回list
  • 索引可以被應(yīng)用于值為list的謂語(yǔ),你也可以在它們上執(zhí)行函數(shù)
  • 這些謂語(yǔ)不能進(jìn)行排序

24.6 反向邊

圖的邊是單向的。對(duì)于節(jié)點(diǎn)到節(jié)點(diǎn)的邊,有時(shí)需要構(gòu)建反向。只要某個(gè)主-謂-賓三元組需要一個(gè)反向邊,手動(dòng)添加即可。如果某個(gè)謂語(yǔ)都會(huì)有一個(gè)反向,只要在schema中指定@reverse,Dgraph就會(huì)自動(dòng)計(jì)算出反向邊

anEdge的反向邊是~anEdge

對(duì)于已有的數(shù)據(jù),Dgraph會(huì)計(jì)算所有的反向邊。對(duì)于在schema修改之后新添加的數(shù)據(jù),Dgraph會(huì)為每個(gè)新加的三元組計(jì)算并添加反向邊

24.7 查詢schema

查詢所有的schema:

schema { }

查詢特定的字段:

schema {
  type
  index
  reverse
  tokenizer
}

查詢特定謂語(yǔ):

schema {
  type
  index
  reverse
  tokenizer
}

25. Mutation

添加或移除數(shù)據(jù)被稱作mutation

一個(gè)添加三元組的mutation使用set關(guān)鍵詞添加數(shù)據(jù):

{
  set {
    # triples in here
  }
}

25.1 Triple

輸入的語(yǔ)言是遵循W3C格式的 RDF N-Quad format三元組

triple的格式如下:

<subject> <predicate> <object> 

意味著以subject為標(biāo)志的圖節(jié)點(diǎn)通過有向邊predicate連接到object。每個(gè)triple以一個(gè)句號(hào)(full stop)結(jié)尾。triple中的subject一定是圖中的一個(gè)節(jié)點(diǎn),而object可以是一個(gè)node也可以是一個(gè)value

例如,下面的triple:

<0x01> <name> "Alice" .

表示uid為0x01的節(jié)點(diǎn)有一個(gè)string類型的name,值為“Alice”。而下面的triple:

<0x01> <friend> <0x02> .

表示uid為0x01的節(jié)點(diǎn)通過friend邊連接到0x02

Dgraph會(huì)為每個(gè)節(jié)點(diǎn)創(chuàng)建一個(gè)唯一的64位識(shí)別符——uid。一個(gè)mutation要么使用空節(jié)點(diǎn)或外部id節(jié)點(diǎn)(blank or external id nodes)讓Dgraph為subject或者object創(chuàng)建uid,或者指定一個(gè)之前的mutation創(chuàng)建的uid

25.2 空節(jié)點(diǎn)與uid

mutation中的空節(jié)點(diǎn)寫作_:identifier,標(biāo)志mutation內(nèi)部的節(jié)點(diǎn)。Dgraph會(huì)為每個(gè)空節(jié)點(diǎn)創(chuàng)建一個(gè)uid,并將它作為mutation的結(jié)果返回。例如,下面的mutation:

{
 set {
    _:class <student> _:x .
    _:class <student> _:y .
    _:class <name> "awesome class" .
    _:x <name> "Alice" .
    _:x <planet> "Mars" .
    _:x <friend> _:y .
    _:y <name> "Bob" .
 }
}

返回的結(jié)果:

{
  "data": {
    "code": "Success",
    "message": "Done",
    "uids": {
      "class": "0x2712",
      "x": "0x2713",
      "y": "0x2714"
    }
  }
}

圖被更新后,就仿佛儲(chǔ)存如下的triple:

<0x6bc818dc89e78754> <student> <0xc3bcc578868b719d> .
<0x6bc818dc89e78754> <student> <0xb294fb8464357b0a> .
<0x6bc818dc89e78754> <name> "awesome class" .
<0xc3bcc578868b719d> <name> "Alice" .
<0xc3bcc578868b719d> <planet> "Mars" .
<0xc3bcc578868b719d> <friend> <0xb294fb8464357b0a> .
<0xb294fb8464357b0a> <name> "Bob" .

空節(jié)點(diǎn)標(biāo)簽_:class、_:x以及_:在mutation執(zhí)行之后不能再涌來識(shí)別節(jié)點(diǎn),也不會(huì)存儲(chǔ)在dgraph中,下一個(gè)mutation一樣可以復(fù)用它們

后續(xù)的mutation可以更新已有的uid的數(shù)據(jù)。例如,下面的mutation向class中添加了一個(gè)新學(xué)生:

{
 set {
    <0x6bc818dc89e78754> <student> _:x .
    _:x <name> "Chris" .
 }
}

25.3 外部id

Dgraph的輸入語(yǔ)言是RDF,它還支持<a_fixed_identifier> <predicate> literal/node格式的triple,這里的標(biāo)簽a_fixed_identifier會(huì)被當(dāng)作這個(gè)node的唯一識(shí)別符。例如,混合schema.org識(shí)別符、the movie database識(shí)別符以及空節(jié)點(diǎn):

_:userA <http://schema.org/type> <http://schema.org/Person> .
_:userA <http://schema.org/name> "FirstName LastName" .
<https://www.themoviedb.org/person/32-robin-wright> <http://schema.org/type> <http://schema.org/Person> .
<https://www.themoviedb.org/person/32-robin-wright> <http://schema.org/name> "Robin Wright" .

0.8版本的Dgraph沒有原生支持這樣的外部id作為節(jié)點(diǎn)的識(shí)別符,外部id可以以xid邊的形式作為node的屬性存儲(chǔ)下來。例如,上面的謂語(yǔ)在Dgraph中是有效的,但是節(jié)點(diǎn)http://schema.org/Person在Dgraph中是以一個(gè)uid來識(shí)別的,比方說0x123,它會(huì)有如下的一個(gè)邊:

<0x123> <xid> "http://schema.org/Person" .

而Robin Wright可能有uid0x321,以及triple:

<0x321> <xid> "https://www.themoviedb.org/person/32-robin-wright" .
<0x321> <http://schema.org/type> <0x123> .
<0x321> <http://schema.org/name> "Robin Wright" .

一個(gè)合適的schema可能是:

xid: string @index(exact) .
<http://schema.org/type>: uid @reverse .

查詢舉例:

{
  var(func: eq(xid, "http://schema.org/Person")) {
    allPeople as <~http://schema.org/type>
  }

  q(func: uid(allPeople)) {
    <http://schema.org/name>
  }
}

查詢舉例,通過外部id查找Eobin Wright:

{
  robin(func: eq(xid, "https://www.themoviedb.org/person/32-robin-wright")) {
    expand(_all_) { expand(_all_) }
  }
}

注意 xid邊不會(huì)在mutation的時(shí)候自動(dòng)添加。用戶需要自己判斷某個(gè)xid是否存在,并添加節(jié)點(diǎn)及xid

25.4 語(yǔ)言與RDF類型

RDF N-Quad允許給一個(gè)字符串值指定語(yǔ)言類型以及一個(gè)RDF類型,語(yǔ)言通過@lang指定,例如:

<0x01> <name> "Adelaide"@en .
<0x01> <name> "Аделаида"@ru .
<0x01> <name> "Adéla?de"@fr .

RDF類型通過標(biāo)準(zhǔn)的^^分隔符附在字面量上,像這樣:

<0x01> <age> "32"^^<xs:int> .
<0x01> <birthdate> "1985-06-08"^^<xs:dateTime> .

支持的RDF datatypes以及相關(guān)的內(nèi)部類型以及相關(guān)的內(nèi)部類型如下:

Storage Type Dgraph type
<xs:string> string
<xs:dateTime> dateTime
<xs:date> datetime
<xs:int> int
<xs:boolean> bool
<xs:double> float
<xs:float> float
geo:geojson geo
http://www.w3.org/2001/XMLSchema#string string
http://www.w3.org/2001/XMLSchema#dateTime dateTime
http://www.w3.org/2001/XMLSchema#date dateTime
http://www.w3.org/2001/XMLSchema#int int
http://www.w3.org/2001/XMLSchema#boolean bool
http://www.w3.org/2001/XMLSchema#double float
http://www.w3.org/2001/XMLSchema#float float

參閱章節(jié)RDF schema types來理解RDF類型如何影響mutation與storage

25.5 批量mutation

每個(gè)mutation都可能包含多個(gè)RDF三元組,對(duì)于大的數(shù)據(jù)上傳操作,這些mutation可以批量、并發(fā)執(zhí)行。工具dgraph-live-loader就是用于干這個(gè)的,默認(rèn)是每批1000行RDF,同時(shí)并發(fā)100個(gè)

Dgraphloader以一個(gè)gzip壓縮格式的N-Quad文件(不含{ set {的三元組列表),具體查看Bulk Data Loading

25.6 刪除

刪除操作,是用delete關(guān)鍵字標(biāo)志的,它可以從存儲(chǔ)中移除三元組

例如,如果存儲(chǔ)中含有:

<0xf11168064b01135b> <name> "Lewis Carrol"
<0xf11168064b01135b> <died> "1998"

那么刪除的mutation:

{
  delete {
     <0xf11168064b01135b> <died> "1998" .
  }
}

刪除不需要的數(shù)據(jù),并將它從索引中移除(如果有索引的話)

對(duì)于刪除語(yǔ)句S P *,會(huì)將節(jié)點(diǎn)N所有謂語(yǔ)P的數(shù)據(jù)(以及相關(guān)索引)都移除

{
  delete {
     <0xf11168064b01135b> <author.of> * .
  }
}

對(duì)于語(yǔ)句S * *,會(huì)刪除節(jié)點(diǎn)S所有的出邊(但是,節(jié)點(diǎn)自己可能會(huì)作為某些邊的終點(diǎn)留存下來),另外被刪除的邊相關(guān)的任意反向邊,還有被刪除的數(shù)據(jù)的所有索引也都會(huì)被刪除

{
  delete {
     <0xf11168064b01135b> * * .
  }
}

注意,* P O* * O 是不被支持的,因?yàn)檎业剿械娜脒吿y了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Take a Tour Dgraph的GraphQL+-是一種基于facebook的GraphQL的圖查詢語(yǔ)言。G...
    羽_da59閱讀 4,009評(píng)論 2 3
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,279評(píng)論 2 89
  • 正文之前 本文有很多的參考來源,就不一一列舉了。除了少部分自己寫的,其他的都是從別的地方拼湊來的。但求不噴,不圖啥...
    張照博閱讀 37,218評(píng)論 3 31
  • 人是一種奇怪的生物,他具有其它物種所沒有的敏感情緒。別人的一舉手、一投足,或是一句話、一個(gè)表情都也可能使我們?cè)酒?..
    迷迭香榭閱讀 651評(píng)論 0 0
  • 寫在前面 新文來啦!和之前的文區(qū)別會(huì)比較大,一直都想寫一個(gè)現(xiàn)實(shí)一點(diǎn)的東西,算是一次新的嘗試吧。大概是隨著年齡的增長(zhǎng)...
    作者熄歌閱讀 538評(píng)論 0 5

友情鏈接更多精彩內(nèi)容