I am really liking this suggestion out of the whole thread so far. This is really the summarization of the problem and the solution.
Furthermore, I believe that a facet should also be tied to a parent type and not just it’s type. A facet is information stored on the edge, so in GraphQL edges exist 1) between two types and 2) between a type and it’s predicates.
So a NickName type could be a child of many different Types. All of these parent types might not have the same facets on their edges as it just doesn’t make sense.
Take for example the friend facet data:
type Person {
id: ID
name: String!
friends: [Person]
parents: [Person]
children: [Person]
close: Boolean @dgraph(facet: "close", on: ["Person.friends"])
}
type Business {
id: ID
name: String!
employees: [Person]
}
This would then notate that the Person.close would only be applicable to query if the parent edge was coming from Person.friends, but not on Person.parents, Person.children, nor Business.employees.
If this is implemented well, this could go great measures to simplify (and complicate) schemas with a use case such as keeping a type of relationship on the edge.
enum RelationshipType {
friend
parent
child
coworker
}
type Person {
id: ID
name: String!
relationships: [Person]
close: Boolean @dgraph(facet: "close", on: ["Person.relationships"])
relationshipType: RelationshipType @dgraph(facet: "type", on: ["Person.relationships"])
}
Another thing that quickly becomes problematic without the on data is how to enforce a required facet. If the Person is queries at the root then the facet will always be null, so a required facet will always be missing here. And if a Person is subQueried under Business.employees they would not have the same facet requirements as if they were under Person.friend
yeah, we have discussed this point and find out that facets inside Animal type are easy to handle and solve many problems. We need to store the facets somewhere and it’s not a good idea to store them inside types which reference Animal.
The next thing is representing facets in graphql. I think we can change the @facets directive with @dgraph(facet:) and rest of the things will be same.
And we will convert the Dgraph response to a valid Graphql response as discussed in detail in previous comments.
We need to explicitly query the facet in order for them to included in the query response. So, null can only be returned from the response if the predicate doesn’t have queried facet.
Fair point. @JatinDevDG is going to provide an example of how this modelling might look if done through the @dgraph directive. The [scalar] list case is a bit tricky because in the example that you mentioned above, it looks like we are creating a new node for nickname. Hence there is no way of differentiating between [uid] and [scalar] for facets.
If we do provide an on argument, what is the expected behaviour? Do you expect the API to throw an error if a user queries or mutates the facet along an edge it is not supposed to? We don’t really need the on argument as we expect the user to know which edge a facet belongs to and expect them to only provide it on mutations/queries along that edge.
Given that the user would know that they don’t have facets at root, they won’t be querying them I suppose. That’s a bad query I would say?
Hi, sorry for barging in, but I think this (Traverse on different types) is a good example of a usecase where facets could shine in GraphQL (assuming we get the sorting and first: thing of the facets fixed)
In that post the OP wants to compute a path, and a graph database should be able to do that trivially, without resorting to the kinds of data structure that the OP has created. But because of the limitations of GraphQL, the extended tuple data structure (i.e. encoding edge data into a vertex, leaving the edges unlabelled) was used .
Maybe this is something I can be more onboard with
Ok, fair enough, so I can move the facet into a type and do a more natural modelling. Great!
For Scalar and [Scalar], I don’t like it as much … because
For me, this is technically ok:
it gives you a way to export a Dgraph facet into a GraphQL type, sure, but I wouldn’t do that modelling: why is petClose a property of a person? Regardless of how it’s implemented in the underlying database, I’d want to model this more like
type Person {
name: String!
ownsPet: PetOwnership
}
type PetOwnership {
pet: String
close: Boolean!
since: DateTime
}
or, if pet is really worthy of it’s own type, put pet into it’s own type and also model the pet ownership relation.
So I’d always go with the UID option and model it that way. But I can’t here cause the UID options and Scalar options are different in their GraphQL representation… so if I did have facets on scalars in my DB, then I wouldn’t be able to get the modelling I want … but then I probably wouldn’t have have done it that way anyway, so maybe I don’t mind? In the end, I’d like to be able to model scalar facets the ‘right’ way, but maybe this works first up.
Neo4J’s GraphQL adapter solves relationship properties (facets) by creating an extra level of nesting between the parent and the child. I think this is the only way that will work with complex graph structures.
type Movie {
title: String
year: Int
ratings: [Rated]
}
type User {
userId: ID
name: String
rated: [Rated]
}
type Rated @relation(name: "RATED") {
from: User
to: Movie
rating: Float
created: DateTime
}
Hi,
There is no PR to track currently. This is assigned to me in current sprint and is being worked upon. Last 2 weeks we were busy with 21.03 bug fixes and testing and didn’t get time for this.
I have kind of been thinking in the interim, we could do something like that with one caveat:
type Movie {
title: String!
year: Int!
ratings: [Rating]
}
type User {
userId: ID!
name: String!
ratings: [Rating]
}
type Rating {
id: String! @id
user: User! @hasInverse(field: ratings)
movie: Movie! @hasInverse(field: ratings)
rating: Float!
createdAt: DateTime
}
Basically, when we would create a rating, we would create the id as user_movie forcing a unique (composite) identifier based on the relationship.
This may even be better than using facets. I am wondering if there is even a speed difference between a facet and a new type, considering it is all stored as a triple in the database anyway.
From what I understand adding the facets to graphql (and hence into generated filters) would allow you to mutate/delete with the capability of filtering based on and/or altering the relationships much easier (removal if you’re deleting the object) compared to having a type defined for that relationship (since types are not searchable or added to filters), correct? Would neatly give a work around for a lot of the requests for nested filters/cascading deletes for many use-cases if that’s the case.
The PR mentioned here was just for schema changes. This is an involved undertaking, we don’t have a timeline for this feature. I’d recommend running DQL queries / lambdas to get facets from GraphQL.