Support nested link and multiple link

I have the following schema:

type User {
    id: String! @id
    name: String
}
type Group {
    id: String! @id
    owner: User
    members: [Member] @hasInverse(field: group)
    posts: [Post] @hasInverse(field: group)
}
type Member {
    id: String! @id
    user: User
    group: Group
    is_owner: Boolean
    level: Int
    last_publish_at: DateTime
    last_query_date: DateTime
    ...
}
type Post {
    id: String! @id
    user: User
    group: Group 
    likes: [User]
    ...
}

1,How should I create a link between User and Group based on Member?

I want to add the groups field to User in order to query my groups, it’s very simple:

type User {
    id: String! @id
    name: String
    groups: [Group] @hasInverse(field: owner)
}

But I want to know which groups I have joined, and now I am facing difficulties. The members of the group are established by Member instead of User.

I want to implement the following solution, but dgraph does not currently support:

type User {
    id: String! @id
    name: String
    join_groups: [Group] @hasInverse(field: members)
}

Or nested:

type User {
    id: String! @id
    name: String
    join_groups: [Group] @hasInverse(field: members.user)
}

2,Implement multiple links based on @hasInverse directive

I want to know which posts I have created and liked. I want to aggregate them in the posts field. I don’t want to store them separately.

So I am expecting the following solution:

type User {
    id: String! @id
    name: String
    join_groups: [Group] @hasInverse(field: members)
    posts: [Post] @hasInverse(field: [user,likes])
}

Realized through multiple links, but currently dgraph does not support.

I am looking forward to a good solution.

I don’t know if the schema I designed is reasonable, if you have better suggestions, please feel free to put forward them.

I can also custom DQL deep query to achieve the above features, but I think that query will be very slow.

Here is how I would(do) do it:

type User {
    id: String! @id
    name: String
    isMemberOf: [Member] @hasInverse(field: user)
    ownsGroups: [Group] @hasInverse(field: owner)
    authoredPosts: [Post] @hasInverse(field: user)
    postsLiked: [Post] @hasInverse(field: likes)
}
type Group {
    id: String! @id
    owner: User
    members: [Member] @hasInverse(field: group)
    posts: [Post] @hasInverse(field: group)
}
type Member {
    id: String! @id
    user: User
    group: Group
    is_owner: Boolean
    level: Int
    last_publish_at: DateTime
    last_query_date: DateTime
    ...
}
type Post {
    id: String! @id
    user: User
    group: Group 
    likes: [User]
    ...
}
{ queryUser { isMemberOf { group { id } } } }
{ queryUser { authoredPosts { id } } }
{ queryUser { postsLiked { id } } }

But then you couldn’t answer the above questions separately. They could only be answered always as either I liked this post or I created this post, I don’t know let me go look at it.

On a side note, You can create some wonky relationships like this:

posts: [Post] @hasInverse(field: user, field: likes)

BUT!! That may cause more problems down the road, FYI.

To chase the rabbit all the way to it’s whole… You may get a benefit of Unions to help with answering the questions show me the posts I have liked or I have authored. It might look a little strange, but would be interesting to see flushed out and used:

type User {
  ...
  postsLiked: [PostLiked] @hasInverse(field: user)
  postsAuthored: [PostAuthored] @hasInverse(field: user)
  postsLikedOrAuthored: [PostLikedOrAuthored] # @hasInverse(field: user) # still unsure of how Unions are going to use hasInverse.May have to manually link with JS-hooks
}

union PostLikedOrAuthored  = PostLiked | PostAuthored

type PostLiked {
  id: ID!
  user: User
  post: Post
  likedAt: DateTime
}

type PostAuthored {
  id: ID!
  user: User
  post: Post
}

type Post {
  id: ID
  author: PostAuthored @hasInverse(field: post)
  hasLikes: [PostLiked] @hasInverse(field: post)
}

IMHO, I don’t think you are to that point yet. All of this can be achieved with a better designed schema. I wouldn’t turn to mixing DQL in with custom queries until you absolutely have to. One of the main reasons, is that you lose two of the best features of having a graph database if you use a custom DQL query/mutation. 1) The ability to do deep nested queries almost endlessly and 2) The ability to only get what you need. Right now with a DQL custom query you would have to get every field as deep as could every be wanted and return it to every request, but then sometimes you only want just the root id… whoops, we just traversed a thousand+ nodes for a single id. It breaks the rules of only getting what you want, and only touching what you need.

2 Likes

@amaster507
Thanks for your answer, it helped me a lot.

1 Like

Are you transitioning from a relational database? That comes with a fear of not creating more columns (predicates in graph lang) than what is needed. Then we would build out some pretty wild join statements to get the data that is needed. In Dgraph, don’t really worry about creating too many predicates, or too many relationships until you start hitting performance issues, but then those will probably be related to other underlying issues such as a poor schema design or a big index, or a three way index as I noted above gave me some problems. In a relational database a column takes up space for every row, that is not the same in Dgraph. A predicate only takes up the space that it is filled. Want to create a type with 20,30,50+ predicates, sure why not. Mainly limited to your will power to control the beast of the schema that will be generated. Learning that sometimes the query does better from the bottom up instead of the top down, will give you a deeper understanding of what is going on. Maybe more about all of this later.

3 Likes

Yes. I am worried that if the DQL in-depth query will cause performance degradation, I am also worried that if too many predicates and relationships are established, it will cause other problems (for example, a large amount of redundant data like a relational database).

It’s the first time I use graph database, I don’t know how to design the schema is the best.

In fact, I want to display the posts I created, the posts I liked, the posts of the users I follow, and the posts liked by the users I follow on my user homepage.

So I need to aggregate them in a list so that they can be displayed on the homepage.

I hope to achieve this features through a good schema design, i want to aggregate them in a posts field instead of aggregation through DQL queries.

If I aggregate them into a list through DQL deep query, such a query would be very bad.

I don’t think this would be an issue.

Waiting on Unions would probably be the best way to achieve this IMO.

Not necessarily. Deep DQL is not really the issue. Deep queries is where Dgraph thrives! Make a query as deep as you want. A better way may be to do it in two steps. 1) use DQL to get a list of IDs that you want. 2) Use that list of IDs as a graphQL variable.

Ok thanks for your answer.