在Dgraph中添加或删除数据称为mutation。

三元组

set关键字完成一个增加三元组的mutation。

  1. {
  2. set {
  3. # triples in here
  4. }
  5. }

输入语言为W3C标准RDF N-Quad格式的三元组。(绝对不是标准的!译者注)

每个三元组具有以下形式

  1. <subject> <predicate> <object> .

意味着由 subject 标识的图节点通过有向边 predicate 链接到 object。每个三元组以一个句号结尾。三元组的 subject 始终是图中的一个节点,而object 可以是节点或值(字面量)。

例如,这个三元组

  1. <0x01> <name> "Alice" .
  2. <0x01> <dgraph.type> "Person" .

表示ID为 0x01 的图节点的 name 具有字符串类型的值“Alice”。

这个三元组

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

表示ID为 0x01 的图节点通过 friend 边链接到节点 0x02

Dgraph为mutation中的每个空白节点创建唯一的64位标识符 - 节点的UID。mutation可以包括空白节点作为对象或对象的标识符,或者来自先前mutation的已知UID。

空白节点和UID

Blank nodes in mutations, written _:identifier, identify nodes within a mutation. Dgraph creates a UID identifying each blank node and returns the created UIDs as the mutation result. For example, mutation: mutation中的空白节点,写为 _:identifier,标识mutation中的节点。 Dgraph为每个空白节点创建一个UID,并返回创建的UID作为mutation结果。 例如,mutation:

  1. {
  2. set {
  3. _:class <student> _:x .
  4. _:class <student> _:y .
  5. _:class <name> "awesome class" .
  6. _:class <dgraph.type> "Class" .
  7. _:x <name> "Alice" .
  8. _:x <dgraph.type> "Person" .
  9. _:x <dgraph.type> "Student" .
  10. _:x <planet> "Mars" .
  11. _:x <friend> _:y .
  12. _:y <name> "Bob" .
  13. _:y <dgraph.type> "Person" .
  14. _:y <dgraph.type> "Student" .
  15. }
  16. }

结果输出(在此mutation的每次运行后,实际的UID都会有所不同)

  1. {
  2. "data": {
  3. "code": "Success",
  4. "message": "Done",
  5. "uids": {
  6. "class": "0x2712",
  7. "x": "0x2713",
  8. "y": "0x2714"
  9. }
  10. }
  11. }

该图已更新,就好像它存储了这些三元组一样

  1. <0x6bc818dc89e78754> <student> <0xc3bcc578868b719d> .
  2. <0x6bc818dc89e78754> <student> <0xb294fb8464357b0a> .
  3. <0x6bc818dc89e78754> <name> "awesome class" .
  4. <0x6bc818dc89e78754> <dgraph.type> "Class" .
  5. <0xc3bcc578868b719d> <name> "Alice" .
  6. <0xc3bcc578868b719d> <dgraph.type> "Person" .
  7. <0xc3bcc578868b719d> <dgraph.type> "Student" .
  8. <0xc3bcc578868b719d> <planet> "Mars" .
  9. <0xc3bcc578868b719d> <friend> <0xb294fb8464357b0a> .
  10. <0xb294fb8464357b0a> <name> "Bob" .
  11. <0xb294fb8464357b0a> <dgraph.type> "Person" .
  12. <0xb294fb8464357b0a> <dgraph.type> "Student" .

空白节点标签_:class_:x_:y 不能标识mutation后的节点,可以安全地重用以标识之后的mutation中的新节点。

以后的mutation可以更新现有UID的数据。 例如,以下将新学生添加到班级

  1. {
  2. set {
  3. <0x6bc818dc89e78754> <student> _:x .
  4. _:x <name> "Chris" .
  5. _:x <dgraph.type> "Person" .
  6. _:x <dgraph.type> "Student" .
  7. }
  8. }

查询也可以直接使用UID

  1. {
  2. class(func: uid(0x6bc818dc89e78754)) {
  3. name
  4. student {
  5. name
  6. planet
  7. friend {
  8. name
  9. }
  10. }
  11. }
  12. }

外部ID

Dgraph’s input language, RDF, also supports triples of the form <a_fixed_identifier> <predicate> literal/node and variants on this, where the label a_fixed_identifier is intended as a unique identifier for a node. For example, mixing schema.org identifiers, the movie database identifiers and blank nodes: Dgraph的输入语言RDF也支持 <a_fixed_identifier> <predicate> literal/node 形式的三元组和其上的变变种,其中标签 a_fixed_identifier 旨在作为节点的唯一标识符。例如,混合使用schema.org标识符、电影数据标识符和空白节点:

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

由于Dgraph本身不支持诸如节点标识符之类的外部ID。相反,可以将外部ID存储为具有 xid 边的节点的属性。例如,上面三元组,谓词名称在Dgraph中有效,但是用 <http://schema.org/Person> 标识的节点可以在Dgraph中标识为UID 0x123 和边。

  1. <0x123> <xid> "http://schema.org/Person" .
  2. <0x123> <dgraph.type> "ExternalType" .

虽然 Robin Wright 可能会获得UID 0x321 和三元组

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

schema应该如下

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

查询示例:所有人

  1. {
  2. var(func: eq(xid, "http://schema.org/Person")) {
  3. allPeople as <~http://schema.org/type>
  4. }
  5. q(func: uid(allPeople)) {
  6. <http://schema.org/name>
  7. }
  8. }

查询示例:通过外部ID查询Robin Wright。

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

注意 xid边不会自动添加到mutation中。通常,用户有责任检查现有的xid,并在必要时添加节点和xid边。 Dgraph将此类xid的唯一性检查留给了外部程序。

外部ID和Upsert块

upsert块使管理外部ID变得容易

设置schema

  1. xid: string @index(exact) .
  2. <http://schema.org/name>: string @index(exact) .
  3. <http://schema.org/type>: [uid] @reverse .

首先设置类型

  1. {
  2. set {
  3. _:blank <xid> "http://schema.org/Person" .
  4. _:blank <dgraph.type> "ExternalType" .
  5. }
  6. }

现在,您可以创建一个新person,并使用upsert块附上其类型。

  1. upsert {
  2. query {
  3. var(func: eq(xid, "http://schema.org/Person")) {
  4. Type as uid
  5. }
  6. var(func: eq(<http://schema.org/name>, "Robin Wright")) {
  7. Person as uid
  8. }
  9. }
  10. mutation {
  11. set {
  12. uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" .
  13. uid(Person) <http://schema.org/type> uid(Type) .
  14. uid(Person) <http://schema.org/name> "Robin Wright" .
  15. uid(Person) <dgraph.type> "Person" .
  16. }
  17. }
  18. }

您还可以删除一个person节点并分离type和person节点之间的关系。与上面相同,但您使用的是关键字 “delete” 而不是 “set”。 “http://schema.org/Person” 将保留,但 “Robin Wright” 将被删除。

  1. upsert {
  2. query {
  3. var(func: eq(xid, "http://schema.org/Person")) {
  4. Type as uid
  5. }
  6. var(func: eq(<http://schema.org/name>, "Robin Wright")) {
  7. Person as uid
  8. }
  9. }
  10. mutation {
  11. delete {
  12. uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" .
  13. uid(Person) <http://schema.org/type> uid(Type) .
  14. uid(Person) <http://schema.org/name> "Robin Wright" .
  15. uid(Person) <dgraph.type> "Person" .
  16. }
  17. }
  18. }

按person查询

  1. {
  2. q(func: eq(<http://schema.org/name>, "Robin Wright")) {
  3. uid
  4. xid
  5. <http://schema.org/name>
  6. <http://schema.org/type> {
  7. uid
  8. xid
  9. }
  10. }
  11. }

多语言和RDF类型

RDF N-Quad允许为字符串值和RDF类型指定一种语言。语言是使用@lang编写的。例如

  1. <0x01> <name> "Adelaide"@en .
  2. <0x01> <name> "Аделаида"@ru .
  3. <0x01> <name> "Adélaïde"@fr .
  4. <0x01> <dgraph.type> "Person" .

另请参阅查询中如何处理语言字符串

使用标准的^^分隔符将RDF类型附加到文字上。例如

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

支持的RDF数据类型和存储数据的相应内部类型如下。

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

请参阅有关RDF schema 类型的部分,以了解RDF类型如何影响mutation和存储。

批量mutation

每个mutation可能包含多个RDF三元组。对于大数据加载,可以并行处理许多此类mutation。 dgraph live命令就是这样做的。默认情况下,将1000条RDF行批处理到一个查询中,同时并行运行100条这样的查询。

dgraph live takes as input gzipped N-Quad files (that is triple lists without { set {) and batches mutations for all triples in the input. The tool has documentation of options. dgraph live将gzip压缩的N-Quad文件(即没有{set {的三元组列表)作为输入,并对输入中所有三元组进行批量mutation。该工具具有options文档。

  1. dgraph live --help

另请参阅[快速数据加载]。 See also Bulk Data Loading.

删除

delete关键字表示的delete mutation将从存储中删除三元组。

例如,如果存储中包含以下内容:

  1. <0xf11168064b01135b> <name> "Lewis Carrol"
  2. <0xf11168064b01135b> <died> "1998"
  3. <0xf11168064b01135b> <dgraph.type> "Person" .

然后,以下delete mutation将删除指定的错误数据,并将其从所有索引中删除:

  1. {
  2. delete {
  3. <0xf11168064b01135b> <died> "1998" .
  4. }
  5. }

通配符删除

在许多情况下,您将需要删除谓词的多种数据。对于特定的节点N,谓词P的所有数据(以及所有对应的索引)都以模式S P *删除。

  1. {
  2. delete {
  3. <0xf11168064b01135b> <author.of> * .
  4. }
  5. }

模式S * *从节点中删除所有已知边,与已删除边相对应的任何反向边以及已删除数据的任何索引。

注意 对于适合`S 模式的mutation,仅删除与给定节点关联的类型(使用dgraph.type)中的谓词。S ` delete mutation后,所有与该节点类型之一不匹配的谓词都将保留。

  1. {
  2. delete {
  3. <0xf11168064b01135b> * * .
  4. }
  5. }

如果删除模式S * *中的节点S仅具有由dgraph.type定义的类型的少数谓词,则仅删除具有类型谓词的那些三元组。 S * * delete mutation后,包含无类型谓词的节点仍将存在。

注意 不支持模式` P O O`,因为它们存储和查找所有传入边的效率很低。

删除非列表谓词

删除非列表谓词的值(即一对一关系)可以通过两种方式完成。

  • 使用上一节中提到的通配符删除(星号)。
  • 将对象设置为特定值。如果传递的值不是当前值,则该突变将成功但不会生效。如果传递的值是当前值,则该mutation将成功并删除非列表谓词。

对于带有语言标签的值,支持以下特殊语法:

  1. {
  2. delete {
  3. <0x12345> <name@es> * .
  4. }
  5. }

在此示例中,使用es语言标记的名称字段的值被删除。其他标记的值保持不变。

具有RDF的列表类型的Facets

Schema:

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

在RDF中创建包含facets的列表非常简单。

  1. {
  2. set {
  3. _:Julian <name> "Julian" .
  4. _:Julian <nickname> "Jay-Jay" (kind="first") .
  5. _:Julian <nickname> "Jules" (kind="official") .
  6. _:Julian <nickname> "JB" (kind="CS-GO") .
  7. }
  8. }
  1. {
  2. q(func: eq(name,"Julian")){
  3. name
  4. nickname @facets
  5. }
  6. }

结果:

  1. {
  2. "data": {
  3. "q": [
  4. {
  5. "name": "Julian",
  6. "nickname|kind": {
  7. "0": "first",
  8. "1": "official",
  9. "2": "CS-GO"
  10. },
  11. "nickname": [
  12. "Jay-Jay",
  13. "Jules",
  14. "JB"
  15. ]
  16. }
  17. ]
  18. }
  19. }

使用CURL进行mutation

可以通过向Alpha的/mutate端点发出POST请求来进行mutation。在命令行上,可以使用curl来完成。要提交mutation,请在URL中传递参数commitNow=true

运行 set mutation:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. {
  3. set {
  4. _:alice <name> "Alice" .
  5. _:alice <dgraph.type> "Person" .
  6. }
  7. }'

运行 delete mutation:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. {
  3. delete {
  4. # Example: The UID of Alice is 0x56f33
  5. <0x56f33> <name> * .
  6. }
  7. }'

要运行存在文件中的RDF mutation,请使用curl的—data-binary选项,与-d选项不同的是,该数据不经过URL编码。

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true --data-binary @mutation.txt

JSON mutation 格式

也可以使用JSON对象指定mutation。这可以使mutation以更自然的方式表达。由于大多数语言已经具有JSON编组库,因此也消除了应用程序具有自定义序列化代码的需要。

当Dgraph接收到作为JSON对象的mutation时,它首先转换为多个RDF,然后按常规进行处理。

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

注意 JSON mutation可通过gRPC客户端(例如Go客户端,JS客户端和Java客户端)获得,并且可用于具有dgraph-js-http和cURL的HTTP客户端。在此处查看有关cURL的更多信息

设置文字值

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

可以通过将key/value添加到JSON对象来设置文字值。key代表谓词,value代表对象。

例如:

  1. {
  2. "name": "diggy",
  3. "food": "pizza",
  4. "dgraph.type": "Mascot"
  5. }

将转换为RDF:

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

mutation的结果还将包含一个映射,该映射将分配与键blank-0相对应的uid。您可以指定自己的密钥,例如

  1. {
  2. "uid": "_:diggy",
  3. "name": "diggy",
  4. "food": "pizza"
  5. }

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

多语言支持

RDF和JSON mutation之间的重要区别在于指定字符串值的语言。在JSON中,语言标签会附加到边名称中,而不是像RDF中那样附加到值中。

例如,JSON mutation:

  1. {
  2. "food": "taco",
  3. "rating@en": "tastes good",
  4. "rating@es": "sabe bien",
  5. "rating@fr": "c'est bon",
  6. "rating@it": "è buono",
  7. "dgraph.type": "Food"
  8. }

等效于以下RDF:

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

Geo-location支持

JSON中提供了对地理位置数据的支持。Geo-location数据作为JSON对象输入,其键为“type”和“coordinates”。请记住,我们仅支持对Point,Polygon和MultiPolygon类型进行索引,但是我们可以存储其他类型的地理位置数据。下面是一个示例:

  1. {
  2. "food": "taco",
  3. "location": {
  4. "type": "Point",
  5. "coordinates": [1.0, 2.0]
  6. }
  7. }

引用现有节点

如果JSON对象包含一个名为"uid"的字段,则该字段将被解释为图形中现有节点的UID。此机制使您可以引用现有节点。

例如:

  1. {
  2. "uid": "0x467ba0",
  3. "food": "taco",
  4. "rating": "tastes good",
  5. "dgraph.type": "Food"
  6. }

将转换为RDF:

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

节点之间的边

节点之间的边以与文字值相似的方式表示,只是对象变成了JSON对象。

例如:

  1. {
  2. "name": "Alice",
  3. "friend": {
  4. "name": "Betty"
  5. }
  6. }

将转换为RDF:

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

mutation的结果将包含分配给blank-0blank-1节点的uid。如果要在其他键下返回这些uid,则可以将uid字段指定为空白节点。

  1. {
  2. "uid": "_:alice",
  3. "name": "Alice",
  4. "friend": {
  5. "uid": "_:bob",
  6. "name": "Betty"
  7. }
  8. }

将转换为:

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

现有节点的引用方式与添加文字值时相同。例如,链接两个现有节点:

  1. {
  2. "uid": "0x123",
  3. "link": {
  4. "uid": "0x456"
  5. }
  6. }

将转换为:

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

注意 常见的错误是尝试使用{"uid":"0x123","link":"0x456"}。这将导致错误。 Dgraph将此JSON对象解释为将link谓词设置为字符串"0x456",这通常并是不想要的。

删除文字值

删除mutation也可以JSON格式发送。要发送删除mutation,请使用delete_json字段而不是mutation消息中的set_json字段。

注意 如果您使用的是dgraph-js-http客户端或Ratel UI,请查看使用Raw HTTP或Ratel UI的JSON语法部分。

使用删除mutation时,始终必须引用现有节点。因此,必须存在每个JSON对象的"uid"字段。应该删除的谓词应设置为JSON值null。

例如,要删除food rating:

  1. {
  2. "uid": "0x467ba0",
  3. "rating": null
  4. }

删除边

删除单个边需要与创建该边相同的JSON对象。例如,删除link链接从"0x123""0x456"

  1. {
  2. "uid": "0x123",
  3. "link": {
  4. "uid": "0x456"
  5. }
  6. }

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

  1. {
  2. "uid": "0x123",
  3. "link": null
  4. }

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

  1. {
  2. "uid": "0x123"
  3. }

Facets

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

  1. {
  2. "name": "Carol",
  3. "name|initial": "C",
  4. "dgraph.type": "Person",
  5. "friend": {
  6. "name": "Daryl",
  7. "friend|close": "yes",
  8. "dgraph.type": "Person"
  9. }
  10. }

产生以下RDF:

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

Facets不包含类型信息,但Dgraph将尝试从输入中猜测类型。如果Facets的值可以解析为数字,则它将转换为浮点数或整数。如果可以将其解析为布尔值,则将其存储为布尔值。如果该值为字符串,则如果该字符串与Dgraph可以识别的一种时间格式(YYYY,MM-YYYY,DD-MM-YYYY,RFC339等)匹配,则它将被存储为日期时间,否则为字符串。如果您不想冒将Facets数据误解为时间值的风险,最好将数字数据存储为int或float。

删除Facets

删除Facets的最简单方法是覆盖它。当您为没有Facets的同一实体创建新mutation时,现有Facets将被自动删除。

例如:

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

另一种方法是使用Upsert块。 在下面的查询中,我们将删除NameFriend谓词中的Facets。要覆盖,我们需要收集执行此操作的边的值,并使用函数val(var)来完成覆盖。

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. user as var(func: eq(name, "Carol")){
  5. Name as name
  6. Friends as friend
  7. }
  8. }
  9. mutation {
  10. set {
  11. uid(user) <name> val(Name) .
  12. uid(user) <friend> uid(Friends) .
  13. }
  14. }
  15. }' | jq

使用JSON创建列表并与之交互

Schema:

  1. testList: [string] .
  1. {
  2. "testList": [
  3. "Grape",
  4. "Apple",
  5. "Strawberry",
  6. "Banana",
  7. "watermelon"
  8. ]
  9. }

然后,从此列表中删除"Apple"(请记住,区分大小写):

  1. {
  2. q(func: has(testList)) {
  3. uid
  4. testList
  5. }
  6. }
  1. {
  2. "delete": {
  3. "uid": "0x6", #列表的UID
  4. "testList": "Apple"
  5. }
  6. }

您也可以删除多个值

  1. {
  2. "delete": {
  3. "uid": "0x6",
  4. "testList": [
  5. "Strawberry",
  6. "Banana",
  7. "watermelon"
  8. ]
  9. }
  10. }

注意 如果您使用的是dgraph-js-http客户端或Ratel UI,请查看使用Raw HTTP或Ratel UI的JSON语法部分。

添加另一种水果:

  1. {
  2. "uid": "0xd", #列表的UID
  3. "testList": "Pineapple"
  4. }

具有JSON的列表类型的Facets

Schema:

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

要创建列表类型谓词,您需要在一个列表中指定所有值。所有谓词值的Facets应一起指定。它以map格式完成,列表内的谓词值索引是map键,其各自的Facets值作为map值。没有Facets值的谓词值将从Facets映射中丢失。例如:

  1. {
  2. "set": [
  3. {
  4. "uid": "_:Julian",
  5. "name": "Julian",
  6. "nickname": ["Jay-Jay", "Jules", "JB"],
  7. "nickname|kind": {
  8. "0": "first",
  9. "1": "official",
  10. "2": "CS-GO"
  11. }
  12. }
  13. ]
  14. }

在上面您会看到我们有三个值来输入带有各自Facets的列表。您可以运行此查询来检查包含Facets的列表:

  1. {
  2. q(func: eq(name,"Julian")) {
  3. uid
  4. nickname @facets
  5. }
  6. }

之后,如果要使用Facets添加更多值,只需执行相同的过程,但是这次必须使用实际节点的UID来代替空白节点。

  1. {
  2. "set": [
  3. {
  4. "uid": "0x3",
  5. "nickname|kind": "Internet",
  6. "nickname": "@JJ"
  7. }
  8. ]
  9. }

最终结果是:

  1. {
  2. "data": {
  3. "q": [
  4. {
  5. "uid": "0x3",
  6. "nickname|kind": {
  7. "0": "first",
  8. "1": "Internet",
  9. "2": "official",
  10. "3": "CS-GO"
  11. },
  12. "nickname": [
  13. "Jay-Jay",
  14. "@JJ",
  15. "Jules",
  16. "JB"
  17. ]
  18. }
  19. ]
  20. }
  21. }

指定多个操作

当指定添加或删除mutation时,可以使用JSON数组同时指定多个节点。 例如,以下JSON对象可用于添加两个新节点,每个节点都有一个name

  1. [
  2. {
  3. "name": "Edward"
  4. },
  5. {
  6. "name": "Fredric"
  7. }
  8. ]

使用Raw HTTP或Ratel UI的JSON语法

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

您也可以下载适用于Linux,macOS或Windows的Ratel UI

Mutate:

  1. {
  2. "set": [
  3. {
  4. # 这里有一个JSON对象
  5. },
  6. {
  7. # 此处的另一个JSON对象用于多个操作
  8. }
  9. ]
  10. }

删除:

删除操作与删除文字值删除边相同。

Deletion operations are the same as Deleting literal values and Deleting edges.

  1. {
  2. "delete": [
  3. {
  4. # 这里有一个JSON对象
  5. },
  6. {
  7. # 此处的另一个JSON对象用于多个操作
  8. }
  9. ]
  10. }

通过cURL使用JSON操作

首先,您必须配置HTTP header以指定content-type。

  1. -H 'Content-Type: application/json'

注意 为了将jq用于JSON格式,您需要jq包。有关安装详细信息,请参见jq下载页面。您还可以将Python内置的json.tool模块与python -m json.tool一起使用以进行JSON格式设置。

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. {
  3. "set": [
  4. {
  5. "name": "Alice"
  6. },
  7. {
  8. "name": "Bob"
  9. }
  10. ]
  11. }' | jq

删除:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. {
  3. "delete": [
  4. {
  5. "uid": "0xa"
  6. }
  7. ]
  8. }' | jq

使用JSON文件进行mutation:

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

data.json的内容如下所示:

  1. {
  2. "set": [
  3. {
  4. "name": "Alice"
  5. },
  6. {
  7. "name": "Bob"
  8. }
  9. ]
  10. }

对于通过HTTP进行的mutation,JSON文件必须遵循相同的格式:具有”set”或”delete”键的单个JSON对象,以及用于mutation的JSON对象数组。如果您已经具有包含数据数组的文件,则可以使用jq将数据转换为正确的格式。例如,如果您的data.json文件如下所示:

  1. [
  2. {
  3. "name": "Alice"
  4. },
  5. {
  6. "name": "Bob"
  7. }
  8. ]

那么您可以使用以下jq命令将数据转换为正确的格式,其中,.的代表data.json的内容:

  1. cat data.json | jq '{set: .}'
  1. {
  2. "set": [
  3. {
  4. "name": "Alice"
  5. },
  6. {
  7. "name": "Bob"
  8. }
  9. ]
  10. }

Upsert块

upsert块允许在单个请求中执行查询和mutation。 upsert块包含一个查询块和一个或多个mutation块。可以使用uidval函数在mutation块中使用查询块中定义的变量。

通常,upsert块的结构如下:

  1. upsert {
  2. query <query block>
  3. [fragment <fragment block>]
  4. mutation <mutation block 1>
  5. [mutation <mutation block 2>]
  6. ...
  7. }

upsert块的执行还返回执行mutation之前在数据库状态上执行的查询的响应。为了获得最新结果,我们应该提交mutation并执行另一个查询。

uid函数

uid函数允许从查询块中定义的变量中提取UID。根据执行查询块的结果,可能有两种结果:

  • 如果变量为空,即没有节点与查询匹配,则在进行set操作的情况下,uid函数将返回新的UID,因此将其视为空白节点。另一方面,对于delete操作,它不返回UID,因此该操作变为无操作且被忽略。空白节点在所有mutation块中都将获得相同的UID。
  • 如果变量存储一个或多个UID,则uid函数返回存储在变量中的所有UID。在这种情况下,将对返回的所有UID执行一次操作。

val函数

val函数允许从值变量中提取值。值变量存储从UID到其对应值的映射。因此,将val(v)替换为N-Quad中UID(Subject)的映射中存储的值。如果变量v对于给定的UID没有值,则忽略该mutation。 val函数也可以与聚合变量的结果一起使用,在这种情况下,mutation中的所有UID都将使用聚合值进行更新。

uid函数示例

考虑具有以下schema的示例:

  1. curl localhost:8080/alter -X POST -d $'
  2. name: string @index(term) .
  3. email: string @index(exact, trigram) @upsert .
  4. age: int @index(int) .' | jq

现在,假设我们要使用电子邮件和名称信息创建一个新用户。我们还希望确保一封电子邮件在数据库中恰好有一个相应的用户。为此,我们需要先使用给定的电子邮件查询用户是否存在于数据库中。如果存在用户,我们将使用其UID更新名称信息。如果该用户不存在,我们将创建一个新用户并更新电子邮件和姓名信息。

我们可以使用upsert块执行此操作,如下所示:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. q(func: eq(email, "user@company1.io")) {
  5. v as uid
  6. name
  7. }
  8. }
  9. mutation {
  10. set {
  11. uid(v) <name> "first last" .
  12. uid(v) <email> "user@company1.io" .
  13. }
  14. }
  15. }' | jq

结果:

  1. {
  2. "data": {
  3. "q": [],
  4. "code": "Success",
  5. "message": "Done",
  6. "uids": {
  7. "uid(v)": "0x1"
  8. }
  9. },
  10. "extensions": {...}
  11. }

upsert块的查询部分将用户的UID和提供的电子邮件存储在变量v中。然后,mutation部分从变量v提取UID,并将名称和电子邮件信息存储在数据库中。如果用户存在,则信息将更新。如果该用户不存在,则uid(v)被视为空白节点,并如上所述创建新用户。

如果我们再次运行相同的mutation,则数据将被覆盖,并且不会创建新的uid。请注意,再次执行该mutation时,uids在结果中为空,并且data中(q)包含上一个upsert中创建的uid。

  1. {
  2. "data": {
  3. "q": [
  4. {
  5. "uid": "0x1",
  6. "name": "first last"
  7. }
  8. ],
  9. "code": "Success",
  10. "message": "Done",
  11. "uids": {}
  12. },
  13. "extensions": {...}
  14. }

我们可以使用json数据实现相同的结果,如下所示:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '
  2. {
  3. "query": "{ q(func: eq(email, \\"user@company1.io\\")) {v as uid\\n name} }",
  4. "set": {
  5. "uid": "uid(v)",
  6. "name": "first last",
  7. "email": "user@company1.io"
  8. }
  9. }' | jq

现在,我们要添加具有相同电子邮件user@company1.io的同一用户的age信息。我们可以使用upsert块执行以下操作:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. q(func: eq(email, "user@company1.io")) {
  5. v as uid
  6. }
  7. }
  8. mutation {
  9. set {
  10. uid(v) <age> "28" .
  11. }
  12. }
  13. }' | jq

结果:

  1. {
  2. "data": {
  3. "q": [
  4. {
  5. "uid": "0x1"
  6. }
  7. ],
  8. "code": "Success",
  9. "message": "Done",
  10. "uids": {}
  11. },
  12. "extensions": {...}
  13. }

在这里,查询块查询emailuser@company1.io的用户。它将用户的uid存储在变量v中。然后,mutation块通过使用uid函数从变量v中提取uid来更新用户的age

我们可以使用json数据实现相同的结果,如下所示:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. {
  3. "query": "{ q(func: eq(email, \\"user@company1.io\\")) {v as uid} }",
  4. "set":{
  5. "uid": "uid(v)",
  6. "age": "28"
  7. }
  8. }' | jq

如果我们只想在用户存在时执行mutation,则可以使用条件Upsert

val函数示例

假设我们要将谓词age迁移到other,我们可以使用以下mutation来做到这一点:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. v as var(func: has(age)) {
  5. a as age
  6. }
  7. }
  8. mutation {
  9. # we copy the values from the old predicate
  10. set {
  11. uid(v) <other> val(a) .
  12. }
  13. # and we delete the old predicate
  14. delete {
  15. uid(v) <age> * .
  16. }
  17. }
  18. }' | jq

结果:

  1. {
  2. "data": {
  3. "code": "Success",
  4. "message": "Done",
  5. "uids": {}
  6. },
  7. "extensions": {...}
  8. }

在这里,变量a将存储从所有UID到其age的映射。然后,mutation块将每个UID的age相应值存储在other谓词中,并删除age谓词。

我们可以使用json数据实现相同的结果,如下所示:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'{
  2. "query": "{ v as var(func: regexp(email, /.*@company1.io$/)) }",
  3. "delete": {
  4. "uid": "uid(v)",
  5. "age": null
  6. },
  7. "set": {
  8. "uid": "uid(v)",
  9. "other": "val(a)"
  10. }
  11. }' | jq

批量删除示例

假设我们要从数据库中删除company1的所有用户。这可以使用upsert块在一个查询中完成,如下所示:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. v as var(func: regexp(email, /.*@company1.io$/))
  5. }
  6. mutation {
  7. delete {
  8. uid(v) <name> * .
  9. uid(v) <email> * .
  10. uid(v) <age> * .
  11. }
  12. }
  13. }' | jq

我们可以使用json数据实现相同的结果,如下所示:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '{
  2. "query": "{ v as var(func: regexp(email, /.*@company1.io$/)) }",
  3. "delete": {
  4. "uid": "uid(v)",
  5. "name": null,
  6. "email": null,
  7. "age": null
  8. }
  9. }' | jq

有条件的Upsert

upsert块还允许使用@if指令指定条件mutation块。仅当指定条件为true时,才执行mutation。如果条件为false,则忽略该mutation。有条件Upsert的一般结构如下所示:

  1. upsert {
  2. query <query block>
  3. [fragment <fragment block>]
  4. mutation [@if(<condition>)] <mutation block 1>
  5. [mutation [@if(<condition>)] <mutation block 2>]
  6. ...
  7. }

