External IDs and Upsert Block - Mutations

This example could be simplified quite a bit and expanded with additional details, especially for common web developers who need to user external ids (xids) but don’t really need to think about all of the weirdness of http://schema.org/... stuff, or for those using multiple external identifier sources.

So here is my go at rewriting this doc page:


The upsert block provides a way to mutate the graph while using external IDs (xids).

Given the schema:

xid: string @index(exact) .
name: string @index(exact) .
starring: [uid] @reverse .
birthdate: datetime .
year: int .

type Actor {
  xid
  name
  birthdate
}

type Movie {
  xid
  name
  starring
  year
}

Create initial data of a movie and an actor with external identifiers (example uses imdb identifiers):

{
  set {
    _:nm0000705 <dgraph.type> "Actor" .
    _:nm0000705 <xid> "nm0000705" .
    _:nm0000705 <name> "Robin Wright" .

    _:tt0109830 <dgraph.type> "Movie" .
    _:tt0109830 <xid> "tt0109830" .
    _:tt0109830 <name> "Forest Gump" .
  }
}

Then link the two nodes by external identifiers and update nodes to add additional attributes:

upsert {
  query {
    nm0000705 as var(func: eq(xid, "nm0000705"))
    tt0109830 as var(func: eq(xid, "tt0109830"))
  }
  mutation {
    set {
      uid(tt0109830) <starring> uid(nm0000705) .
      uid(nm0000705) <birthdate> "1966-04-08" .
      uid(tt0109830) <year> "1994" .
    }
  }
}

You can also use an upsert block to update the xid itself.

The original data set mutation:

{
  set {
    _:nm0000128 <dgraph.type> "Actor" .
    _:nm0000128 <xid> "nm0000128" .
    _:nm0000128 <name> "Tom Cruise" .
  }
}

Correct the xid using a delete and set in the same upsert mutation.

upsert {
  query {
    nm0000128 as var(func: eq(xid, "nm0000128"))
  }
  mutation {
    delete {
      uid(nm0000128) <xid> "nm0000128" .
    }
    set {
      uid(nm0000128) <xid> "nm0000129" .
    }
  }
}

Note: This example uses the S P O delete method. For more information on delete methods see: https://dgraph.io/docs/mutations/delete/

It is possible that a singular node could have more than a singular external identifier. The important factor is that no two nodes share the same external identifer for the same predicate name. The predicate xid here is for an example, and could be of another name such as uuid. You could also use multiple predicates for unique external identifiers such as imdb and rottentomatoes

Advance Topic: Mixing DQL external identifiers with the GraphQL API:

If using external identifiers with Dgraph’s GraphQL API, you can map the schema so all types reference the same external identifier predicate.

# Dgraph's GraphQL Schema
type Movie {
  id: ID!
  xid: String! @id @dgraph(pred: "xid")
  name: String! @search(by: [exact])
  starring: [Actor] @hasInverse(field: "starringIn")
  year: Int
}

type Actor {
  id: ID!
  xid: String! @id @dgraph(pred: "xid")
  name: String! @search(by: [exact])
  starringIn: [Movie]
  birthdate: DateTime
}

Which would generate the following DQL schema:

type Movie {
  xid
  Movie.name
  Movie.starring
  Movie.year
}
type Actor {
  xid
  Actor.name
  Actor.starringIn
  Actor.birthdate
}
xid: string @index(exact) .
Movie.name: string @index(exact) .
Movie.starring: [uid] .
Movie.year: int .
Actor.name: string @index(exact) .
Actor.starringIn: [uid] .
Actor.birthdate: datetime .

For more information on GraphQL <>DQL schema mapping see: New to Dgraph? DQL vs. GraphQL, Endpoints, Headers, and Schema Translation

1 Like