github.com/dgraph-io/dgraph@v1.2.8/wiki/content/mutations/index.md (about) 1 +++ 2 title = "Mutations" 3 +++ 4 5 Adding or removing data in Dgraph is called a mutation. 6 7 A mutation that adds triples is done with the `set` keyword. 8 ``` 9 { 10 set { 11 # triples in here 12 } 13 } 14 ``` 15 16 ## Triples 17 18 The input language is triples in the W3C standard [RDF N-Quad format](https://www.w3.org/TR/n-quads/). 19 20 Each triple has the form 21 ``` 22 <subject> <predicate> <object> . 23 ``` 24 Meaning that the graph node identified by `subject` is linked to `object` with directed edge `predicate`. Each triple ends with a period. The subject of a triple is always a node in the graph, while the object may be a node or a value (a literal). 25 26 For example, the triple 27 ``` 28 <0x01> <name> "Alice" . 29 <0x01> <dgraph.type> "Person" . 30 ``` 31 Represents that graph node with ID `0x01` has a `name` with string value `"Alice"`. While triple 32 ``` 33 <0x01> <friend> <0x02> . 34 ``` 35 Represents that graph node with ID `0x01` is linked with the `friend` edge to node `0x02`. 36 37 Dgraph creates a unique 64 bit identifier for every blank node in the mutation - the node's UID. A mutation can include a blank node as an identifier for the subject or object, or a known UID from a previous mutation. 38 39 40 ## Blank Nodes and UID 41 42 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: 43 44 ``` 45 { 46 set { 47 _:class <student> _:x . 48 _:class <student> _:y . 49 _:class <name> "awesome class" . 50 _:class <dgraph.type> "Class" . 51 _:x <name> "Alice" . 52 _:x <dgraph.type> "Person" . 53 _:x <dgraph.type> "Student" . 54 _:x <planet> "Mars" . 55 _:x <friend> _:y . 56 _:y <name> "Bob" . 57 _:y <dgraph.type> "Person" . 58 _:y <dgraph.type> "Student" . 59 } 60 } 61 ``` 62 results in output (the actual UIDs will be different on any run of this mutation) 63 ``` 64 { 65 "data": { 66 "code": "Success", 67 "message": "Done", 68 "uids": { 69 "class": "0x2712", 70 "x": "0x2713", 71 "y": "0x2714" 72 } 73 } 74 } 75 ``` 76 The graph has thus been updated as if it had stored the triples 77 ``` 78 <0x6bc818dc89e78754> <student> <0xc3bcc578868b719d> . 79 <0x6bc818dc89e78754> <student> <0xb294fb8464357b0a> . 80 <0x6bc818dc89e78754> <name> "awesome class" . 81 <0x6bc818dc89e78754> <dgraph.type> "Class" . 82 <0xc3bcc578868b719d> <name> "Alice" . 83 <0xc3bcc578868b719d> <dgraph.type> "Person" . 84 <0xc3bcc578868b719d> <dgraph.type> "Student" . 85 <0xc3bcc578868b719d> <planet> "Mars" . 86 <0xc3bcc578868b719d> <friend> <0xb294fb8464357b0a> . 87 <0xb294fb8464357b0a> <name> "Bob" . 88 <0xb294fb8464357b0a> <dgraph.type> "Person" . 89 <0xb294fb8464357b0a> <dgraph.type> "Student" . 90 ``` 91 The blank node labels `_:class`, `_:x` and `_:y` do not identify the nodes after the mutation, and can be safely reused to identify new nodes in later mutations. 92 93 A later mutation can update the data for existing UIDs. For example, the following to add a new student to the class. 94 ``` 95 { 96 set { 97 <0x6bc818dc89e78754> <student> _:x . 98 _:x <name> "Chris" . 99 _:x <dgraph.type> "Person" . 100 _:x <dgraph.type> "Student" . 101 } 102 } 103 ``` 104 105 A query can also directly use UID. 106 ``` 107 { 108 class(func: uid(0x6bc818dc89e78754)) { 109 name 110 student { 111 name 112 planet 113 friend { 114 name 115 } 116 } 117 } 118 } 119 ``` 120 121 ## External IDs 122 123 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](http://schema.org) identifiers, [the movie database](https://www.themoviedb.org/) identifiers and blank nodes: 124 125 ``` 126 _:userA <http://schema.org/type> <http://schema.org/Person> . 127 _:userA <dgraph.type> "Person" . 128 _:userA <http://schema.org/name> "FirstName LastName" . 129 <https://www.themoviedb.org/person/32-robin-wright> <http://schema.org/type> <http://schema.org/Person> . 130 <https://www.themoviedb.org/person/32-robin-wright> <http://schema.org/name> "Robin Wright" . 131 ``` 132 133 As Dgraph doesn't natively support such external IDs as node identifiers. Instead, external IDs can be stored as properties of a node with an `xid` edge. For example, from the above, the predicate names are valid in Dgraph, but the node identified with `<http://schema.org/Person>` could be identified in Dgraph with a UID, say `0x123`, and an edge 134 135 ``` 136 <0x123> <xid> "http://schema.org/Person" . 137 <0x123> <dgraph.type> "ExternalType" . 138 ``` 139 140 While Robin Wright might get UID `0x321` and triples 141 142 ``` 143 <0x321> <xid> "https://www.themoviedb.org/person/32-robin-wright" . 144 <0x321> <http://schema.org/type> <0x123> . 145 <0x321> <http://schema.org/name> "Robin Wright" . 146 <0x321> <dgraph.type> "Person" . 147 ``` 148 149 An appropriate schema might be as follows. 150 ``` 151 xid: string @index(exact) . 152 <http://schema.org/type>: [uid] @reverse . 153 ``` 154 155 Query Example: All people. 156 157 ``` 158 { 159 var(func: eq(xid, "http://schema.org/Person")) { 160 allPeople as <~http://schema.org/type> 161 } 162 163 q(func: uid(allPeople)) { 164 <http://schema.org/name> 165 } 166 } 167 ``` 168 169 Query Example: Robin Wright by external ID. 170 171 ``` 172 { 173 robin(func: eq(xid, "https://www.themoviedb.org/person/32-robin-wright")) { 174 expand(_all_) { expand(_all_) } 175 } 176 } 177 178 ``` 179 180 {{% notice "note" %}} `xid` edges are not added automatically in mutations. In general it is a user's responsibility to check for existing `xid`'s and add nodes and `xid` edges if necessary. Dgraph leaves all checking of uniqueness of such `xid`'s to external processes. {{% /notice %}} 181 182 ## External IDs and Upsert Block 183 184 The upsert block makes managing external IDs easy. 185 186 Set the schema. 187 ``` 188 xid: string @index(exact) . 189 <http://schema.org/name>: string @index(exact) . 190 <http://schema.org/type>: [uid] @reverse . 191 ``` 192 193 Set the type first of all. 194 ``` 195 { 196 set { 197 _:blank <xid> "http://schema.org/Person" . 198 _:blank <dgraph.type> "ExternalType" . 199 } 200 } 201 ``` 202 203 Now you can create a new person and attach its type using the upsert block. 204 ``` 205 upsert { 206 query { 207 var(func: eq(xid, "http://schema.org/Person")) { 208 Type as uid 209 } 210 var(func: eq(<http://schema.org/name>, "Robin Wright")) { 211 Person as uid 212 } 213 } 214 mutation { 215 set { 216 uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" . 217 uid(Person) <http://schema.org/type> uid(Type) . 218 uid(Person) <http://schema.org/name> "Robin Wright" . 219 uid(Person) <dgraph.type> "Person" . 220 } 221 } 222 } 223 ``` 224 225 You can also delete a person and detach the relation between Type and Person Node. It's the same as above, but you use the keyword "delete" instead of "set". "`http://schema.org/Person`" will remain but "`Robin Wright`" will be deleted. 226 227 ``` 228 upsert { 229 query { 230 var(func: eq(xid, "http://schema.org/Person")) { 231 Type as uid 232 } 233 var(func: eq(<http://schema.org/name>, "Robin Wright")) { 234 Person as uid 235 } 236 } 237 mutation { 238 delete { 239 uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" . 240 uid(Person) <http://schema.org/type> uid(Type) . 241 uid(Person) <http://schema.org/name> "Robin Wright" . 242 uid(Person) <dgraph.type> "Person" . 243 } 244 } 245 } 246 ``` 247 248 Query by user. 249 ``` 250 { 251 q(func: eq(<http://schema.org/name>, "Robin Wright")) { 252 uid 253 xid 254 <http://schema.org/name> 255 <http://schema.org/type> { 256 uid 257 xid 258 } 259 } 260 } 261 ``` 262 263 ## Language and RDF Types 264 265 RDF N-Quad allows specifying a language for string values and an RDF type. Languages are written using `@lang`. For example 266 ``` 267 <0x01> <name> "Adelaide"@en . 268 <0x01> <name> "Аделаида"@ru . 269 <0x01> <name> "Adélaïde"@fr . 270 <0x01> <dgraph.type> "Person" . 271 ``` 272 See also [how language strings are handled in queries]({{< relref "query-language/index.md#language-support" >}}). 273 274 RDF types are attached to literals with the standard `^^` separator. For example 275 ``` 276 <0x01> <age> "32"^^<xs:int> . 277 <0x01> <birthdate> "1985-06-08"^^<xs:dateTime> . 278 ``` 279 280 The supported [RDF datatypes](https://www.w3.org/TR/rdf11-concepts/#section-Datatypes) and the corresponding internal type in which the data is stored are as follows. 281 282 | Storage Type | Dgraph type | 283 | ------------- | :------------: | 284 | <xs:string> | `string` | 285 | <xs:dateTime> | `dateTime` | 286 | <xs:date> | `datetime` | 287 | <xs:int> | `int` | 288 | <xs:integer> | `int` | 289 | <xs:boolean> | `bool` | 290 | <xs:double> | `float` | 291 | <xs:float> | `float` | 292 | <geo:geojson> | `geo` | 293 | <xs:password> | `password` | 294 | <http://www.w3.org/2001/XMLSchema#string> | `string` | 295 | <http://www.w3.org/2001/XMLSchema#dateTime> | `dateTime` | 296 | <http://www.w3.org/2001/XMLSchema#date> | `dateTime` | 297 | <http://www.w3.org/2001/XMLSchema#int> | `int` | 298 | <http://www.w3.org/2001/XMLSchema#positiveInteger> | `int` | 299 | <http://www.w3.org/2001/XMLSchema#integer> | `int` | 300 | <http://www.w3.org/2001/XMLSchema#boolean> | `bool` | 301 | <http://www.w3.org/2001/XMLSchema#double> | `float` | 302 | <http://www.w3.org/2001/XMLSchema#float> | `float` | 303 304 305 See the section on [RDF schema types]({{< relref "#rdf-types" >}}) to understand how RDF types affect mutations and storage. 306 307 308 ## Batch mutations 309 310 Each mutation may contain multiple RDF triples. For large data uploads many such mutations can be batched in parallel. The command `dgraph live` does just this; by default batching 1000 RDF lines into a query, while running 100 such queries in parallel. 311 312 `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. 313 314 ``` 315 dgraph live --help 316 ``` 317 See also [Fast Data Loading](/deploy#fast-data-loading). 318 319 ## Delete 320 321 A delete mutation, signified with the `delete` keyword, removes triples from the store. 322 323 For example, if the store contained 324 ``` 325 <0xf11168064b01135b> <name> "Lewis Carrol" 326 <0xf11168064b01135b> <died> "1998" 327 <0xf11168064b01135b> <dgraph.type> "Person" . 328 ``` 329 330 Then delete mutation 331 332 ``` 333 { 334 delete { 335 <0xf11168064b01135b> <died> "1998" . 336 } 337 } 338 ``` 339 340 Deletes the erroneous data and removes it from indexes if present. 341 342 For a particular node `N`, all data for predicate `P` (and corresponding indexing) is removed with the pattern `S P *`. 343 344 ``` 345 { 346 delete { 347 <0xf11168064b01135b> <author.of> * . 348 } 349 } 350 ``` 351 352 The pattern `S * *` deletes all known edges out of a node (the node itself may 353 remain as the target of edges), any reverse edges corresponding to the removed 354 edges and any indexing for the removed data. The predicates to delete are 355 derived from the type information for that node (the value of the `dgraph.type` 356 edges on that node and their corresponding definitions in the schema). If that 357 information is missing, this operation will be a no-op. 358 359 ``` 360 { 361 delete { 362 <0xf11168064b01135b> * * . 363 } 364 } 365 ``` 366 367 368 {{% notice "note" %}} The patterns `* P O` and `* * O` are not supported since its expensive to store/find all the incoming edges. {{% /notice %}} 369 370 ### Deletion of non-list predicates 371 372 Deleting the value of a non-list predicate (i.e a 1-to-1 relation) can be done in two ways. 373 374 1. Using the star notation mentioned in the last section. 375 1. Setting the object to a specific value. If the value passed is not the current value, the mutation will succeed but will have no effect. If the value passed is the current value, the mutation will succeed and will delete the triple. 376 377 For language-tagged values, the following special syntax is supported: 378 379 ``` 380 { 381 delete { 382 <0x12345> <name@es> * . 383 } 384 } 385 ``` 386 387 In this example, the value of name tagged with language tag `es` will be deleted. 388 Other tagged values are left untouched. 389 390 ## Mutations using cURL 391 392 Mutations can be done over HTTP by making a `POST` request to an Alpha's `/mutate` endpoint. On the command line this can be done with curl. To commit the mutation, pass the parameter `commitNow=true` in the URL. 393 394 To run a `set` mutation: 395 396 ```sh 397 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 398 { 399 set { 400 _:alice <name> "Alice" . 401 _:alice <dgraph.type> "Person" . 402 } 403 }' 404 ``` 405 406 To run a `delete` mutation: 407 408 ```sh 409 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 410 { 411 delete { 412 # Example: The UID of Alice is 0x56f33 413 <0x56f33> <name> * . 414 } 415 }' 416 ``` 417 418 To run an RDF mutation stored in a file, use curl's `--data-binary` option so that, unlike the `-d` option, the data is not URL encoded. 419 420 ```sh 421 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true --data-binary @mutation.txt 422 ``` 423 424 ## JSON Mutation Format 425 426 Mutations can also be specified using JSON objects. This can allow mutations to 427 be expressed in a more natural way. It also eliminates the need for apps to 428 have custom serialisation code, since most languages already have a JSON 429 marshalling library. 430 431 When Dgraph receives a mutation as a JSON object, it first converts in into 432 multiple RDFs that are then processed as normal. 433 434 Each JSON object represents a single node in the graph. 435 436 {{% notice "note" %}} 437 JSON mutations are available via gRPC clients such as the Go client, JS client, and Java client, and are available to HTTP clients with [dgraph-js-http](https://github.com/dgraph-io/dgraph-js-http) and cURL. See more about cURL [here]({{< relref "#using-json-operations-via-curl" >}}) 438 {{% /notice %}} 439 440 ### Setting literal values 441 442 When setting new values, the `set_json` field in the `Mutation` message should 443 contain a JSON object. 444 445 Literal values can be set by adding a key/value to the JSON object. The key 446 represents the predicate, and the value represents the object. 447 448 For example: 449 ```json 450 { 451 "name": "diggy", 452 "food": "pizza", 453 "dgraph.type": "Mascot" 454 } 455 ``` 456 Will be converted into the RDFs: 457 ``` 458 _:blank-0 <name> "diggy" . 459 _:blank-0 <food> "pizza" . 460 _:blank-0 <dgraph.type> "Mascot" . 461 ``` 462 463 The result of the mutation would also contain a map, which would have the uid assigned corresponding 464 to the key `blank-0`. You could specify your own key like 465 466 ```json 467 { 468 "uid": "_:diggy", 469 "name": "diggy", 470 "food": "pizza", 471 "dgraph.type": "Mascot" 472 } 473 ``` 474 475 In this case, the assigned uids map would have a key called `diggy` with the value being the uid 476 assigned to it. 477 478 ### Language support 479 480 An important difference between RDF and JSON mutations is in regards to specifying a string value's 481 language. In JSON, the language tag is appended to the edge _name_, not the value like in RDF. 482 483 For example, the JSON mutation 484 ```json 485 { 486 "food": "taco", 487 "rating@en": "tastes good", 488 "rating@es": "sabe bien", 489 "rating@fr": "c'est bon", 490 "rating@it": "è buono", 491 "dgraph.type": "Food" 492 } 493 ``` 494 495 is equivalent to the following RDF: 496 ``` 497 _:blank-0 <food> "taco" . 498 _:blank-0 <dgraph.type> "Food" . 499 _:blank-0 <rating> "tastes good"@en . 500 _:blank-0 <rating> "sabe bien"@es . 501 _:blank-0 <rating> "c'est bon"@fr . 502 _:blank-0 <rating> "è buono"@it . 503 ``` 504 505 ### Geolocation support 506 507 Support for geolocation data is available in JSON. Geo-location data is entered 508 as a JSON object with keys "type" and "coordinates". Keep in mind we only 509 support indexing on the Point, Polygon, and MultiPolygon types, but we can store 510 other types of geolocation data. Below is an example: 511 512 ``` 513 { 514 "food": "taco", 515 location: { 516 "type": "Point", 517 "coordinates": [1.0, 2.0] 518 } 519 } 520 ``` 521 522 ### Referencing existing nodes 523 524 If a JSON object contains a field named `"uid"`, then that field is interpreted 525 as the UID of an existing node in the graph. This mechanism allows you to 526 reference existing nodes. 527 528 For example: 529 ```json 530 { 531 "uid": "0x467ba0", 532 "food": "taco", 533 "rating": "tastes good", 534 "dgraph.type": "Food" 535 } 536 ``` 537 Will be converted into the RDFs: 538 ``` 539 <0x467ba0> <food> "taco" . 540 <0x467ba0> <rating> "tastes good" . 541 <0x467ba0> <dgraph.type> "Food" . 542 ``` 543 544 ### Edges between nodes 545 546 Edges between nodes are represented in a similar way to literal values, except 547 that the object is a JSON object. 548 549 For example: 550 ```json 551 { 552 "name": "Alice", 553 "friend": { 554 "name": "Betty" 555 } 556 } 557 ``` 558 Will be converted into the RDFs: 559 ``` 560 _:blank-0 <name> "Alice" . 561 _:blank-0 <friend> _:blank-1 . 562 _:blank-1 <name> "Betty" . 563 ``` 564 565 The result of the mutation would contain the uids assigned to `blank-0` and `blank-1` nodes. If you 566 wanted to return these uids under a different key, you could specify the `uid` field as a blank 567 node. 568 569 ```json 570 { 571 "uid": "_:alice", 572 "name": "Alice", 573 "friend": { 574 "uid": "_:bob", 575 "name": "Betty" 576 } 577 } 578 ``` 579 580 Will be converted to: 581 582 ``` 583 _:alice <name> "Alice" . 584 _:alice <friend> _:bob . 585 _:bob <name> "Betty" . 586 ``` 587 588 Existing nodes can be referenced in the same way as when adding literal values. 589 E.g. to link two existing nodes: 590 ```json 591 { 592 "uid": "0x123", 593 "link": { 594 "uid": "0x456" 595 } 596 } 597 ``` 598 599 Will be converted to: 600 601 ```JSON 602 <0x123> <link> <0x456> . 603 ``` 604 605 {{% notice "note" %}} 606 A common mistake is to attempt to use `{"uid":"0x123","link":"0x456"}`. This 607 will result in an error. Dgraph interprets this JSON object as setting the 608 `link` predicate to the string`"0x456"`, which is usually not intended. {{% 609 /notice %}} 610 611 ### Deleting literal values 612 613 Deletion mutations can also be sent in JSON format. To send a delete mutation, 614 use the `delete_json` field instead of the `set_json` field in the `Mutation` 615 message. 616 617 {{% notice "note" %}} Check the [JSON Syntax using Raw HTTP or Ratel UI]({{< relref "#json-syntax-using-raw-http-or-ratel-ui">}}) section if you're using the dgraph-js-http client or Ratel UI. {{% /notice %}} 618 619 When using delete mutations, an existing node always has to be referenced. So 620 the `"uid"` field for each JSON object must be present. Predicates that should 621 be deleted should be set to the JSON value `null`. 622 623 For example, to remove a food rating: 624 ```json 625 { 626 "uid": "0x467ba0", 627 "rating": null 628 } 629 ``` 630 631 ### Deleting edges 632 633 Deleting a single edge requires the same JSON object that would create that 634 edge. E.g. to delete the predicate `link` from `"0x123"` to `"0x456"`: 635 ```json 636 { 637 "uid": "0x123", 638 "link": { 639 "uid": "0x456" 640 } 641 } 642 ``` 643 644 All edges for a predicate emanating from a single node can be deleted at once 645 (corresponding to deleting `S P *`): 646 ```json 647 { 648 "uid": "0x123", 649 "link": null 650 } 651 ``` 652 653 If no predicates are specified, then all of the node's known outbound edges (to 654 other nodes and to literal values) are deleted (corresponding to deleting `S * 655 *`). The predicates to delete are derived using the type system. Refer to the 656 [RDF format]({{< relref "#delete" >}}) documentation and the section on the 657 [type system]({{< relref "query-language/index.md#type-system" >}}) for more 658 information: 659 660 ```json 661 { 662 "uid": "0x123" 663 } 664 ``` 665 666 ### Facets 667 668 Facets can be created by using the `|` character to separate the predicate 669 and facet key in a JSON object field name. This is the same encoding schema 670 used to show facets in query results. E.g. 671 ```json 672 { 673 "name": "Carol", 674 "name|initial": "C", 675 "dgraph.type": "Person", 676 "friend": { 677 "name": "Daryl", 678 "friend|close": "yes", 679 "dgraph.type": "Person" 680 } 681 } 682 ``` 683 Produces the following RDFs: 684 ``` 685 _:blank-0 <name> "Carol" (initial=C) . 686 _:blank-0 <dgraph.type> "Person" . 687 _:blank-0 <friend> _:blank-1 (close=yes) . 688 _:blank-1 <name> "Daryl" . 689 _:blank-1 <dgraph.type> "Person" . 690 ``` 691 692 Facets do not contain type information but Dgraph will try to guess a type from 693 the input. If the value of a facet can be parsed to a number, it will be 694 converted to either a float or an int. If it can be parsed as a boolean, it will 695 be stored as a boolean. If the value is a string, it will be stored as a 696 datetime if the string matches one of the time formats that Dgraph recognizes 697 (YYYY, MM-YYYY, DD-MM-YYYY, RFC339, etc.) and as a double-quoted string 698 otherwise. If you do not want to risk the chance of your facet data being 699 misinterpreted as a time value, it is best to store numeric data as either an 700 int or a float. 701 702 ### Deleting Facets 703 704 The easiest way to delete a Facet is overwriting it. When you create a new mutation for the same entity without a facet, the existing facet will be deleted automatically. 705 706 e.g: 707 708 ```RDF 709 <0x1> <name> "Carol" . 710 <0x1> <friend> <0x2> . 711 ``` 712 713 Another way to do this is by using the Upsert Block. 714 715 > In this query below, we are deleting Facet in the Name and Friend predicates. To overwrite we need to collect the values of the edges on which we are performing this operation and use the function "val(var)" to complete the overwriting. 716 717 ```sh 718 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 719 upsert { 720 query { 721 user as var(func: eq(name, "Carol")){ 722 Name as name 723 Friends as friend 724 } 725 } 726 727 mutation { 728 set { 729 uid(user) <name> val(Name) . 730 uid(user) <friend> uid(Friends) . 731 } 732 } 733 }' | jq 734 ``` 735 736 ### Creating a list with JSON and interacting with 737 738 Schema: 739 740 ```JSON 741 testList: [string] . 742 ``` 743 744 ```JSON 745 { 746 "testList": [ 747 "Grape", 748 "Apple", 749 "Strawberry", 750 "Banana", 751 "watermelon" 752 ] 753 } 754 ``` 755 756 Let’s then remove "Apple" from this list (Remember, it’s case sensitive): 757 758 ```JSON 759 { 760 "uid": "0xd", #UID of the list. 761 "testList": "Apple" 762 } 763 ``` 764 765 {{% notice "note" %}} Check the [JSON Syntax using Raw HTTP or Ratel UI]({{< relref "#json-syntax-using-raw-http-or-ratel-ui">}}) section if you're using the dgraph-js-http client or Ratel UI. {{% /notice %}} 766 767 768 Add another fruit: 769 770 ```JSON 771 { 772 "uid": "0xd", #UID of the list. 773 "testList": "Pineapple" 774 } 775 ``` 776 777 ### Specifying multiple operations 778 779 When specifying add or delete mutations, multiple nodes can be specified 780 at the same time using JSON arrays. 781 782 For example, the following JSON object can be used to add two new nodes, each 783 with a `name`: 784 785 ```JSON 786 [ 787 { 788 "name": "Edward" 789 }, 790 { 791 "name": "Fredric" 792 } 793 ] 794 ``` 795 796 ### JSON Syntax using Raw HTTP or Ratel UI 797 798 This syntax can be used in the most current version of Ratel, in the [dgraph-js-http](https://github.com/dgraph-io/dgraph-js-http) client or even via cURL. 799 800 You can also [download the Ratel UI for Linux, macOS, or Windows](https://discuss.dgraph.io/t/ratel-installer-for-linux-macos-and-windows-preview-version-ratel-update-from-v1-0-6/2884/). 801 802 Mutate: 803 ```JSON 804 { 805 "set": [ 806 { 807 # One JSON obj in here 808 }, 809 { 810 # Another JSON obj in here for multiple operations 811 } 812 ] 813 } 814 ``` 815 816 Delete: 817 818 Deletion operations are the same as [Deleting literal values]({{< relref "#deleting-literal-values">}}) and [Deleting edges]({{< relref "#deleting-edges">}}). 819 820 ```JSON 821 { 822 "delete": [ 823 { 824 # One JSON obj in here 825 }, 826 { 827 # Another JSON obj in here for multiple operations 828 } 829 ] 830 } 831 ``` 832 833 ### Using JSON operations via cURL 834 835 First you have to configure the HTTP header to specify content-type. 836 837 ```sh 838 -H 'Content-Type: application/json' 839 ``` 840 841 {{% notice "note" %}} 842 In order to use `jq` for JSON formatting you need the `jq` package. See the 843 [`jq` downloads](https://stedolan.github.io/jq/download/) page for installation 844 details. You can also use Python's built in `json.tool` module with `python -m 845 json.tool` to do JSON formatting. 846 {{% /notice %}} 847 848 ```sh 849 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $' 850 { 851 "set": [ 852 { 853 "name": "Alice" 854 }, 855 { 856 "name": "Bob" 857 } 858 ] 859 }' | jq 860 861 ``` 862 863 To delete: 864 865 ```sh 866 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $' 867 { 868 "delete": [ 869 { 870 "uid": "0xa" 871 } 872 ] 873 }' | jq 874 ``` 875 876 Mutation with a JSON file: 877 878 ```sh 879 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d @data.json 880 ``` 881 882 where the contents of data.json looks like the following: 883 884 ```json 885 { 886 "set": [ 887 { 888 "name": "Alice" 889 }, 890 { 891 "name": "Bob" 892 } 893 ] 894 } 895 ``` 896 897 The JSON file must follow the same format for mutations over HTTP: a single JSON 898 object with the `"set"` or `"delete"` key and an array of JSON objects for the 899 mutation. If you already have a file with an array of data, you can use `jq` to 900 transform your data to the proper format. For example, if your data.json file 901 looks like this: 902 903 ```json 904 [ 905 { 906 "name": "Alice" 907 }, 908 { 909 "name": "Bob" 910 } 911 ] 912 ``` 913 914 then you can transform your data to the proper format with the following `jq` 915 command, where the `.` in the `jq` string represents the contents of data.json: 916 917 ```sh 918 cat data.json | jq '{set: .}' 919 ``` 920 921 ``` 922 { 923 "set": [ 924 { 925 "name": "Alice" 926 }, 927 { 928 "name": "Bob" 929 } 930 ] 931 } 932 ``` 933 934 ## Upsert Block 935 936 The upsert block allows performing queries and mutations in a single request. The upsert 937 block contains one query block and one or more than one mutation blocks. Variables defined 938 in the query block can be used in the mutation blocks using the `uid` and `val` function. 939 940 In general, the structure of the upsert block is as follows: 941 942 ``` 943 upsert { 944 query <query block> 945 [fragment <fragment block>] 946 mutation <mutation block 1> 947 [mutation <mutation block 2>] 948 ... 949 } 950 ``` 951 952 Execution of an upsert block also returns the response of the query executed on the state 953 of the database *before mutation was executed*. To get the latest result, we should commit 954 the mutation and execute another query. 955 956 ### `uid` Function 957 958 The `uid` function allows extracting UIDs from variables defined in the query block. 959 There are two possible outcomes based on the results of executing the query block: 960 961 * If the variable is empty i.e. no node matched the query, the `uid` function returns a new UID in case of a `set` operation and is thus treated similar to a blank node. On the other hand, for `delete/del` operation, it returns no UID, and thus the operation becomes a no-op and is silently ignored. A blank node gets the same UID across all the mutation blocks. 962 * If the variable stores one or more than one UIDs, the `uid` function returns all the UIDs stored in the variable. In this case, the operation is performed on all the UIDs returned, one at a time. 963 964 ### `val` Function 965 966 The `val` function allows extracting values from value variables. Value variables store 967 a mapping from UIDs to their corresponding values. Hence, `val(v)` is replaced by the value 968 stored in the mapping for the UID (Subject) in the N-Quad. If the variable `v` has no value 969 for a given UID, the mutation is silently ignored. The `val` function can be used with the 970 result of aggregate variables as well, in which case, all the UIDs in the mutation would 971 be updated with the aggregate value. 972 973 ### Example of `uid` Function 974 975 Consider an example with the following schema: 976 977 ```sh 978 curl localhost:8080/alter -X POST -d $' 979 name: string @index(term) . 980 email: string @index(exact, trigram) @upsert . 981 age: int @index(int) .' | jq 982 ``` 983 984 Now, let's say we want to create a new user with `email` and `name` information. 985 We also want to make sure that one email has exactly one corresponding user in 986 the database. To achieve this, we need to first query whether a user exists 987 in the database with the given email. If a user exists, we use its UID 988 to update the `name` information. If the user doesn't exist, we create 989 a new user and update the `email` and `name` information. 990 991 We can do this using the upsert block as follows: 992 993 ```sh 994 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 995 upsert { 996 query { 997 q(func: eq(email, "user@company1.io")) { 998 v as uid 999 name 1000 } 1001 } 1002 1003 mutation { 1004 set { 1005 uid(v) <name> "first last" . 1006 uid(v) <email> "user@company1.io" . 1007 } 1008 } 1009 }' | jq 1010 ``` 1011 1012 Result: 1013 1014 ```json 1015 { 1016 "data": { 1017 "q": [], 1018 "code": "Success", 1019 "message": "Done", 1020 "uids": { 1021 "uid(v)": "0x1" 1022 } 1023 }, 1024 "extensions": {...} 1025 } 1026 ``` 1027 1028 The query part of the upsert block stores the UID of the user with the provided email 1029 in the variable `v`. The mutation part then extracts the UID from variable `v`, and 1030 stores the `name` and `email` information in the database. If the user exists, 1031 the information is updated. If the user doesn't exist, `uid(v)` is treated 1032 as a blank node and a new user is created as explained above. 1033 1034 If we run the same mutation again, the data would just be overwritten, and no new uid is 1035 created. Note that the `uids` map is empty in the result when the mutation is executed 1036 again and the `data` map (key `q`) contains the uid that was created in the previous upsert. 1037 1038 ```json 1039 { 1040 "data": { 1041 "q": [ 1042 { 1043 "uid": "0x1", 1044 "name": "first last" 1045 } 1046 ], 1047 "code": "Success", 1048 "message": "Done", 1049 "uids": {} 1050 }, 1051 "extensions": {...} 1052 } 1053 ``` 1054 1055 We can achieve the same result using `json` dataset as follows: 1056 1057 ```sh 1058 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d ' 1059 { 1060 "query": "{ q(func: eq(email, \\"user@company1.io\\")) {v as uid\\n name} }", 1061 "set": { 1062 "uid": "uid(v)", 1063 "name": "first last", 1064 "email": "user@company1.io" 1065 } 1066 }' | jq 1067 ``` 1068 1069 Now, we want to add the `age` information for the same user having the same email 1070 `user@company1.io`. We can use the upsert block to do the same as follows: 1071 1072 ```sh 1073 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 1074 upsert { 1075 query { 1076 q(func: eq(email, "user@company1.io")) { 1077 v as uid 1078 } 1079 } 1080 1081 mutation { 1082 set { 1083 uid(v) <age> "28" . 1084 } 1085 } 1086 }' | jq 1087 ``` 1088 1089 Result: 1090 1091 ```json 1092 { 1093 "data": { 1094 "q": [ 1095 { 1096 "uid": "0x1" 1097 } 1098 ], 1099 "code": "Success", 1100 "message": "Done", 1101 "uids": {} 1102 }, 1103 "extensions": {...} 1104 } 1105 ``` 1106 1107 Here, the query block queries for a user with `email` as `user@company1.io`. It stores 1108 the `uid` of the user in variable `v`. The mutation block then updates the `age` of the 1109 user by extracting the uid from the variable `v` using `uid` function. 1110 1111 We can achieve the same result using `json` dataset as follows: 1112 1113 ```sh 1114 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $' 1115 { 1116 "query": "{ q(func: eq(email, \\"user@company1.io\\")) {v as uid} }", 1117 "set":{ 1118 "uid": "uid(v)", 1119 "age": "28" 1120 } 1121 }' | jq 1122 ``` 1123 1124 If we want to execute the mutation only when the user exists, we could use 1125 [Conditional Upsert]({{< relref "#conditional-upsert" >}}). 1126 1127 ### Example of `val` Function 1128 1129 Let's say we want to migrate the predicate `age` to `other`. We can do this using the 1130 following mutation: 1131 1132 ```sh 1133 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 1134 upsert { 1135 query { 1136 v as var(func: has(age)) { 1137 a as age 1138 } 1139 } 1140 1141 mutation { 1142 # we copy the values from the old predicate 1143 set { 1144 uid(v) <other> val(a) . 1145 } 1146 1147 # and we delete the old predicate 1148 delete { 1149 uid(v) <age> * . 1150 } 1151 } 1152 }' | jq 1153 ``` 1154 1155 Result: 1156 1157 ```json 1158 { 1159 "data": { 1160 "code": "Success", 1161 "message": "Done", 1162 "uids": {} 1163 }, 1164 "extensions": {...} 1165 } 1166 ``` 1167 1168 Here, variable `a` will store a mapping from all the UIDs to their `age`. The mutation 1169 block then stores the corresponding value of `age` for each UID in the `other` predicate 1170 and deletes the `age` predicate. 1171 1172 We can achieve the same result using `json` dataset as follows: 1173 1174 ```sh 1175 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'{ 1176 "query": "{ v as var(func: regexp(email, /.*@company1.io$/)) }", 1177 "delete": { 1178 "uid": "uid(v)", 1179 "age": null 1180 }, 1181 "set": { 1182 "uid": "uid(v)", 1183 "other": "val(a)" 1184 } 1185 }' | jq 1186 ``` 1187 1188 ### Bulk Delete Example 1189 1190 Let's say we want to delete all the users of `company1` from the database. This can be 1191 achieved in just one query using the upsert block as follows: 1192 1193 ```sh 1194 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 1195 upsert { 1196 query { 1197 v as var(func: regexp(email, /.*@company1.io$/)) 1198 } 1199 1200 mutation { 1201 delete { 1202 uid(v) <name> * . 1203 uid(v) <email> * . 1204 uid(v) <age> * . 1205 } 1206 } 1207 }' | jq 1208 ``` 1209 1210 We can achieve the same result using `json` dataset as follows: 1211 1212 ```sh 1213 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '{ 1214 "query": "{ v as var(func: regexp(email, /.*@company1.io$/)) }", 1215 "delete": { 1216 "uid": "uid(v)", 1217 "name": null, 1218 "email": null, 1219 "age": null 1220 } 1221 }' | jq 1222 ``` 1223 1224 ## Conditional Upsert 1225 1226 The upsert block also allows specifying conditional mutation blocks using an `@if` 1227 directive. The mutation is executed only when the specified condition is true. If the 1228 condition is false, the mutation is silently ignored. The general structure of 1229 Conditional Upsert looks like as follows: 1230 1231 ``` 1232 upsert { 1233 query <query block> 1234 [fragment <fragment block>] 1235 mutation [@if(<condition>)] <mutation block 1> 1236 [mutation [@if(<condition>)] <mutation block 2>] 1237 ... 1238 } 1239 ``` 1240 1241 The `@if` directive accepts a condition on variables defined in the query block and can be 1242 connected using `AND`, `OR` and `NOT`. 1243 1244 ### Example of Conditional Upsert 1245 1246 Let's say in our previous example, we know the `company1` has less than 100 employees. 1247 For safety, we want the mutation to execute only when the variable `v` stores less than 1248 100 but greater than 50 UIDs in it. This can be achieved as follows: 1249 1250 ```sh 1251 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 1252 upsert { 1253 query { 1254 v as var(func: regexp(email, /.*@company1.io$/)) 1255 } 1256 1257 mutation @if(lt(len(v), 100) AND gt(len(v), 50)) { 1258 delete { 1259 uid(v) <name> * . 1260 uid(v) <email> * . 1261 uid(v) <age> * . 1262 } 1263 } 1264 }' | jq 1265 ``` 1266 1267 We can achieve the same result using `json` dataset as follows: 1268 1269 ```sh 1270 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '{ 1271 "query": "{ v as var(func: regexp(email, /.*@company1.io$/)) }", 1272 "cond": "@if(lt(len(v), 100) AND gt(len(v), 50))", 1273 "delete": { 1274 "uid": "uid(v)", 1275 "name": null, 1276 "email": null, 1277 "age": null 1278 } 1279 }' | jq 1280 ``` 1281 1282 ### Example of Multiple Mutation Blocks 1283 1284 Consider an example with the following schema: 1285 1286 ```sh 1287 curl localhost:8080/alter -X POST -d $' 1288 name: string @index(term) . 1289 email: [string] @index(exact) @upsert .' | jq 1290 ``` 1291 1292 Let's say, we have many users stored in our database each having one or more than 1293 one email Addresses. Now, we get two email Addresses that belong to the same user. 1294 If the email Addresses belong to the different nodes in the database, we want to delete 1295 the existing nodes and create a new node with both the emails attached to this new node. 1296 Otherwise, we create/update the new/existing node with both the emails. 1297 1298 ```sh 1299 curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?commitNow=true -d $' 1300 upsert { 1301 query { 1302 # filter is needed to ensure that we do not get same UIDs in u1 and u2 1303 q1(func: eq(email, "user_email1@company1.io")) @filter(not(eq(email, "user_email2@company1.io"))) { 1304 u1 as uid 1305 } 1306 1307 q2(func: eq(email, "user_email2@company1.io")) @filter(not(eq(email, "user_email1@company1.io"))) { 1308 u2 as uid 1309 } 1310 1311 q3(func: eq(email, "user_email1@company1.io")) @filter(eq(email, "user_email2@company1.io")) { 1312 u3 as uid 1313 } 1314 } 1315 1316 # case when both emails do not exist 1317 mutation @if(eq(len(u1), 0) AND eq(len(u2), 0) AND eq(len(u3), 0)) { 1318 set { 1319 _:user <name> "user" . 1320 _:user <dgraph.type> "Person" . 1321 _:user <email> "user_email1@company1.io" . 1322 _:user <email> "user_email2@company1.io" . 1323 } 1324 } 1325 1326 # case when email1 exists but email2 does not 1327 mutation @if(eq(len(u1), 1) AND eq(len(u2), 0) AND eq(len(u3), 0)) { 1328 set { 1329 uid(u1) <email> "user_email2@company1.io" . 1330 } 1331 } 1332 1333 # case when email1 does not exist but email2 exists 1334 mutation @if(eq(len(u1), 0) AND eq(len(u2), 1) AND eq(len(u3), 0)) { 1335 set { 1336 uid(u2) <email> "user_email1@company1.io" . 1337 } 1338 } 1339 1340 # case when both emails exist and needs merging 1341 mutation @if(eq(len(u1), 1) AND eq(len(u2), 1) AND eq(len(u3), 0)) { 1342 set { 1343 _:user <name> "user" . 1344 _:user <dgraph.type> "Person" . 1345 _:user <email> "user_email1@company1.io" . 1346 _:user <email> "user_email2@company1.io" . 1347 } 1348 1349 delete { 1350 uid(u1) <name> * . 1351 uid(u1) <email> * . 1352 uid(u2) <name> * . 1353 uid(u2) <email> * . 1354 } 1355 } 1356 }' | jq 1357 ``` 1358 1359 Result (when database is empty): 1360 1361 ```json 1362 { 1363 "data": { 1364 "q1": [], 1365 "q2": [], 1366 "q3": [], 1367 "code": "Success", 1368 "message": "Done", 1369 "uids": { 1370 "user": "0x1" 1371 } 1372 }, 1373 "extensions": {...} 1374 } 1375 ``` 1376 1377 Result (both emails exist and are attached to different nodes): 1378 ```json 1379 { 1380 "data": { 1381 "q1": [ 1382 { 1383 "uid": "0x2" 1384 } 1385 ], 1386 "q2": [ 1387 { 1388 "uid": "0x3" 1389 } 1390 ], 1391 "q3": [], 1392 "code": "Success", 1393 "message": "Done", 1394 "uids": { 1395 "user": "0x4" 1396 } 1397 }, 1398 "extensions": {...} 1399 } 1400 ``` 1401 1402 Result (when both emails exist and are already attached to the same node): 1403 1404 ```json 1405 { 1406 "data": { 1407 "q1": [], 1408 "q2": [], 1409 "q3": [ 1410 { 1411 "uid": "0x4" 1412 } 1413 ], 1414 "code": "Success", 1415 "message": "Done", 1416 "uids": {} 1417 }, 1418 "extensions": {...} 1419 } 1420 ``` 1421 1422 We can achieve the same result using `json` dataset as follows: 1423 1424 ```sh 1425 curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '{ 1426 "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}}", 1427 "mutations": [ 1428 { 1429 "cond": "@if(eq(len(u1), 0) AND eq(len(u2), 0) AND eq(len(u3), 0))", 1430 "set": [ 1431 { 1432 "uid": "_:user", 1433 "name": "user", 1434 "dgraph.type": "Person" 1435 }, 1436 { 1437 "uid": "_:user", 1438 "email": "user_email1@company1.io", 1439 "dgraph.type": "Person" 1440 }, 1441 { 1442 "uid": "_:user", 1443 "email": "user_email2@company1.io", 1444 "dgraph.type": "Person" 1445 } 1446 ] 1447 }, 1448 { 1449 "cond": "@if(eq(len(u1), 1) AND eq(len(u2), 0) AND eq(len(u3), 0))", 1450 "set": [ 1451 { 1452 "uid": "uid(u1)", 1453 "email": "user_email2@company1.io", 1454 "dgraph.type": "Person" 1455 } 1456 ] 1457 }, 1458 { 1459 "cond": "@if(eq(len(u1), 1) AND eq(len(u2), 0) AND eq(len(u3), 0))", 1460 "set": [ 1461 { 1462 "uid": "uid(u2)", 1463 "email": "user_email1@company1.io", 1464 "dgraph.type": "Person" 1465 } 1466 ] 1467 }, 1468 { 1469 "cond": "@if(eq(len(u1), 1) AND eq(len(u2), 1) AND eq(len(u3), 0))", 1470 "set": [ 1471 { 1472 "uid": "_:user", 1473 "name": "user", 1474 "dgraph.type": "Person" 1475 }, 1476 { 1477 "uid": "_:user", 1478 "email": "user_email1@company1.io", 1479 "dgraph.type": "Person" 1480 }, 1481 { 1482 "uid": "_:user", 1483 "email": "user_email2@company1.io", 1484 "dgraph.type": "Person" 1485 } 1486 ], 1487 "delete": [ 1488 { 1489 "uid": "uid(u1)", 1490 "name": null, 1491 "email": null 1492 }, 1493 { 1494 "uid": "uid(u2)", 1495 "name": null, 1496 "email": null 1497 } 1498 ] 1499 } 1500 ] 1501 }' | jq 1502 ```