@if指令接受查询块中定义的变量的条件,并且可以使用ANDORNOT进行连接。

有条件Upsert的示例

假设在前面的示例中,我们知道company1的员工人数少于100。为了安全起见,我们希望仅在变量v中存储的UID小于100但大于50时才执行该mutation。这可以通过以下方式实现:

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. v as var(func: regexp(email, /.*@company1.io$/))
  5. }
  6. mutation @if(lt(len(v), 100) AND gt(len(v), 50)) {
  7. delete {
  8. uid(v) <name> * .
  9. uid(v) <email> * .
  10. uid(v) <age> * .
  11. }
  12. }
  13. }' | jq

我们可以使用json数据实现相同的结果,如下所示:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '{
  2. "query": "{ v as var(func: regexp(email, /.*@company1.io$/)) }",
  3. "cond": "@if(lt(len(v), 100) AND gt(len(v), 50))",
  4. "delete": {
  5. "uid": "uid(v)",
  6. "name": null,
  7. "email": null,
  8. "age": null
  9. }
  10. }' | jq

多个mutation块的例子

考虑具有以下schema的示例:

  1. curl localhost:8080/alter -X POST -d $'
  2. name: string @index(term) .
  3. email: [string] @index(exact) @upsert .' | jq

假设我们在数据库中存储了许多用户,每个用户都有一个或多个电子邮件地址。现在,我们得到两个属于同一用户的电子邮件地址。如果电子邮件地址属于数据库中的不同节点,则我们要删除现有节点并创建一个新节点,并将这两个电子邮件都附加到该新节点上。否则,我们将使用这两封电子邮件创建新的或更新现有的节点。

  1. curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $'
  2. upsert {
  3. query {
  4. # filter is needed to ensure that we do not get same UIDs in u1 and u2
  5. q1(func: eq(email, "user_email1@company1.io")) @filter(not(eq(email, "user_email2@company1.io"))) {
  6. u1 as uid
  7. }
  8. q2(func: eq(email, "user_email2@company1.io")) @filter(not(eq(email, "user_email1@company1.io"))) {
  9. u2 as uid
  10. }
  11. q3(func: eq(email, "user_email1@company1.io")) @filter(eq(email, "user_email2@company1.io")) {
  12. u3 as uid
  13. }
  14. }
  15. # case when both emails do not exist
  16. mutation @if(eq(len(u1), 0) AND eq(len(u2), 0) AND eq(len(u3), 0)) {
  17. set {
  18. _:user <name> "user" .
  19. _:user <dgraph.type> "Person" .
  20. _:user <email> "user_email1@company1.io" .
  21. _:user <email> "user_email2@company1.io" .
  22. }
  23. }
  24. # case when email1 exists but email2 does not
  25. mutation @if(eq(len(u1), 1) AND eq(len(u2), 0) AND eq(len(u3), 0)) {
  26. set {
  27. uid(u1) <email> "user_email2@company1.io" .
  28. }
  29. }
  30. # case when email1 does not exist but email2 exists
  31. mutation @if(eq(len(u1), 0) AND eq(len(u2), 1) AND eq(len(u3), 0)) {
  32. set {
  33. uid(u2) <email> "user_email1@company1.io" .
  34. }
  35. }
  36. # case when both emails exist and needs merging
  37. mutation @if(eq(len(u1), 1) AND eq(len(u2), 1) AND eq(len(u3), 0)) {
  38. set {
  39. _:user <name> "user" .
  40. _:user <dgraph.type> "Person" .
  41. _:user <email> "user_email1@company1.io" .
  42. _:user <email> "user_email2@company1.io" .
  43. }
  44. delete {
  45. uid(u1) <name> * .
  46. uid(u1) <email> * .
  47. uid(u2) <name> * .
  48. uid(u2) <email> * .
  49. }
  50. }
  51. }' | jq

