What would be the query for finding mutual friends with GraphQL?

How we find common nodes between two users or an intersection?

With GraphQL you would have to do it with 2 queries with client side logic or with a custom query using either DQL or a Lambda.

Here is DQL given this GraphQL schema:

type Person {
  id: ID!
  name: String!
  friends: [Person] @hasInverse(field: "friends")
}
{
  var(func: uid("0x2")) {
    p1Friends as Person.Friends
  }
  var(func: uid("0x3") {
    p2Friends as Person.Friends
  }
  mutualFriends(func: uid(p1Friends)) @filter(uid(p2Friends)) {
    id: uid
    name: Person.name
  }
}

This is really just an intersection, which I am not sure can be done in graphql yet without nested filters…

type Person {
  id: ID!
  name: String!
  friends: [Person] @hasInverse(field: "friends")
}

I think what we really want is nested filters (hopefully we will have this soon):

query {
  queryPerson(filter: { friends: { id: ['0x1', '0x2'] } }) {
    id
    name
  }
}

But we could for now:

query {
  Q1: getPerson(id: '0x1') {
    friends {
      id
    }
  },
  Q2: getPerson(id: '0x2') {
    friends {
      id
    }
  }
}

OR

query {
  queryPerson(filter: { id: ['0x1', '0x2'] }) {
    friends {
      id
    }
  }
}

Then intersect those results in typescript…

I am wondering if there is a way to use @custom fields to create a mutualFriends field…

J

I’ll post this here too for reference of a full example of how you could write DQL inside of the GraphQL in a custom query

https://dgraph.io/docs/graphql/custom/dql/

IMHO, I would only use custom dql for aggregation, unless it is impossible otherwise due to the problems here:

No paging, @auth, etc…

EDIT: Then again, filtering on the frontend has its own problems… :frowning:

J

4/9 of the items on that list have been resolved and 2 won’t/can’t fix.

Yes and no. @custom now works with @auth to some degree: WIP: Auth rules custom DQL query, I believe paging (at least param passing) works. But maybe not the complete control that a user wants. SO then the other option is to put the DQL in a lambda (mixed emotions) and then do your custom logic, filtering, auth, in that lambda script.

Filtering has limitations, yes, but every API will have their own set of limitations on filtering to some degree. For a known unique filter need, a custom query or lambda script may still be a viable option and suffice the need without needing to create an entirely backend layer on top of Dgraph.

With all of this being true, and if this find mutual friends was a common query need, I wouldn’t mind making a custom query for it. Given my example above, if there were any auth rules on the Person type, they would trickle down to a custom query that returned nodes of that type which would suit most of the need most likely.

Would a option to filter child nodes be a better fix for this situation if RFC: Nested Filters in GraphQL was inmplemented sometime down the road? Most likely (if it was done correctly) But that query would probably look like:

query {
  queryPerson(filter: {
    and: [
      { friends: { id: ['0x1'] } }
      { friends: { id: ['0x2'] } }
    ]
  ) {
    id
    name
  }
}

to check for having BOTH instead of just having EITHER

Yup yup… all the custom dql stuff I just need to experiment with to really get down what it can and can’t do. I have not been following it, so good to know there are some enhancements coming.

Lol, I pretty much just have one emotion on this one.

Whoops… I guess the OR here is an outer join, when we want an inner join.


Wouldn’t this currently work on that note?

query {
  queryPerson @cascade {
    friends(filter: {
      and: [
        { id: '0x1' },
        { id: '0x2' }
      ]
    }) {
      id
      name
    }
  }
}

J

You cannot have a friend that has both of those IDs, but you can have two friends with those IDs.

This would be like (pretending no need for pivot tables)

SELECT p.*
FROM
person p
INNER JOIN person f ON p.friends=f.id AND f.id=1 AND f.id=2

vs.

SELECT p.*
FROM
person p
INNER JOIN person f1 ON p.friends=f1.id AND f1.id=1
INNER JOIN person f2 ON p.friends=f2.id AND f2.id=2

Yup yup. Makes sense. I don’t want an array of two groups of friends, but a joined group of friends.

It is crazy to me that this cannot be done without nested filters in graphql.

J

1 Like