Using scalar to filter unrelated nodes

I am trying to filter nodes based on a single scalar value that is retrieved from an unrelated node.

Specifically, I have two types:

<project.title>: string @index(term) .
<project.users>: [uid] @count @reverse .
<user.email>: string @index(hash) @upsert .
<user.is_super>: bool .

I’m doing some basic authorization and I want users to be able to access projects they are assigned to (i.e. are in project.users), but I also want users with is_super=true to be able to access all projects and they may not be specifically assigned to them (i.e. super users may not be in project.users).

Right now I have a query like:

query q($user: string) {
  user_projects AS var(func: type(Project)) @cascade {
    project.users @filter(uid($user))
  }

  data(func: uid(user_projects)) {
    uid
    expand(_all_)
  }
}

I’d like to be able to add to that query to get all projects if is_super=true for $user . As far as I can tell, this isn’t possible because there may be no relationship between the project and the super user. I can’t seem to figure out how, and I’m not sure if it’s supported, to store either a single user, or their is_super status in a variable that could then be used in my data query, to do something like this:

{
  # ...
  data(func: type(Project)) @filter(eq(super, true) OR uid(user_projects)) {
    uid
    expand(_all_)
  }
}

In a GraphDB all is possible until we prove otherwise.

I can help you, but a sample of your structure would help a lot. Otherwise, I may end up creating an example structure that is not the same as yours and my answer would be useless.

I gonna wait for your answer.

I hope this is what you need. Let me know if you’re looking some more/different:

Schema:

project.title: string @index(term) .
project.users: [uid] @count @reverse .
user.email: string @index(hash) @upsert .
user.is_super: bool .

type User {
    user.email
    user.is_super
}

type Project {
    project.title
    project.users
}

Mutate:

{
  set {
  	_:user1 <dgraph.type> "User" .
  	_:user1 <user.email> "user1@example.com" .
  	_:user1 <is_super> "false" .

  	_:user2 <dgraph.type> "User" .
	_:user2 <user.email> "user2@example.com" .
  	_:user2 <is_super> "false" .

  	_:admin <dgraph.type> "User" .
	_:admin <user.email> "admin@example.com" .
  	_:admin <is_super> "true" .

  	_:proj1 <dgraph.type> "Project" .
  	_:proj1 <project.title> "proj1" .
  	_:proj1 <project.users> _:user1 .

  	_:proj2 <dgraph.type> "Project" .
  	_:proj2 <project.title> "proj2" .
  	_:proj2 <project.users> _:user1 .
  	_:proj2 <project.users> _:user2 .
  }
}

Check data:

{
  projects(func: type(Project)) {
    uid
    expand(_all_) {
      expand(_all_)
    }
  }
  users(func: type(User)) {
    uid
    expand(_all_)
  }
}

Current Query:

query q($user: string = "0xfffd8d67d878c36d") {
  user_projects AS var(func: type(Project)) @cascade {
    project.users @filter(uid($user))
  }

  data(func: uid(user_projects)) {
    uid
    expand(_all_)
  }
}

What I’m looking for is if $user has the uid of _:admin, then all of the projects should be returned.

I think that helps. I gonna review the whole post again. Thanks!

Check if this works for you

query q($user: string = "0x2") {
  user_projects AS var(func: type(Project)) @cascade {
    project.users @filter(uid($user))
  }
    AD as adminPower(func: uid($user)) @filter(eq(is_super, "true")) {
      is_super
      }
    ALL as var(func: type(Project)) @filter(eq(len(AD), 1)) {
      uid
    }
  
  data(func: uid(user_projects, ALL)) {
    uid
    expand(_all_)
  }

}

Note that some of your predicates has “user.is_super” but the sample you have share is “is_super”.

1 Like

That worked. Thanks!

Is the len function documented anywhere. I did some digging, but couldn’t find it.

Glad it worked

Not exactly. It is in the upsert block part (in mutations topic) but is not in the Language part. That’s something we need to add. But needs further testing to make a good doc for it.