Skip to content

Instantly share code, notes, and snippets.

@kiendang
Last active October 9, 2016 15:17
Show Gist options
  • Save kiendang/66965da15487e32966ad to your computer and use it in GitHub Desktop.
Save kiendang/66965da15487e32966ad to your computer and use it in GitHub Desktop.
= Location and infection tracking in a hospital
== Data model
=== People and hospital rooms
People and places inside a hospital are represented as nodes with labels _Person_ and _Facility_ respectively. _Person_ nodes have extra labels _Doctor_, _Nurse_, or _Patient_,... and properties _name_, _age_. _Facility_ nodes have properties _type_ (_eg_ _Ward_) and _name_ (_eg_ _Ward 01_).
[source,cypher]
----
CREATE (alice:Person:Patient{name:"Alice", age:34, issue:"flu"})
CREATE (w1:Facility:Room{name:"Ward 01", type:"Ward"});
MATCH n RETURN n;
----
//graph_output
Populate the graph database (adding more _Person_ and _Facility_ nodes)
//hide
[source,cypher]
----
CREATE (mike:Person:Doctor{name:"Mike", age:42})
CREATE (john:Person:Doctor{name:"John", age:39})
CREATE (jen:Person:Visitor{name:"Jen", age:26})
CREATE (anne:Person:Nurse{name:"Anne", age:23})
CREATE (bruce:Person:Patient{name:"Bruce", age:57})
CREATE (chloe:Person:Visitor{name:"Chloe", age:54})
CREATE (brian:Person:Patient{name:"Brian", age:36, issue:"flu"})
CREATE (lana:Person:Nurse{name:"Lana", age:27})
CREATE (adam:Person:Chef{name:"Adam", age:33})
CREATE (bob:Person:Phamacist{name:"Bob", age:30})
CREATE (w2:Facility:Room{name:"Ward 02", type:"Ward"})
CREATE (w3:Facility:Room{name:"Ward 03", type:"Ward"})
CREATE (w4:Facility:Room{name:"Ward 04", type:"Ward"})
CREATE (w5:Facility:Room{name:"Ward 05", type:"Ward"})
CREATE (c1:Facility:Room{name:"Consulting Room 01", type:"Consulting Room"})
CREATE (c2:Facility:Room{name:"Consulting Room 02", type:"Consulting Room"})
CREATE (r1:Facility:Room{name:"Reception", type:"Reception"})
CREATE (p1:Facility:Room{name:"Pharmacy", type:"Pharmacy"})
CREATE (k1:Facility:Room{name:"Kitchen", type:"Kitchen"})
CREATE (wc1:Facility:Room{name:"WC", type:"WC"})
CREATE (d1:Facility:Room{name:"Day Room", type:"Day room"})
CREATE (t1:Facility:Room{name:"Treatment Room 01", type:"Treatment Room"})
CREATE (t2:Facility:Room{name:"Treatment Room 02", type:"Treatment Room"})
CREATE (i1:Facility:Room{name:"ICU", type:"ICU"})
----
=== Connecting people
Relationships between patients and doctors, patients and visitors are represented as relationships/links/edges between nodes.
[source,cypher]
----
MATCH (a:Patient),(b:Doctor)
WHERE a.name = "Alice" AND b.name = "Mike"
CREATE (b)-[r:TREATS]->(a);
MATCH (a:Visitor),(b:Patient)
WHERE a.name = "Jen" AND b.name = "Alice"
CREATE (a)-[r:VISITS]->(b);
MATCH path=(p:Person)-[]-(:Person)
WHERE p.name = "Alice"
RETURN path;
----
//graph_result
Adding in data
//hide
[source,cypher]
----
MATCH (a:Patient),(b:Doctor)
WHERE a.name = "Brian" AND b.name = "Mike"
CREATE (b)-[r:TREATS]->(a);
MATCH (a:Patient),(b:Doctor)
WHERE a.name = "Bruce" AND b.name = "John"
CREATE (b)-[r:TREATS]->(a);
MATCH (a:Visitor),(b:Patient)
WHERE a.name = "Chloe" AND b.name = "Bruce"
CREATE (a)-[r:VISITS]->(b);
----
=== Connecting people and locations (location tracking)
Suppose Alice was at coordinate (x:15, y:3) from 11 am to 1 pm. We want to store this piece of information in graph database using nodes and edges. We can do that by represent the coordinates as a node labeled _Location_ with properties _x_ and _y_. This node is then connected to the node representing Alice by an edge labeled _AT_ with properties _from:11am_ and _to:1pm_. Since there is currently no built-in data type for datetime in neo4j, we store time as integer (2015-07-01 11 am as 1435719600).
[source,cypher]
----
MATCH (a:Person),(f:Facility),(f1:Facility),(f2:Facility)
WHERE a.name = "Alice" AND f.name = "Ward 01" AND f1.name = "Kitchen" AND f2.name = "Treatment Room 01"
CREATE (a)-[r:AT{from:1435719600, to:1435719600 + 3600 * 2}]->(l:Location{x:15, y:3})-[r2:WITHIN]->(f)
CREATE (a)-[:AT{from:1435719600 + 3600 * 2, to:1435719600 + 3600 * 3}]->(:Location{x:5, y:30})-[:WITHIN]->(f1)
CREATE (a)-[:AT{from:1435719600 + 3600 * 3, to:1435719600 + 3600 * 6}]->(:Location{x:20, y:38})-[:WITHIN]->(f2);
----
[source,cypher]
----
MATCH path=(p:Person)-[:AT]->(:Location)-[:WITHIN]->(:Facility)
WHERE p.name = "Alice"
RETURN path;
----
//graph_result
_The nodes immediately connected to Alice are Location nodes with properties x and y. Graphgist shows the first property which is x. Also the edges have properties from and to to represent the time which Graphgist does not display._
Adding in data
//hide
[source,cypher]
----
MATCH (a:Person),(f:Facility),(f1:Facility)
WHERE a.name = "Jen" AND f.name = "Ward 01" AND f1.name = "Kitchen"
CREATE (a)-[r:AT{from:1435719600 + 3600 + 1800, to:1435719600 + 3600 * 2}]->(l:Location{x:14, y:4})-[r2:WITHIN]->(f)
CREATE (a)-[:AT{from:1435719600 + 3600 * 2, to:1435719600 + 3600 * 3}]->(:Location{x:6, y:30})-[:WITHIN]->(f1);
MATCH (p:Person),(f:Facility)
WHERE p.name = "Adam" AND f.name = "Kitchen"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600 * 6}]->(:Location{x:2, y:38})-[:WITHIN]->(f);
MATCH (p:Person),(f:Facility),(f1:Facility)
WHERE p.name = "Bob" AND f.name = "Pharmacy" AND f1.name = "Kitchen"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600}]->(:Location{x:4, y:30})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600, to:1435719600 + 3600 + 1800}]->(:Location{x:4, y:30})-[:WITHIN]->(f1)
CREATE (p)-[:AT{from:1435719600 + 3600 + 1800, to:1435719600 + 3600 * 6}]->(:Location{x:4, y:30})-[:WITHIN]->(f);
MATCH (p:Person),(f:Facility),(f1:Facility)
WHERE p.name = "Mike" AND f.name = "Consulting Room 01" AND f1.name = "Treatment Room 01"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600 * 3}]->(:Location{x:30, y:37})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600 * 3, to:1435719600 + 3600 * 6}]->(:Location{x:21, y:37})-[:WITHIN]->(f1);
MATCH (p:Person),(f:Facility),(f1:Facility),(f2:Facility)
WHERE p.name = "Bruce" AND f.name = "Reception" AND f1.name = "Consulting Room 01" AND f2.name = "Ward 02"
CREATE (p)-[:AT{from:1435719600 + 3600, to:1435719600 + 3600 + 15 * 60}]->(:Location{x:40, y:17})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600 + 15 * 60, to:1435719600 + 3600 * 2}]->(:Location{x:32, y:38})-[:WITHIN]->(f1)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2, to:1435719600 + 3600 * 6}]->(:Location{x:20, y:2})-[:WITHIN]->(f2);
MATCH (p:Person),(f:Facility),(f1:Facility)
WHERE p.name = "Chloe" AND f.name = "Reception" AND f1.name = "Ward 02"
CREATE (p)-[:AT{from:1435719600 + 3600 * 2 + 1800, to:1435719600 + 3600 * 2 + 1800 + 10 * 60}]->(:Location{x:40, y:15})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2 + 1800 + 10 * 60, to:1435719600 + 3600 * 6}]->(:Location{x:20, y:3})-[:WITHIN]->(f1);
MATCH (p:Person),(f:Facility),(f1:Facility)
WHERE p.name = "Anne" AND f.name = "Day Room" AND f1.name = "Treatment Room 01"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600 * 2 + 45 * 60}]->(:Location{x:4, y:20})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2 + 45 * 60, to:1435719600 + 3600 * 6}]->(:Location{x:15, y:38})-[:WITHIN]->(f1);
MATCH (p:Person),(f:Facility),(f1:Facility),(f2:Facility)
WHERE p.name = "Lana" AND f.name = "Day Room" AND f.name = "Consulting Room 01" AND f2.name = "Ward 02"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600 + 1800}]->(:Location{x:3, y:18})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600 + 1800, to:1435719600 + 3600 * 2}]->(:Location{x:33, y:37})-[:WITHIN]->(f1)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2, to:1435719600 + 3600 * 6}]->(:Location{x:21, y:3})-[:WITHIN]->(f2);
MATCH (p:Person),(f:Facility)
WHERE p.name = "John" and f.name = "Consulting Room 02"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600 * 6}]->(:Location{x:36, y:38})-[:WITHIN]->(f);
MATCH (p:Person),(f:Facility),(f1:Facility),(f2:Facility),(f3:Facility)
WHERE p.name = "Brian" AND f.name = "Reception" AND f1.name = "Consulting Room 02" AND f2.name = "Pharmacy" AND f3.name = "WC"
CREATE (p)-[:AT{from:1435719600 + 3600 * 2, to: 1435719600 + 3600 * 2 + 5 * 60}]->(:Location{x:40, y:15})-[:WITHIN]->(f)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2 + 5 * 60, to: 1435719600 + 3600 * 2 + 35 * 60}]->(:Location{x:36, y:37})-[:WITHIN]->(f1)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2 + 35 * 60, to: 1435719600 + 3600 * 2 + 45 * 60}]->(:Location{x:22, y:24})-[:WITHIN]->(f2)
CREATE (p)-[:AT{from:1435719600 + 3600 * 2 + 45 * 60, to: 1435719600 + 3600 * 2 + 50 * 60}]->(:Location{x:4, y:4})-[:WITHIN]->(f3);
MATCH (p:Person),(f:Facility)
WHERE p.name = "Susan" AND f.name = "Reception"
CREATE (p)-[:AT{from:1435719600, to:1435719600 + 3600 * 6}]->(:Location{x:40, y:16})-[:WITHIN]->(f);
----
Take a look at the whole database:
[source,cypher]
----
MATCH n RETURN n LIMIT 100
----
//graph_result
== Graph database in action
Now we have a graph database storing data on people inside a hospital, when and where they have been. We can use this to
=== Infection tracking
Suppose Alice was discovered to carry an infectious virus and the hospital wants to know where she has been earlier and who are the people that have been in contact with her.
Find out where Alice has been:
[source,cypher]
----
MATCH path=(p:Person)-[a:AT]->(:Location)-[:WITHIN]->(f:Facility)
WHERE p.name = "Alice"
RETURN f.name AS location, a.from AS from, a.to AS to;
----
//table
Visualizing using graphs:
//hide
[source,cypher]
----
MATCH path=(p:Person)-[a:AT]->(:Location)-[:WITHIN]->(f:Facility)
WHERE p.name = "Alice"
RETURN path;
----
//graph_result
Find out who has been in the same place (of course at the same time) with Alice
[source,cypher]
----
MATCH path=(p:Person)-[a:AT]->(:Location)-[:WITHIN]->(f:Facility)<-[:WITHIN]-(:Location)<-[a2:AT]-(p1:Person)
WHERE p.name = "Alice" AND ((a2.from <= a.from AND a.from <= a2.to) OR (a.from <= a2.from AND a2.from <= a.to)) AND p1.name <> "Alice"
RETURN p1.name AS name, f.name AS place_of_contact;
----
//table
As graph:
//hide
[source,cypher]
----
MATCH path=(p:Person)-[a:AT]->(:Location)-[:WITHIN]->(:Facility)<-[:WITHIN]-(:Location)<-[a2:AT]-(p1:Person)
WHERE p.name = "Alice" AND ((a2.from <= a.from AND a.from <= a2.to) OR (a.from <= a2.from AND a2.from <= a.to)) AND p1.name <> "Alice"
RETURN path;
----
//graph_result
Find out who has been within 5 meters from Alice
[source,cypher]
----
MATCH path=(p:Person)-[a:AT]->(l:Location)-[:WITHIN]->(f:Facility)<-[:WITHIN]-(l2:Location)<-[a2:AT]-(p1:Person)
WHERE p.name = "Alice" AND ((a2.from <= a.from AND a.from <= a2.to) OR (a.from <= a2.from AND a2.from <= a.to)) AND (SQRT((l.x - l2.x) * (l.x - l2.x) + (l.y - l2.y) * (l.y - l2.y)) <= 5) AND p1.name <> "Alice"
RETURN p1.name AS name;
----
//table
//hide
[source,cypher]
----
MATCH path=(p:Person)-[a:AT]->(l:Location)-[:WITHIN]->(f:Facility)<-[:WITHIN]-(l2:Location)<-[a2:AT]-(p1:Person)
WHERE p.name = "Alice" AND ((a2.from <= a.from AND a.from <= a2.to) OR (a.from <= a2.from AND a2.from <= a.to)) AND (SQRT((l.x - l2.x) * (l.x - l2.x) + (l.y - l2.y) * (l.y - l2.y)) <= 5) AND p1.name <> "Alice"
RETURN path;
----
//graph_result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment