tscherg
tscherg

Reputation: 1082

Neo4j match Relationship parameters satisfying a certain schema

Using cypher, is there any way to match a path where the relationships satisfy a certain input schema generically?

I know I can do something like

parameters: {
    "age": 20
}

MATCH (n)-[r:MY_RELATION]-() WHERE $age>18 AND $age<24 ...

when i want to match only relations satisfying the schema { "type": "integer", "minimum": 19, "maximum": 23 }.

But then i have to specify the min and max range within the relationship. What if I want to match strings against a schema, or even more complex types like address with subparameters etc.?

Is there a generic way to do it?

edit: I'll try and state the question more clearly. What I want is a graph and a (parameterized) query to traverse that graph, such that:

edit 2: What I want may not even be possible. I want all of the information about the constraint to reside in the edge, including the parameter to test against. So I would want something along the lines of

parameters: { "age": 20, "location": "A" }
MATCH (n)-[r]-()
WHERE r.testtype='integer' AND getParameterByName(r.testparamname) < r.max
OR r.testtype='string' AND getParameterByName(r.testparamname)=r.allowedStringValue

Of course, as can be read in the neo4j documentation about parameter functionality it should not be possible to dynamically load the parameter via a name that resides in the DB.

There may yet be some workaround?

Upvotes: 2

Views: 667

Answers (2)

cybersam
cybersam

Reputation: 67044

[UPDATED]

Original answer:

Your question is not stated very clearly, but I'll try to answer anyway.

I think something like this is what you want:

parameters: {
  "minimum": 19,
  "maximum": 23
}

MATCH (n)-[r:MY_RELATION]-() WHERE $maximum >= r.age >= $minimum
...

There is no need to specify a "type" parameter. Just make sure your parameter values are of the appropriate type.

New answer (based on updated question):

Suppose the parameters are specified this way (where test indicates the type of test):

parameters: {
  "age": 20,
  "test": "age_range"
}

Then you could do this (where r would contain the properties test, min, and max):

MATCH (n)-[r:MY_RELATION]-(m)
WHERE r.test = $test AND r.min <= $age <= r.max
RETURN n, r, m;

Or, if you do not need all the relationships to be of the same type, this should also work and may be easier to visualize (where r would be of, say, type "age_range", and contain the properties min and max):

MATCH (n)-[r]-(m)
WHERE TYPE(r) = $test AND r.min <= $age <= r.max
RETURN n, r, m;

To help you decide which approach to use, you should profile the two approaches with your code and some actual data to see which is faster for you.

Even Newer answer (based on edit 2 in question)

The following parameter and query should do what you want. Square brackets can be used to dyamically specify the name of a property.

parameters: {
  "data": {
    "age": 20,
    "location": "A"
  }
}

MATCH (n)-[r]-()
WHERE r.testtype='integer' AND $data[r.testparamname] < r.max
OR r.testtype='string' AND $data[r.testparamname]=r.allowedStringValue
...

Upvotes: 3

Dave Bennett
Dave Bennett

Reputation: 11216

Does this solution meet your requirements?

Considering the following small sample data set

MERGE (p1:Person {name: 'P 01'})
MERGE (p2:Person {name: 'P 02'})
MERGE (p3:Person {name: 'P 03'})
MERGE (p1)-[:MY_RELATION { minimum: 19, maximum: 23 }]->(p2)
MERGE (p2)-[:MY_RELATION { minimum: 19, maximum: 20 }]->(p3)

This query will only return the nodes and relationship where the supplied parameter fits the relationship constraints (e.g. $age = 21 should only return a single row). It is basically the inverse of @cybersam's proposal.

MATCH (s:Person)-[r:MY_RELATION]->(e:Person)
WHERE r.minimum <= $age <= r.maximum
RETURN *

Upvotes: 0

Related Questions