graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Variable-length relationship pattern matching

Open AccsoSG opened this issue 4 years ago • 8 comments

Use case: I have nodes of the same type (label) that can have an "IS_EQUIVALENT_TO" relationship with each other. For this I would like to model an "equivalent" field in the GraphQL schema. When creating such "equivalents" a unidirectional relationship is created. So far, so good. However, only this one direction is taken into account in a query.

Here is my example:

Extract from the scheme

type IdentifierType {
  id: ID! @id
  name: String
  equivalents: [IdentifierType] @relationship(type: "IS_EQUIVALENT_TO", direction: OUT)
}

Mutation to create the nodes:

mutation {
  createIdentifierTypes(input: {
    name: "Test1", 
    equivalents: {
      create: {
        node: {
          name: "Test2"
        }
      }
    }
  }) {
    identifierTypes {
      id
      equivalents {
        id
        equivalents {
          id
        }
      }
    }
  }
}

Created nodes: image

Mutation response:

{
  "data": {
    "createIdentifierTypes": {
      "identifierTypes": [
        {
          "id": "8ab86b3b-7eec-49eb-9682-9fc22d37fb52",
          "equivalents": [
            {
              "id": "58d3ca1d-6395-40cb-ab8d-76d9d4127410",
              "equivalents": [
              ]
            }
          ]
        }
      ]
    }
  }
}

Describe the solution you'd like A way to ignore the direction of the relationship to get the following result:

