How to model user,posts and likes on dgraph?

I thought of this:
Three nodes: User, post, and like

type User{
    posts: [Post]
    likes: [Like]
}

type post{
    likes: [Like]
    author: User
}

type Like{
    time 
    Likeby User 
    LikedOn Post
}

Now if a user likes a post. A new node will be added with edge to post and user.
But with this method, I can not find if a user has liked the post or not.

I can also directly set edge between user and post to track likes. But what if user unlikes and likes it again. I want to know if this happens.

Any help is appreciated?

Hi, your question is unclear. Does something like this not work?

query Foo($u: User!) {
    queryLike(Likeby: $u) {
        LikedOn {
             ...
        }
    }
}

queryLike(Likeby: $u)

this won’t work.
With dgraph, we can only search on primitive fields.

What I want is …associate a like with user and post.

  1. When a user likes it we add the like.
  2. When the user unlikes it we update it so that if a user likes it again we don’t send a notification.

Now, I want to know possible data models for this use-case.

Simple words:
I want to find if a user has liked a certain post.
With data model like this: Relational Data Modeling for Modern Apps - Courses

Like {
LikeBy: User
LikedOn: Post
}

I want to find node which was liked by user User{username: “bro”} and liked on post Post{id: “oX7”}
@chewxy

Not an expert here, but I don’t see why you can’t query this as expected.

I guess I am missing something here:
But

{
queryLike() {
id
likedBy(filter: {username: {eq: “test”}}) {
username
}
}
}

I can not search on edges. Non-nullable field ‘likedBy’ (type User!) was not present in result from Dgraph. GraphQL error propagation triggered.",

^ this must not be your exact schema then… your schema most likely has LikedBy: User! and an id field

So if you want to get the likes only by a specific user then apply the filter and cascade. Or rearrange the query:

{
  queryLike @cascade {
    id
    likedBy(filter: {username: {eq: "test"}}) {
      username
    }
  }
}
{
  queryUser(filter: { username: { eq: "test" }) {
    likes {
      id
    }
  }
}

See for reference of work in progress/RFC:

This is business logic and there are several different ways to handle it. Each different way to implement this has its own tradeoffs. do you want to store a list somewhere of sent notifications, or do you want to perpetually store a Like with a delete boolean field that you change if a user deletes the like, but you keep the node itself. But then you have to factor that in when counting likes for posts… This is what makes every schema structure unique and why not every schema is the same because of business logic like this and how the developer wants to handle unique edge cases.

1 Like

Sorry, I missed this.

You are right:

this must not be your exact schema then… your schema most likely has LikedBy: User!

And, how expensive is this query. Does this kind of query search all likes. Is there an index already on likedOn and likedBy fields?

^ This is your more performant query.

Anything with cascade has the possibility to query more data from the disk and then remove extra nodes before final return.

With cascade, it retrieves all Likes from disk and all of the queried edges and predicates before trimming it down to just what you want. But to be clear, it does not get all of the edge node values because there is a filter on the likedBy edge which makes that edge only get the ones matching that filter.

You are thinking of indexing in terms of relational databases and not a graph database. Edges do not have indexes how you are thinking of indexing. If you want a deeper understaning you should go download the Dgraph whitepaper

1 Like

Yeah, the schema is designed like a relational DB. With 2 “tables”, and a 3rd “table” which is just connecting the two tables. Instead, it should be like this:

type Post {
  Author: User
  LikedBy: [User] @reverse # Add reverse index, so you can query User -> all the posts they liked.
  Text: String
  CreatedOn: Datetime
  EditedOn: Datetime
  VisibleTo: ... # if you care about privacy.
}

type User {
 Name: String
 ...
}

The Post.Text can get more complex if you care about versioning of edits and such. We have shown that in this blog post: Building a Stack Overflow Clone with Dgraph, and React - Dgraph Blog

1 Like

In this case:

type Post {
  Author: User
  LikedBy: [User] @reverse # Add reverse index, so you can query User -> all the posts they liked.
  Text: String
  CreatedOn: Datetime
  EditedOn: Datetime
  VisibleTo: ... # if you care about privacy.
}

Can I find if the edge “LikedBy” exists between Post and User efficiently?
Will this query be fine in terms of lookup?
I think that all user nodes will not be read from the disk in this query? Right?

{
  queryPost(filter: {id: {eq: "test"}}) {
    likedBy(filter: {id: "testUser"}){
      id
    }
  }
}

@amaster507
@mrjn

1 Like

Yup, the query you mentioned should be pretty efficient. It won’t read all the users.

1 Like