External IDs and Upsert Block - Mutations

The upsert block makes managing external IDs easy.

Set the schema.

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

Set the type first of all.

{
  set {
    _:blank <xid> "http://schema.org/Person" .
    _:blank <dgraph.type> "ExternalType" .
  }
}

Now you can create a new person and attach its type using the upsert block.

   upsert {
      query {
        var(func: eq(xid, "http://schema.org/Person")) {
          Type as uid
        }
        var(func: eq(<http://schema.org/name>, "Robin Wright")) {
          Person as uid
        }
      }
      mutation {
          set {
           uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" .
           uid(Person) <http://schema.org/type> uid(Type) .
           uid(Person) <http://schema.org/name> "Robin Wright" .
           uid(Person) <dgraph.type> "Person" .
          }
      }
    }

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.

   upsert {
      query {
        var(func: eq(xid, "http://schema.org/Person")) {
          Type as uid
        }
        var(func: eq(<http://schema.org/name>, "Robin Wright")) {
          Person as uid
        }
      }
      mutation {
          delete {
           uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" .
           uid(Person) <http://schema.org/type> uid(Type) .
           uid(Person) <http://schema.org/name> "Robin Wright" .
           uid(Person) <dgraph.type> "Person" .
          }
      }
    }

Query by user.

{
  q(func: eq(<http://schema.org/name>, "Robin Wright")) {
    uid
    xid
    <http://schema.org/name>
    <http://schema.org/type> {
      uid
      xid
    }
  }
}

This is a companion discussion topic for the original entry at https://dgraph.io/docs/mutations/external-ids-upsert-block/

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