JSON 变更格式 (JSON Mutation Format)

你也能够通过JSON对象指定一个变更。这可以让变更以更自然的方式表达出来。它还消除了应用程序自定义序列化代码的需要,因为大多数语言已经有了JSON处理库。

Dgraph接收到JSON对象的变更时,它首先将其转换为内部边格式,然后再将其处理为Dgraph

对于JSON

JSON -> Edges -> Posting list

对于RDF

RDF -> Edges -> Posting list

每个JSON对象表示图中的单个节点。

注意:JSON变更可以通过Go客户端、JavaScript客户端和Java客户端等gRPC客户端使用,也可以通过dgraph-js-httpcURLHTTP客户端使用。在这里可以看到更多关于cURL的信息。

设置字面量值

当设置新值时,变更消息中的set_json字段应该包含一个JSON对象。

字面量值可以直接通过向JSON对象设置key/value来进行。key将表示谓词(predicate),value将表示对象。

例如:

{
  "name": "diggy",
  "food": "pizza",
  "dgraph.type": "Mascot"
}

上面JSON对象将被转化为如下RDF

_:blank-0 <name> "diggy" .
_:blank-0 <food> "pizza" .
_:blank-0 <dgraph.type> "Mascot" .

变更的返回结果是一个Map,该Map具有一个uid,这个uid对应于键blank-0。你也可以像下面这样指定你自己的uid键:

{
  "uid": "_:diggy",
  "name": "diggy",
  "food": "pizza",
  "dgraph.type": "Mascot"
}

在这种情况下,分配的uid映射将有一个名为diggy的键,其值是分配给它的uid

禁止值 (Forbidden values)

使用JSON提交变更时,uid()val() 这两个字符串是不允许使用的。

例如:Dgraph不能处理以下提交的变更:

{
  "uid": "uid(t)",
  "user_name": "uid(s ia)",
  "name": "val (s kl)",
}

多语言支持 (Language Support)

RDF变更和JSON变更之间的一个重要区别在于指定字符串值的语言。在JSON中,语言标记被附加到边名,而不是像RDF那样的值。

例如,下面是一个使用JSON提交的变更:

{
  "food": "taco",
  "rating@en": "tastes good",
  "rating@es": "sabe bien",
  "rating@fr": "c'est bon",
  "rating@it": "è buono",
  "dgraph.type": "Food"
}

作为对比,使用RDF提交相同的变更需要:

_:blank-0 <food> "taco" .
_:blank-0 <dgraph.type> "Food" .
_:blank-0 <rating> "tastes good"@en .
_:blank-0 <rating> "sabe bien"@es .
_:blank-0 <rating> "c'est bon"@fr .
_:blank-0 <rating> "è buono"@it .

地理位置支持 (Geolocation support)

JSON变更支持地理位置数据。地理位置数据以JSON对象的形式输入,带有typecoordinates键。记住,我们只支持在PointPolygonMultiPolygon类型上建立索引,但是我们可以存储其他类型的地理位置数据。下面是一个例子:

{
  "food": "taco",
  "location": {
    "type": "Point",
    "coordinates": [1.0, 2.0]
  }
}

引用已存在的节点 (Referencing existing nodes)

如果JSON对象包含一个名为uid的字段,那么该字段将被解释为图中现有节点的uid。这种机制允许您引用现有的节点。

例如:

{
  "uid": "0x467ba0",
  "food": "taco",
  "rating": "tastes good",
  "dgraph.type": "Food"
}

上面的JSON对象将被转化为以下RDF

<0x467ba0> <food> "taco" .
<0x467ba0> <rating> "tastes good" .
<0x467ba0> <dgraph.type> "Food" .

节点之间的边 (Edges between nodes)

节点之间的边以类似于字面量值的方式表示,只是对象是一个JSON对象:

{
  "name": "Alice",
  "friend": {
    "name": "Betty"
  }
}

上面的JSON对象将会被转化为:

_:blank-0 <name> "Alice" .
_:blank-0 <friend> _:blank-1 .
_:blank-1 <name> "Betty" .

变更的结果将包含分配给blank-0blank-1节点的uid。如果您想在不同的键下返回这些uid,您可以将uid字段指定为一个空白节点:

{
  "uid": "_:alice",
  "name": "Alice",
  "friend": {
    "uid": "_:bob",
    "name": "Betty"
  }
}

上面的JSON对象将会被转化为:

_:alice <name> "Alice" .
_:alice <friend> _:bob .
_:bob <name> "Betty" .

可以使用与添加字面量值相同的方式引用现有节点。例如连接两个现有节点:

{
  "uid": "0x123",
  "link": {
    "uid": "0x456"
  }
}

将会被转化为:

<0x123> <link> <0x456> .

注意:一个常见的错误是尝试使用{"uid":"0x123","link":"0x456"}。这将导致一个错误:Dgraph将这个JSON对象解释为将链接谓词设置为字符串0x456,这通常不是你希望的。

删除字面量值 (Deleting literal values)

JSON也能提交删除变更。

要发送删除变更,在变更消息中使用delete_json字段而不是set_json字段。

注意:如果您使用dgraph-js-http客户端或Ratel UI,请检查JSON语法使用原始HTTPRatel UI部分。

在使用删除变更时,始终必须引用现有节点。因此,必须提供每个JSON对象的uid字段。应该删除的谓词应该设置为JSONnull

例如,要删除食物评级:

{
  "uid": "0x467ba0",
  "rating": null
}

删除边

删除一条边需要创建该边的相同JSON对象。例如,删除谓词链接0x1230x456

{
  "uid": "0x123",
  "link": {
    "uid": "0x456"
  }
}

从单个节点发出的谓词(predicate)的所有边都可以一次删除(相当于删除S P *):

{
  "uid": "0x123",
  "link": null
}

如果没有指定谓词,则删除节点的所有已知出站边(到其他节点和到字面量值)(对应于删除S * *)。要删除的谓词是使用类型系统派生的。有关更多信息,请参阅RDF格式文档类型系统部分:

{
  "uid": "0x123"
}

Facets

可以通过使用|字符来分隔JSON对象字段名中的谓词和facet键来创建facet。这与用于在查询结果中显示facet的编码模式相同。如:

{
  "name": "Carol",
  "name|initial": "C",
  "dgraph.type": "Person",
  "friend": {
    "name": "Daryl",
    "friend|close": "yes",
    "dgraph.type": "Person"
  }
}

等价于下面的RDF

_:blank-0 <name> "Carol" (initial="C") .
_:blank-0 <dgraph.type> "Person" .
_:blank-0 <friend> _:blank-1 (close="yes") .
_:blank-1 <name> "Daryl" .
_:blank-1 <dgraph.type> "Person" .

facet不包含类型信息,但是Dgraph将尝试从输入猜测类型。如果一个facet的值可以被解析为一个数字,那么它将被转换为floatint。如果它可以被解析为一个布尔值,那么它将被存储为一个布尔值。如果值是字符串,如果该字符串与Dgraph能识别的时间格式(YYYY, MM-YYYY, DD-MM-YYYY, RFC339等)中的一种相匹配,那么它将被存储为datetime,否则将被存储为双引号字符串。如果不想冒facet数据被误读为时间值的风险,最好将数值数据存储为intfloat类型。

删除 Facets

删除Facet最简单的方法是覆盖它。当您为没有facet的相同实体创建一个新的变更时,现有的facet将被自动删除:

<0x1> <name> "Carol" .
<0x1> <friend> <0x2> .

另一种方法是使用Upsert Block

在下面的查询中,我们将删除NameFriend谓词中的Facet。要覆盖,我们需要收集执行此操作的边的值,并使用val(var)函数来完成覆盖:

curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
upsert {
  query {
    user as var(func: eq(name, "Carol")){
      Name as name
      Friends as friend
    }
  }

  mutation {
    set {
      uid(user) <name> val(Name) .
      uid(user) <friend> uid(Friends) .
    }
  }
}' | jq

使用 JSON创建一个List然后对这个List操作:

Schema:

testList: [string] .

testList中添加一些数据:

{
  "testList": [
    "Grape",
    "Apple",
    "Strawberry",
    "Banana",
    "watermelon"
  ]
}

现在让我们把Apple从这个列表中删除(注意,List中的项是区分大小写的):

{
  q(func: has(testList)) {
    uid
    testList
  }
}
{
  "delete": {
    "uid": "0x6", #UID of the list.
    "testList": "Apple"
  }
}