结果(当数据库为空时):

  1. {
  2. "data": {
  3. "q1": [],
  4. "q2": [],
  5. "q3": [],
  6. "code": "Success",
  7. "message": "Done",
  8. "uids": {
  9. "user": "0x1"
  10. }
  11. },
  12. "extensions": {...}
  13. }

结果(两种电子邮件都存在并且被附加到不同的节点):

  1. {
  2. "data": {
  3. "q1": [
  4. {
  5. "uid": "0x2"
  6. }
  7. ],
  8. "q2": [
  9. {
  10. "uid": "0x3"
  11. }
  12. ],
  13. "q3": [],
  14. "code": "Success",
  15. "message": "Done",
  16. "uids": {
  17. "user": "0x4"
  18. }
  19. },
  20. "extensions": {...}
  21. }

结果(当两个电子邮件同时存在并且已经附加到同一节点时):

  1. {
  2. "data": {
  3. "q1": [],
  4. "q2": [],
  5. "q3": [
  6. {
  7. "uid": "0x4"
  8. }
  9. ],
  10. "code": "Success",
  11. "message": "Done",
  12. "uids": {}
  13. },
  14. "extensions": {...}
  15. }

我们可以使用json数据实现相同的结果,如下所示:

  1. curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '{
  2. "query": "{q1(func: eq(email, \"user_email1@company1.io\")) @filter(not(eq(email, \"user_email2@company1.io\"))) {u1 as uid} \n q2(func: eq(email, \"user_email2@company1.io\")) @filter(not(eq(email, \"user_email1@company1.io\"))) {u2 as uid} \n q3(func: eq(email, \"user_email1@company1.io\")) @filter(eq(email, \"user_email2@company1.io\")) {u3 as uid}}",
  3. "mutations": [
  4. {
  5. "cond": "@if(eq(len(u1), 0) AND eq(len(u2), 0) AND eq(len(u3), 0))",
  6. "set": [
  7. {
  8. "uid": "_:user",
  9. "name": "user",
  10. "dgraph.type": "Person"
  11. },
  12. {
  13. "uid": "_:user",
  14. "email": "user_email1@company1.io",
  15. "dgraph.type": "Person"
  16. },
  17. {
  18. "uid": "_:user",
  19. "email": "user_email2@company1.io",
  20. "dgraph.type": "Person"
  21. }
  22. ]
  23. },
  24. {
  25. "cond": "@if(eq(len(u1), 1) AND eq(len(u2), 0) AND eq(len(u3), 0))",
  26. "set": [
  27. {
  28. "uid": "uid(u1)",
  29. "email": "user_email2@company1.io",
  30. "dgraph.type": "Person"
  31. }
  32. ]
  33. },
  34. {
  35. "cond": "@if(eq(len(u1), 1) AND eq(len(u2), 0) AND eq(len(u3), 0))",
  36. "set": [
  37. {
  38. "uid": "uid(u2)",
  39. "email": "user_email1@company1.io",
  40. "dgraph.type": "Person"
  41. }
  42. ]
  43. },
  44. {
  45. "cond": "@if(eq(len(u1), 1) AND eq(len(u2), 1) AND eq(len(u3), 0))",
  46. "set": [
  47. {
  48. "uid": "_:user",
  49. "name": "user",
  50. "dgraph.type": "Person"
  51. },
  52. {
  53. "uid": "_:user",
  54. "email": "user_email1@company1.io",
  55. "dgraph.type": "Person"
  56. },
  57. {
  58. "uid": "_:user",
  59. "email": "user_email2@company1.io",
  60. "dgraph.type": "Person"
  61. }
  62. ],
  63. "delete": [
  64. {
  65. "uid": "uid(u1)",
  66. "name": null,
  67. "email": null
  68. },
  69. {
  70. "uid": "uid(u2)",
  71. "name": null,
  72. "email": null
  73. }
  74. ]
  75. }
  76. ]
  77. }' | jq