Maintaining hierarchy constraints in nested tree

I'm looking for strategies to handle parts of a tree where I need to maintain certain contstraints as you go two levels deep. Consider this GraphQL example:

{
  text{
    body
    names{
      name
      startIndex
      endIndex
      people {
        title
        _id
      }
    }
  }
}

A text includes strings which are a person's name. That person may have multiple names, so there is a separate node representing that unique individual. But, if the name node in the above example is shared by more than one person, it will list multiple people. There are similar examples for other entity types.

What I want to do is make sure that the person returned is constrained by its relationship to the text node. We do have this relationship in our database, so it is possible to query all of the following:

(:Text)-[:MENTIONS]->(:Person)
(:Text)-[:MENTIONS]->(:Name)
(:Person)-[:NAMED]->(:Name)

How can we construct a GraphQL schema or query such that we can get the person mentioned in that particular text? Are we stuck with @cypher directives handling very specific tree hierarchies, or is there a way to make it more flexible?

The best way I can think of to handle it is with GraphQL queries that are only one level deep, then relate the results on the client side. For example, we might do this:

{
  text{
    body
    names{
      _id
      entities{
        _id
      }
    }
    entities {
      _id
      names {
        _id
      }
    }
  }
}

From there, we'd need JavaSCript functions to match or filter names and entities by _id. That seems really ugly.

Soulliberty, welcome to the community. It seems like what you're after is going to be using the ability in your GraphQL schema to either create an individual Cypher query or add a @cyper directive query on your type. But off the top of my head it looks like you could have something like:

type text {
  entities: [Person] @cypher (
   statement: "MATCH (this)-[:MENTIONS || :NAMED]->(p) return p" 
}

You can then filter the results on the front end whoever you like. Just a rough idea.

1 Like

I think we can do cypher statements like this for specific cases, and maybe that's our only answer. Our [Name] nodes have a different type and properties than the [Person] nodes which is why this is more complicated.

The other thing to do is just write a fully custom Cypher query to handle it and feed your variables. When I've been faced with something that gets more complex in the past that's what I've ended up doing.

1 Like

Another idea we've had is to ensure that certain nodes remain unique to certain relationship pairs. Let's say a chapter mentions "Mary." Instead of having one Name Node for "Mary," we would create unique name nodes for every individual with that name. It may look like this:

names: [
  {
     name: "Mary",
     id: 1,
     person: {
       title: "Mary Sue"
     },
     texts: [some_text_list]
  },
  {
     name: "Mary",
     id: 2,
     person: {
       title: "Mary Jo"
     },
     texts: [some_other_list]
  },
]

This way, we maintain a one-to-one relationship that will always carry through the tree. It makes querying unique names a little tougher, but that's acceptable given our use case.

I attended a session today on neosemantics (n10s): Neo4j RDF & Semantics toolkit - Neo4j Labs Neo semantics allows you to do quite a bit creating constraints on your graph that's not available out of the box with Neo4j alone. It might be worth you time to look at.