{
  "data": {
    "createIdentifierTypes": {
      "identifierTypes": [
        {
          "id": "8ab86b3b-7eec-49eb-9682-9fc22d37fb52",
          "equivalents": [
            {
              "id": "58d3ca1d-6395-40cb-ab8d-76d9d4127410",
              "equivalents": [
                {
                  "id": "8ab86b3b-7eec-49eb-9682-9fc22d37fb52"
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Further considerations: It could also be a use case to query all "equivalents" recursively. For example, the following nodes are in the database: image Now I want to recursively get all connected nodes (in both directions). Maybe circular relationships are a challenge here?

AccsoSG avatar Nov 18 '21 01:11 AccsoSG

Hey @AccsoSG, thanks for raising! Although in slightly different contexts, we already have this tracked in #142 and #353, but the principle remains the same.

Further considerations: It could also be a use case to query all "equivalents" recursively. For example, the following nodes are in the database: image Now I want to recursively get all connected nodes (in both directions). Maybe circular relationships are a challenge here?

This, however, we have eyes on internally, but I don't believe is tracked in a GitHub issue. Really this is describing variable length relationship matching, so if you don't mind, I will re-title your issue just to focus on that given that the other feature request is already tracked elsewhere? 🙂

darrellwarde avatar Nov 22 '21 09:11 darrellwarde

Hi @darrellwarde , Thanks for the feedback. Feel free to rename the issue.

AccsoSG avatar Nov 25 '21 10:11 AccsoSG

Just to chime in, this would be a great feature to have! Spoke to several users that are looking for this functionality.

nielsdejong avatar May 04 '22 08:05 nielsdejong

Just wanted to upvote this idea. From my perspective, a variable length GraphQL query would make this library extremely valuable.

Assuming that we can't represent a variably recursive mutation in GraphQL notation, are there any other ways to get a tree-like data structure from the database?

type Item = {
  id: string;
  children?: Item[]
}

barbinbrad avatar Nov 18 '22 04:11 barbinbrad

I would also add that a variable length relationship matching would be incredibly valuable. In my graphs currently I've had to define "variable" length relationships within my graph in a strict manner.

For example: A relationship (IS_FRIEND_WITH3) which represents within 3 hops of the (IS_FRIEND_WITH), adding many relationships to a graph as each person would have many relationships connecting it to all of their friends, their friends of friends (2 hops), and their friends of friends of friends (3 hops).

The benefit of this IS_FRIEND_WITH3 relationship is that you could find a Person where any of their "loosely connected" friends (friends, friends of friends, and friends of friends of friends) satisfy a condition. Without this relationship, you'd have to have a large OR statement representing each hop.

Current Example Schema:

type Person {
  name: String
  friends: [Person!]! @relationship(type: "IS_FRIEND_WITH", direction: OUT)
  friends3: [Person!]! @relationship(type: "IS_FRIEND_WITH3", direction: OUT)
}

query with IS_FRIEND_WITH3 relationship

query {
  persons(where: {friends3_SOME: {name: "Sam"} } ) {
  ...

query without the IS_FRIEND_WITH3 relationship

query {
  persons(where: {
  OR: [
   { friends_SOME: {name: "Sam"} }
  { friends_SOME: {friends_SOME: {name: "Sam"} } },
  { friends_SOME: {friends_SOME: {friends_SOME: {name: "Sam"} } } }
]} ) ...

It'd be help out the cleanliness of the graph a lot to be able to just define these number of hops in the schema, for example:

type Person {
  id: ID! @id
  name: String
  friends: [Person!]! @relationship(type: "IS_FRIEND_WITH", direction: OUT)
  friends3: [Person!]! @relationship(type: "IS_FRIEND_WITH",  numberHops: 3, direction: OUT)
}

Where numberHops would represent up to 3 hops (a variable length relationship of IS_FRIEND_WITH*1..3)

It'd be even more amazing if you provide the number of hops in the query such as

query {
  persons(where: {friends*1..3: {name: "Sam"} } ) {
  ...

jrsperry avatar Dec 07 '22 18:12 jrsperry

I would also add that a variable length relationship matching would be incredibly valuable. In my graphs currently I've had to define "variable" length relationships within my graph in a strict manner.

For example: A relationship (IS_FRIEND_WITH3) which represents within 3 hops of the (IS_FRIEND_WITH), adding many relationships to a graph as each person would have many relationships connecting it to all of their friends, their friends of friends (2 hops), and their friends of friends of friends (3 hops).

The benefit of this IS_FRIEND_WITH3 relationship is that you could find a Person where any of their "loosely connected" friends (friends, friends of friends, and friends of friends of friends) satisfy a condition. Without this relationship, you'd have to have a large OR statement representing each hop.

Current Example Schema:

type Person {
  name: String
  friends: [Person!]! @relationship(type: "IS_FRIEND_WITH", direction: OUT)
  friends3: [Person!]! @relationship(type: "IS_FRIEND_WITH3", direction: OUT)
}

query with IS_FRIEND_WITH3 relationship

query {
  persons(where: {friends3_SOME: {name: "Sam"} } ) {
  ...

query without the IS_FRIEND_WITH3 relationship

query {
  persons(where: {
  OR: [
   { friends_SOME: {name: "Sam"} }
  { friends_SOME: {friends_SOME: {name: "Sam"} } },
  { friends_SOME: {friends_SOME: {friends_SOME: {name: "Sam"} } } }
]} ) ...

It'd be help out the cleanliness of the graph a lot to be able to just define these number of hops in the schema, for example:

type Person {
  id: ID! @id
  name: String
  friends: [Person!]! @relationship(type: "IS_FRIEND_WITH", direction: OUT)
  friends3: [Person!]! @relationship(type: "IS_FRIEND_WITH",  numberHops: 3, direction: OUT)
}

Where numberHops would represent up to 3 hops (a variable length relationship of IS_FRIEND_WITH*1..3)

It'd be even more amazing if you provide the number of hops in the query such as

query {
  persons(where: {friends*1..3: {name: "Sam"} } ) {
  ...

100% -- this is what I was getting at. Thanks for the limits and suggestions.

barbinbrad avatar Dec 07 '22 20:12 barbinbrad

A proposal design for this will be discussed in https://github.com/neo4j/graphql/discussions/3182

angrykoala avatar Apr 12 '23 14:04 angrykoala

any update on this?

btroop avatar Apr 14 '24 01:04 btroop