Motivation
Add native support for @facets
in GraphQL
User Impact
Users will now be able to mutate and query facets in GraphQL.
Implementation
1. Scalar
type Person {
name: String!
petClose: Boolean @facet(field: "pet", name: "close")
petSince: DateTime @facet(field: "pet", name: "since")
pet: String
}
The above input GraphQL schema will generate the following types in the output schema:
input AddPersonInput {
name: String!
pet: PersonPetFacetRef
}
input PersonPetFacetRef {
value: String
petClose: Boolean
petSince: DateTime
}
type AddPersonPayload {
person: PersonPayload
numUids: Int
}
type PersonPayload {
name: String!
pet: PersonPetFacetPayload
}
type PersonPetFacetPayload {
value: String
petClose: Boolean
petSince: DateTime
}
The following GraphQL mutation
mutation {
addPerson(input: [{
name: "Carol", pet:{ value:"Jerry",petClose:true, petSince: 2006-01-02T15:04:05}}]) {
person {
...
}
}
}
would be rewritten to this DQL mutation
{
"uid": "_:Carol",
"Person.name": "Carol",
"dgraph.type": "Person",
"Person.pet": "Jerry",
"Person.pet|since": "2006-01-02T15:04:05",
"Person.pet|close": true
}
The GraphQL query
{
queryPerson {
name
pet {
value
petClose
petSince
}
}
}
would be rewritten to
{
queryPerson(func: type(Person)) {
Person.name
Person.pet @facets(close,since)
}
}
Dgraph Response
{
"data": {
"queryPerson": [
{
"Person.name": "Carol",
"Person.pet": "Jerry",
"Person.pet|close": true,
"Person.pet|since": "2006-01-02T15:04:05"
}
]
}
}
GraphQL Response
{
"data": {
"queryPerson": [
{
"name": "Carol",
"pet": {
"value": "Jerry",
"petClose": true,
"petSince": "2006-01-02T15:04:05"
}
}
]
}
}
2. UID
type Person {
name: String!
petClose: Boolean @facet(field: "pet", name: "close")
petSince: DateTime @facet(field: "pet", name: "since")
pet: Animal
}
type Animal {
id: ID!
name: String!
color: String
}
The above input GraphQL schema will generate the following types in the output schema:
input AddPersonInput {
name: String!
pet: PersonPetFacetRef
}
input AnimalRef {
id: ID!
name: String!
color: String
}
input PersonPetFacetRef {
value: AnimalRef
petClose: Boolean
petSince: DateTime
}
type AddPersonPayload {
person: PersonPayload
numUids: Int
}
type PersonPayload {
name: String!
pet: PersonPetFacetPayload
}
type PersonPetFacetPayload {
value: Animal
petClose: Boolean
petSince: DateTime
}
The following GraphQL mutation
mutation {
addPerson(input: [{
name: "Carol", pet:{ value:{name:"Jerry",color:"Gray"},petClose:true,petSince: "2006-01-02T15:04:05"}}]) {
person {
...
}
}
}
would be rewritten to this DQL mutation
{
"uid": "_:Carol",
"Person.name": "Carol",
"dgraph.type": "Person",
"Person.pet": {
"uid": "_:Jerry",
"Animal.name": "Jerry",
"Animal.color": "Gray",
"Person.pet|close": true,
"Person.pet|since": "2006-01-02T15:04:05",
"dgraph.type": "Animal"
}
}
GraphQL query
{
queryPerson {
name
pet {
value{
name
color
}
petClose
petSince
}
}
}
would be rewritten to
{
queryPerson(func: type(Person)) {
Person.name
Person.pet @facets(close,since) {
Animal.name
Animal.color
}
}
}
Dgraph Response:
{
"data": {
"queryPerson": [
{
"Person.name": "Carol",
"Person.pet": {
"Animal.name": "Jerry",
"Animal.color": "Gray",
"Person.pet|close": true,
"Person.pet|since": "2006-01-02T15:04:05"
}
}
]
}
}
GraphQL Response:
{
"data": {
"queryPerson": [
{
"name": "Carol",
"pet": {
"value": {
"name": "Jerry",
"color": "Gray"
},
"petClose": true,
"petSince": "2006-01-02T15:04:05"
}
}
]
}
}
3. [UID]
type Person {
name: String!
petClose: Boolean @facet(field: "pet", name: "close")
petSince: DateTime @facet(field: "pet", name: "since")
pet: [Animal]
}
type Animal {
id: ID!
name: String!
color: String
}
The above input GraphQL schema will generate the following types in the output schema:
input AddPersonInput {
name: String!
pet: [PersonPetFacetRef]
}
input AnimalRef {
id: ID!
name: String!
color: String
}
input PersonPetFacetRef {
value: AnimalRef
petClose: Boolean
petSince: DateTime
}
type AddPersonPayload {
person: PersonPayload
numUids: Int
}
type PersonPayload {
name: String!
pet: [PersonPetFacetPayload]
}
type PersonPetFacetPayload {
value: Animal
petClose: Boolean
petSince: DateTime
}
The following GraphQL mutation
mutation {
addPerson(input: [{
name: "Carol", pet: [
{value: {name: "Jerry"}, petClose:true, petSince: "2006-01-02T15:04:05"},
{value: {name: "Tom", color: "Grey"}, petClose:false, petSince: "2007-01-02T15:04:05"},
{value: {name: "Donald"}, petClose:false, petSince: "2008-01-02T15:04:05"}, }]}]) {
numUids
}
}
would be rewritten to this DQL mutation
{
"Person.name": "Carol",
"dgraph.type": "Person",
"Person.pet": [
{
"Animal.name": "Jerry",
"Person.pet|close": true,
"Person.pet|since": "2006-01-02T15:04:05",
"dgraph.type": "Animal"
},
{
"Animal.name": "Tom",
"Animal.color": "Grey",
"Person.pet|close": false,
"Person.pet|since": "2007-01-02T15:04:05",
"dgraph.type": "Animal"
},
{
"Animal.name": "Donald",
"Person.friend|close": "false",
"Person.pet|since": "2008-01-02T15:04:05",
"dgraph.type": "Animal"
}
]
}
GraphQL query
{
queryPerson {
name
pet {
value {
name
color
}
petClose
petSince
}
}
}
would be rewritten to
{
queryPerson(func: type(Person)) {
Person.name
Person.pet @facets(close,since) {
Animal.name
Animal.color
}
}
}
Dgraph Response:
{
"data": {
"queryPerson": [
{
"Person.name": "Carol",
"Person.pet": [
{
"Animal.name": "Jerry",
"Person.pet|close": true,
"Person.pet|since": "2006-01-02T15:04:05"
},
{
"Animal.name": "Tom",
"Animal.color": "Grey",
"Person.pet|close": false,
"Person.pet|since": "2007-01-02T15:04:05"
},
{
"Animal.name": "Donald",
"Person.friend|close": "false",
"Person.pet|since": "2008-01-02T15:04:05"
}
]
}
]
}
}
GraphQL Response:
{
"data": {
"queryPerson": [
{
"name": "Carol",
"pet": [
{
"value": {
"name": "Jerry",
"color": null
},
"petClose": true,
"petSince": "2006-01-02T15:04:05"
},
{
"value": {
"name": "Tom",
"color": "Grey"
},
"petClose": false,
"petSince": "2007-01-02T15:04:05"
},
{
"value": {
"name": "Donald",
"color": null
},
"petClose": false,
"petSince": "2008-01-02T15:04:05"
}
]
}
]
}
}
4. [Scalar]
type Person {
name: String!
petClose: Boolean @facet(field: "pet", name: "close")
petSince: DateTime @facet(field: "pet", name: "since")
pet: [String]
}
The above input GraphQL schema will generate the following types in the output schema:
input AddPersonInput {
name: String!
pet: [PersonPetFacetRef]
}
input PersonPetFacetRef {
value: String
petClose: Boolean
petSince: DateTime
}
type AddPersonPayload {
person: PersonPayload
numUids: Int
}
type PersonPayload {
name: String!
value: [PersonPetFacetPayload]
}
type PersonPetFacetPayload {
value: String
petClose: Boolean
petSince: DateTime
}
The following GraphQL mutation
mutation {
addPerson(input: [{
name: "Carol", pet: [
{ value: "Jerry", petClose: true, petSince: "2006-01-02T15:04:05" },
{ value: "Tom", petSince: "2007-01-02T15:04:05" },
{ value: "Donald", petClose: false }
]
}])
{
numUids
}
}
would be rewritten to this DQL mutation
{
"Person.name": "Carol",
"dgraph.type": "Person",
"Person.pet": [
"Jerry",
"Tom",
"Donald"
],
"Person.pet|close": {
"0": true,
"2": false
},
"Person.pet|since": {
"0": "2006-01-02T15:04:05",
"1": "2007-01-02T15:04:05"
}
}
The GraphQL query
{
queryPerson {
name
pet {
value
petClose
petSince
}
}
}
would be rewritten to
{
queryPerson(func: type(Person)) {
Person.name
Person.pet @facets(close,since)
}
}
Dgraph Response:
{
"data": {
"queryPerson": {
"Person.name": "Carol",
"Person.pet": [
"Jerry",
"Tom",
"Donald"
],
"Person.pet|close": {
"0": true,
"2": false
},
"Person.pet|since": {
"0": "2006-01-02T15:04:05",
"1": "2007-01-02T15:04:05"
}
}
}
}
GraphQL Response:
{
"data": {
"queryPerson": [
{
"name": "Carol",
"pet": [
{
"value": "Jerry",
"petClose": true,
"petSince": "2006-01-02T15:04:05"
},
{
"value": "Tom",
"petClose": null,
"petSince": "2007-01-02T15:04:05"
},
{
"value": "Donald",
"petClose": false,
"petSince": null
}
]
}
]
}
}
Interaction with existing directives
-
@hasInverse
: Canât be applied on a field with@facet
. But, should continue to work the same on any other object field, even on the field which has facets. -
@search
: Facets donât require any indexing, so explicitly specifying@search
wonât have any effect. Facet fields will have filtering built automatically. -
@dgraph
: NA -
@id
: NA. -
@withSubscription
: NA. -
@secret
: NA -
@auth
: NA -
@custom
: NA -
@cascade
: Wonât have any effect on facet fields in the query. Should work as expected for other fields.
Open Questions
Mutation
- While adding/updating, facets would only be rewritten as part of a DQL mutation, if the field on which the facet exist was also added/updated. If only facets are provided for add/update without the corresponding field, then they are not rewritten into the DQL mutation and are dropped. Update case needs to be checked?
Queries
- If the user requests a facet without the field that it is on, should we handle it or should we return an error?
References
- Facets in Dgraph Query: https://dgraph.io/docs/query-language/facets/
- Facets in JSON format: https://dgraph.io/docs/mutations/json-mutation-format/
- Facet List Type in Dgraph: https://dgraph.io/docs/mutations/facets-in-list-type-with-rdf/