简体   繁体   中英

How do I make sure a path between two nodes runs through all the connections of an intermediate node?

I may be trying to solve a problem in the stupidest way possible, so please correct me if there's an easier way to do this. I'm trying to model some conditionals via a graph. In this case, we have people and health plans.

Let's say that to qualify for health plan A, you have to live in a certain place and be full-time, or you can live in another place and be full or part time.

The way I'm modeling this is with a series of paths between a person and a plan, with the last node before the plan being a "Condition" node. I think this structure allows for AND and OR relationships. More than one Condition linking to a plan represents ORs (You can be in one state OR you can be in a different state AND be full time).

Here's a picture of a path between one person (yellow node: "FTai" don't ask about the name) and a plan (green node). 在此处输入图片说明

The red node represents the Condition, and the rule is that this person can utilize this health plan if all there is a path from the person to the Condition through all of the Condition's incoming connections. How do I write a query that, for a particular person, collects all the plans they can access?

For instance, for the other person ("Hai"), the query shouldn't produce the plan because they aren't connected to the Full Time node, which feeds into the Connection.

Interesting question.

Provided that the relationships present are all meaningful to the evaluation of the condition (for example, no :ONCE_LIVED_IN relationship to Hawaii or similar that might cause the condition to wrongly evaluate to true), and provided the relationship direction from a person to a plan is all one-way, with no other outgoing relationships that might derail a path into an irrelevant subgraph, it should be fairly straight forward to form your query.

Here's a creation query for the graph in your description:

create (p:Person{name:'FTai'}), (p2:Person{name:'Hai'})
create (hi:State{name:'Hawaii'})
create (con:Condition)
create (hhc:Plan{name:'Hawaii Healthcare'})
create (ft:EmploymentStatus{name:'Full Time'})
create (pt:EmploymentStatus{name:'Part Time'})
create (con)-[:ALLOWS]->(hhc)
create (ft)-[:REQUIRED_BY]->(con)
create (hi)-[:REQUIRED_BY]->(con)
create (p)-[:HAS_STATUS]->(ft)
create (p)-[:LIVES_IN]->(hi)
create (p2)-[:HAS_STATUS]->(pt)
create (p2)-[:LIVES_IN]->(hi)

Here's a query that starts at a :Person and follows outgoing relationships until it reaches a :Condition. From there, for each :Condition matched, we collect the distinct last relationships from those paths into that :Condition (should be :REQUIRED_BY relationships), and compare that to the number of incoming :REQUIRED_BY relationships into the :Condition. If the number of relationships are the same, then we've met every :REQUIRED_BY relationship into the :Condition, and can return the connected :Plan.

MATCH (p:Person{name:'FTai'})-[r*..5]->(con:Condition)
WITH con, COLLECT(DISTINCT LAST(r)) as metRequirements
WHERE SIZE(metRequirements) = SIZE(()-[:REQUIRED_BY]->(con))
MATCH (con)-[:ALLOWS]->(plan:Plan)
RETURN DISTINCT plan

EDIT

My creation query didn't include the :DEFINED_AS relationships between states and employment status, and the presence of those relationships will cause the query I provided to match improperly (so Hai will wrongly fulfill the condition).

There are a few ways around this kind of obstacle. You can do whitelisting or blacklisting. For whitelisting, you'll need to provide every valid relationship to traverse in the variable-length relationship.

Blacklisting will likely be easier, if there are relationship types that should never be traversed for your query. Here's an updated version of the query that will work even if the :DEFINED_AS relationships are in place:

// you can add to the blacklist as needed, and change to a parameter
WITH ['DEFINED_AS'] as blacklist
MATCH (p:Person{name:'Hai'})-[r*..5]->(con:Condition)
WHERE NONE(rel in r WHERE TYPE(rel) in blacklist)
WITH con, COLLECT(DISTINCT LAST(r)) as metRequirements
WHERE SIZE(metRequirements) = SIZE(()-[:REQUIRED_BY]->(con))
MATCH (con)-[:ALLOWS]->(plan:Plan)
RETURN DISTINCT plan

Maybe I don't see the complexity of your query but how about something like this:

match (state:State)-[:REQUIRED_BY]->(and:Condition)<-[:REQUIRED_BY]-(status:EmploymentStatus)
with state,status,and
match (status)<-[:HAS_STATUS]-(person:Person)-[:LIVES_IN]->(state)
with person, and
match (and)-[:ALLOWS]->(plan:Plan)
return person, plan 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM