Understanding the mapping between GraphQL and DQL Schema Definitions

I want to use DQL to be able to bulk load RDF triples into DGraph and then query the loaded data by name using both DQL and GraphQL.

I understand that in order to be able to query using GraphQL, I must create a GraphQL Schema and I understand in this scenario, the best practice is to create the Dgraph schema using GraphQL and then modify it using DQL.

So first, I apply the schema using the GraphQL API for Dgraph:

mutation {
 updateGQLSchema(
   input: { set: { schema: "type Person {id: ID! name: String @search} type Film {id: ID! name: String @search}"}})
 {
   gqlSchema {
     schema
     generatedSchema
   }
 }
}

This results in a schema with these predicates:

            {
                "predicate": "Film.name",
                "type": "string",
                "index": true,
                "tokenizer": [
                    "term"
                ]
            },
            {
                "predicate": "Person.name",
                "type": "string",
                "index": true,
                "tokenizer": [
                    "term"
                ]
            },

I would like to be able to perform type agnostic queries using DQL, so I use DQL to /alter to the schema:

name: string @index(term) .

type Person {
  name
}

type Film {
  name
}

Which adds a new predicate to the schema:

            {
                "predicate": "name",
                "type": "string",
                "index": true,
                "tokenizer": [
                    "term"
                ]
            }

Now I load my data using DQL:

{
  set {
   _:luke <name> "Luke Skywalker" .
   _:luke <dgraph.type> "Person" .

   _:sr <name> "Revenge of Luke Skywalker" .
   _:sr <dgraph.type> "Film" . .

  }
}

Using DQL to query works well…I can query to get any entity with term “Luke” in the name:

{
  starwars(func: anyofterms(name, "Luke")) {
    uid
    name
  }
}

And I can get only Person entities with term “Luke” in the name:

{
  starwars(func: type(Person)) @filter(anyofterms(name, "Luke")) {
    uid
    name
  }
}

I cannot figure out how to perform these same queries using GraphQL…

I have tried:

query {
    queryPerson(filter: {
        name: {
            anyofterms: "Luke"
        }
    }) {
        id
        name
    }
}

I understand that the triples are created in Dgraph using the “name” predicate and not Person.name. I just don’t understand why Dgraph does it this way.

I am equally confused about why this DQL query does not work:

{
  starwars(func: anyofterms(Person.name, "Luke")) {
    uid
    name
  }
}

Other things I have Tried:

i’ve tried creating the schema using DQL first and then GraphQL but I get the same result.

I’ve tried using DQL to create triples with Person.name predicate (instead of just name) - in this case, GraphQL queries work but the type agnostic DQL query does not work

I’ve tried using GraphQL to addPerson entities - this lets me query using Person.name - but type agnostic name queries do not work.

I fundamentally understand that the triples are stored using the predicates I specify in my mutation…does that mean in order to satisfy my use case i have to create 2 triples for each name? for example:

{
  set {
   _:luke <name> "Luke Skywalker" .
   _:luke <Person.name> "Luke Skywalker" .
   _:luke <dgraph.type> "Person" .

   _:sr <name> "Revenge of Luke Skywalker" .
   _:sr <Film.name> "Revenge of Luke Skywalker" .
   _:sr <dgraph.type> "Film" . .

  }
}

Dgraph metadata

dgraph version

Dgraph version : v21.03.1
Dgraph codename : rocket-1
Dgraph SHA-256 : a00b73d583a720aa787171e43b4cb4dbbf75b38e522f66c9943ab2f0263007fe
Commit SHA-1 : ea1cb5f35
Commit timestamp : 2021-06-17 20:38:11 +0530
Branch : HEAD
Go version : go1.16.2
jemalloc enabled : true

This was pretty close, but the problem I believe is that the GraphQL endpoint is still trying to use the Film.name and Person.name predicates instead of the new name predicate. If you know you want to map a single predicate into two types then map it in the GraphQL schema using the dgraph directive:

type Film {
  id: ID!
  name: String! @search @dgraph(pred: "name")
}
type Person {
  id: ID!
  name: String! @search @dgraph(pred: "name")
}

Then for your use case you will not have to altar the DQL schema.

They were created with the “name” predicate because that is what you created them with in your DQL mutation. You could have set them with the “Person.name” predicate and the GraphQL would have worked in your first test, but not the type agnostic DQL query.

The reason why is to help separate fields by types. With Dgraph as you have found out, the same predicate can be used in multiple types, and while this may be what you want, this is most likely not what is wanted in the GraphQL API by default as then different search directives could cause a conflict. Having a broader range of predicates also helps to distribute the graph load better then having everything in a single type. The Person.name is really just a naming convention to help allow to have the same field in the multiple types in the GraphQL API. Under the hood the rewriter resolvers from GraphQL rewrite the field to the correct type-dotted notation or the mapped predicate if provided as my solution suggests doing.

Documentation:

https://dgraph.io/docs/graphql/dgraph/

1 Like

Makes perfect sense - thank you! I had skipped that section in the docs thinking I was not using an existing schema :flushed:

1 Like

I understand that, it should probably be titled something along the lines of GraphQL Schema Mapping instead of Existing Schema.