当然,你也能够一次性删除多个值:

{
  "delete": {
    "uid": "0x6",
    "testList": [
      "Strawberry",
      "Banana",
      "watermelon"
    ]
  }
}

注意:如果您使用dgraph-js-http客户端或Ratel UI,请检查JSON语法使用原始HTTPRatel UI部分。

添加一个水果:

{
   "uid": "0x6", #UID of the list.
   "testList": "Pineapple"
}

JSON 与 List 类型的 facet

schema:

<name>: string @index(exact).
<nickname>: [string] .

要创建list类型的谓词(predicate),需要指定单个列表中的所有值。所有谓词值的facet应该一起指定。它是用map格式完成的,列表中的谓词值的索引是map键,它们各自的facet值是map值。不包含facet值的谓词值将从facet映射中丢失。如:

{
  "set": [
    {
      "uid": "_:Julian",
      "name": "Julian",
      "nickname": ["Jay-Jay", "Jules", "JB"],
      "nickname|kind": {
        "0": "first",
        "1": "official",
        "2": "CS-GO"
      }
    }
  ]
}

在上面您可以看到,我们有三个值,它们各自具有方面,可以进入列表。你可以运行这个查询来检查包含facet的列表:

{
  q(func: eq(name,"Julian")) {
  uid
  nickname @facets
  }
}

之后如果希望使用facet添加更多值,只需执行相同的过程,但现在必须使用相应节点的UID,而不是使用空白节点:

{
  "set": [
    {
      "uid": "0x3",
      "nickname|kind": "Internet",
      "nickname": "@JJ"
    }
  ]
}

最后返回的结果:

{
  "data": {
    "q": [
      {
        "uid": "0x3",
        "nickname|kind": {
          "0": "first",
          "1": "Internet",
          "2": "official",
          "3": "CS-GO"
        },
        "nickname": [
          "Jay-Jay",
          "@JJ",
          "Jules",
          "JB"
        ]
      }
    ]
  }
}

指定多个操作

当指定添加或删除变更时,可以使用JSON数组同时指定多个节点。

例如,下面的JSON对象可以用来添加两个新节点,每个节点都有一个名称:

[
  {
    "name": "Edward"
  },
  {
    "name": "Fredric"
  }
]

使用 Raw HTTPRatel UI 解析 JSON

这种语法可以在最新版本的Ratel中使用,也可以在dgraph-js-http客户端中使用,甚至可以通过cURL使用。

您也可以下载Ratel UI

变更:

{
  "set": [
    {
      # One JSON obj in here
    },
    {
      # Another JSON obj in here for multiple operations
    }
  ]
}

删除: 删除操作与删除字面量值和删除边相同。

{
  "delete": [
    {
      # One JSON obj in here
    },
    {
      # Another JSON obj in here for multiple operations
    }
  ]
}

通过 cURL 使用 JSON 操作

首先,你需要设置 HTTP 相应的请求头为 application/json

-H 'Content-Type: application/json'

注意:为了使用jq进行JSON格式化,你需要jq包。有关安装细节,请参阅jq下载页面。你也可以使用Python的内置json工具模块,使用python -m json

curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'
    {
      "set": [
        {
          "name": "Alice"
        },
        {
          "name": "Bob"
        }
      ]
    }' | jq

删除操作:

curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'
    {
      "delete": [
        {
          "uid": "0xa"
        }
      ]
    }' | jq

或者你也可以提交一个在文件中的变更:

curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d @data.json

data.json 中文件内容类似于:

{
  "set": [
    {
      "name": "Alice"
    },
    {
      "name": "Bob"
    }
  ]
}

对于HTTP上的变更,JSON文件必须遵循相同的格式:一个带有setdelete键的JSON对象和一个用于变更的JSON对象数组。如果您已经拥有一个包含数据数组的文件,则可以使用jq将数据转换为适当的格式。例如,如果你的Json文件看起来像这样:

[
  {
    "name": "Alice"
  },
  {
    "name": "Bob"
  }
]

然后可以使用以下jq命令将数据转换为适当的格式,其中.jq '{set: .}'中表示data.json的内容:

cat data.json | jq '{set: .}'
{
  "set": [
    {
      "name": "Alice"
    },
    {
      "name": "Bob"
    }
  ]
}