Hey @core-devs
We added scalar type support to dgraph schema last week. Have been thinking about what would be the best way to add objects category to it. Here are my thoughts:
Note: This is part of implementation outlined here - Supporting type and schema in Dgraph through GraphQL
###A few constraints/assumptions/deviations from Graphql that we want to have:
- Predicates should have universal types. This means that if we define an edge
name
to be of typestring
, it will always be of this type irrespective which entity it is coming out of. - We want the object types implementation to be as lean and simplistic as possible. This means forgoing GraphqL constructs like Interfaces, Enums, etc. for now. We can add them later as required.
###Object types will have following properties (points explained by the following example):
- Since we are forgoing the interface GraphQL type, we just have one object category to denote all entities in the system.
- Object struct will have a list of attributes (along with their types stored as a map) to denote possible edges for that entity.
- Although objects could just be denoted with a
name
, storing this kind of attribute list will help in query/mutation validations. - Edges coming out of object can either point to scalar types or to another entities, so attribute types will have two kinds:
- scalar types, denoted as
<predicate> -> <scalarType>
- entity types, denoted as
<predicate> -> [<objectType1>, <objectType2>, ...]
- scalar types, denoted as
- For entity type mappings, list can contain multiple object types since an edge coming out of an entity can point to varioues entity types.
- Example: A
friend
edge/predicate coming out from a personType entity (e.g.Tarzan
) can point to apersonType
entity or ananimalType
entity.
- Example: A
- Every instance of object type will have a
Name
field specified by the client through schema file.
Example of the schema file to instantiate object types as defined above:
- Note that
friend
attribute inperson
points to a multi-element list, this corresponds to point 5 above. - Objects in schema MUST be declared before outlining their attributes (this can be made optional through some smart code, like inferring nested json as object types)
- One thought is to have
species
attribute directly match tospeciesType
instead through alist
to denote presence of exactly one outward edge.
{
"name": "string",
"age": "int",
// all object types must be defined before hand
"robot": "object",
"person": "object",
"speciesType": "object",
// object types expanded with attributes
"robot": {
"name": "string", // for scalar types, only previously specified type definition will be considered
"friend": ["person"], // [...] denotes a list (multiple edges)
"creator": ["person"],
"species": ["speciesType"]
},
"person": {
"name": "string",
"friend": ["person","robot"],
"father": ["person"],
"species": ["speciesType"]
},
"speciesType": {
"name": "string"
}
}
###The Schema parser will work as follows:
- Define and store scalar types as encountered (
name
andage
) - If object type encountered:
- define and instantiate with
obj.name = <key>
if not present inschema
. - ignore if present
- define and instantiate with
- If nested json is encountered:
- If an object is not present with
obj.name = <key>
, throw error. - If present, append attributes to it.
- Note: If any attribute was already defined at root level, it’s type will be inferred from there otherwise a new
<predicate> -> <scalarType>
pair will be added to theschema
. - This would ensure that in future, when we want object specific types for scalars, we can have them without much ado.
- If an object is not present with
Have tried to include as much explanation as possible in this post, but please do let me know any doubts or suggestions regarding this.
Thanks!
Annotations:
FAQ:
- Why objects are explicitly defined in schema file beforehand and not inferred directly.
- To handle cases like this:
"animal": {
....
"friend": ["person", "animal"],
...
},
"person": {
"friend": ["person","animal"],
...
}