Skip to content

Instantly share code, notes, and snippets.

@kbastani
Created May 13, 2014 21:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kbastani/66482fd47d089a13e8bf to your computer and use it in GitHub Desktop.
Save kbastani/66482fd47d089a13e8bf to your computer and use it in GitHub Desktop.
Who owns the fish?
= Logical Graphs =
:neo4j-version: 2.0.3
:author: Kenny Bastani
:twitter: @kennybastani
image::https://c2.staticflickr.com/4/3034/3047586737_9ba0b25fbf.jpg[Who owns the fish?]
There are five houses in five different colours starting from left to right. In each house lives a person of a different nationality. These owners all drink a certain type of beverage, smoke a certain brand of cigarette and keep a certain type of pet. No two owners have the same pet, smoke the same brand or drink the same beverage. The question is: WHO OWNS THE FISH???
== Problem
* Build a dependency graph in Neo4j that solves the problem of Einstein's famous riddle using Cypher.
== Questions
* Who owns the fish?
== Graph data model
image::http://raw.githubusercontent.com/kbastani/meetup-analytics/master/docs/images/meetup-analytics-graph-gist.png[Meetup Analytics Graph Data Model]
== Create sample dataset
//setup
//hide
[source,cypher]
----
// Setup tags, location, groups
MERGE (nosql:Tag { tag: "NoSQL" })
MERGE (neo4j:Tag { tag: "Neo4j" })
MERGE (mongodb:Tag { tag: "MongoDB" })
MERGE (redis:Tag { tag: "Redis" })
MERGE (cassandra:Tag { tag: "Cassandra" })
MERGE (couchbase:Tag { tag: "Couchbase" })
MERGE (riak:Tag { tag: "Riak" })
MERGE (sanfrancisco:Location { city: "San Francisco" })
MERGE (graphdbgroup:Group { name: "Graph Database San Francisco" })
MERGE (redisgroup:Group { name: "The San Francisco Redis Meetup Group" })
MERGE (mongodbgroup:Group { name: "San Francisco MongoDB User Group" })
MERGE (cassandragroup:Group { name: "Silicon Valley Apache Cassandra User Group" })
MERGE (couchbasegroup:Group { name: "The San Francisco Couchbase Group" })
MERGE (riakgroup:Group { name: "San Francisco Riak Meetup" })
// Setup tag relationships
MERGE (nosql)<-[:HAS_TAG]-(graphdbgroup)
MERGE (neo4j)<-[:HAS_TAG]-(graphdbgroup)
MERGE (nosql)<-[:HAS_TAG]-(mongodbgroup)
MERGE (mongodb)<-[:HAS_TAG]-(mongodbgroup)
MERGE (nosql)<-[:HAS_TAG]-(redisgroup)
MERGE (redis)<-[:HAS_TAG]-(redisgroup)
MERGE (nosql)<-[:HAS_TAG]-(cassandragroup)
MERGE (cassandra)<-[:HAS_TAG]-(cassandragroup)
MERGE (nosql)<-[:HAS_TAG]-(couchbasegroup)
MERGE (couchbase)<-[:HAS_TAG]-(couchbasegroup)
MERGE (nosql)<-[:HAS_TAG]-(riakgroup)
MERGE (riak)<-[:HAS_TAG]-(riakgroup)
// Setup location relationships
MERGE (graphdbgroup)-[:LOCATED_IN]->(sanfrancisco)
MERGE (redisgroup)-[:LOCATED_IN]->(sanfrancisco)
MERGE (mongodbgroup)-[:LOCATED_IN]->(sanfrancisco)
MERGE (cassandragroup)-[:LOCATED_IN]->(sanfrancisco)
MERGE (couchbasegroup)-[:LOCATED_IN]->(sanfrancisco)
MERGE (riakgroup)-[:LOCATED_IN]->(sanfrancisco)
// Setup days
MERGE (day1:Day { day: 1, month: 1, year: 2014, timestamp: 10000000 })
MERGE (day2:Day { day: 2, month: 1, year: 2014, timestamp: 10000001 })
MERGE (day3:Day { day: 3, month: 1, year: 2014, timestamp: 10000002 })
MERGE (day4:Day { day: 4, month: 1, year: 2014, timestamp: 10000003 })
MERGE (day5:Day { day: 5, month: 1, year: 2014, timestamp: 10000004 })
MERGE (day6:Day { day: 6, month: 1, year: 2014, timestamp: 10000005 })
MERGE (day7:Day { day: 7, month: 1, year: 2014, timestamp: 10000006 })
// Setup day relationships
MERGE (day1)-[:NEXT]->(day2)
MERGE (day2)-[:NEXT]->(day3)
MERGE (day3)-[:NEXT]->(day4)
MERGE (day4)-[:NEXT]->(day5)
MERGE (day5)-[:NEXT]->(day6)
MERGE (day6)-[:NEXT]->(day7)
// Setup member counts: Graph Database SF
CREATE (graphdbMembers1:Stats { count: 101 })
CREATE (graphdbMembers2:Stats { count: 105 })
CREATE (graphdbMembers3:Stats { count: 108 })
CREATE (graphdbMembers4:Stats { count: 116 })
CREATE (graphdbMembers5:Stats { count: 123 })
CREATE (graphdbMembers6:Stats { count: 125 })
CREATE (graphdbMembers7:Stats { count: 132 })
// Setup member counts: The San Francisco Redis Meetup Group
CREATE (redisMembers1:Stats { count: 56 })
CREATE (redisMembers2:Stats { count: 58 })
CREATE (redisMembers3:Stats { count: 62 })
CREATE (redisMembers4:Stats { count: 64 })
CREATE (redisMembers5:Stats { count: 66 })
CREATE (redisMembers6:Stats { count: 68 })
CREATE (redisMembers7:Stats { count: 70 })
// Setup member counts: San Francisco MongoDB User Group
CREATE (mongodbMembers1:Stats { count: 120 })
CREATE (mongodbMembers2:Stats { count: 122 })
CREATE (mongodbMembers3:Stats { count: 124 })
CREATE (mongodbMembers4:Stats { count: 126 })
CREATE (mongodbMembers5:Stats { count: 128 })
CREATE (mongodbMembers6:Stats { count: 130 })
CREATE (mongodbMembers7:Stats { count: 132 })
// Setup member counts: Silicon Valley Apache Cassandra User Group
CREATE (cassandraMembers1:Stats { count: 91 })
CREATE (cassandraMembers2:Stats { count: 98 })
CREATE (cassandraMembers3:Stats { count: 105 })
CREATE (cassandraMembers4:Stats { count: 109 })
CREATE (cassandraMembers5:Stats { count: 112 })
CREATE (cassandraMembers6:Stats { count: 115 })
CREATE (cassandraMembers7:Stats { count: 122 })
// Setup member counts: The San Francisco Couchbase Group
CREATE (couchbaseMembers1:Stats { count: 44 })
CREATE (couchbaseMembers2:Stats { count: 46 })
CREATE (couchbaseMembers3:Stats { count: 48 })
CREATE (couchbaseMembers4:Stats { count: 52 })
CREATE (couchbaseMembers5:Stats { count: 55 })
CREATE (couchbaseMembers6:Stats { count: 59 })
CREATE (couchbaseMembers7:Stats { count: 64 })
// Setup member counts: San Francisco Riak Meetup
CREATE (riakMembers1:Stats { count: 34 })
CREATE (riakMembers2:Stats { count: 36 })
CREATE (riakMembers3:Stats { count: 39 })
CREATE (riakMembers4:Stats { count: 43 })
CREATE (riakMembers5:Stats { count: 47 })
CREATE (riakMembers6:Stats { count: 49 })
CREATE (riakMembers7:Stats { count: 52 })
// Setup member relationships: Graph Database SF
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers1)-[:ON_DAY]->(day1)
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers2)-[:ON_DAY]->(day2)
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers3)-[:ON_DAY]->(day3)
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers4)-[:ON_DAY]->(day4)
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers5)-[:ON_DAY]->(day5)
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers6)-[:ON_DAY]->(day6)
MERGE (graphdbgroup)-[:HAS_MEMBERS]->(graphdbMembers7)-[:ON_DAY]->(day7)
// Setup member relationships: The San Francisco Redis Meetup Group
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers1)-[:ON_DAY]->(day1)
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers2)-[:ON_DAY]->(day2)
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers3)-[:ON_DAY]->(day3)
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers4)-[:ON_DAY]->(day4)
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers5)-[:ON_DAY]->(day5)
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers6)-[:ON_DAY]->(day6)
MERGE (redisgroup)-[:HAS_MEMBERS]->(redisMembers7)-[:ON_DAY]->(day7)
// Setup member relationships: San Francisco MongoDB User Group
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers1)-[:ON_DAY]->(day1)
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers2)-[:ON_DAY]->(day2)
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers3)-[:ON_DAY]->(day3)
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers4)-[:ON_DAY]->(day4)
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers5)-[:ON_DAY]->(day5)
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers6)-[:ON_DAY]->(day6)
MERGE (mongodbgroup)-[:HAS_MEMBERS]->(mongodbMembers7)-[:ON_DAY]->(day7)
// Setup member relationships: Silicon Valley Apache Cassandra User Group
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers1)-[:ON_DAY]->(day1)
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers2)-[:ON_DAY]->(day2)
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers3)-[:ON_DAY]->(day3)
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers4)-[:ON_DAY]->(day4)
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers5)-[:ON_DAY]->(day5)
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers6)-[:ON_DAY]->(day6)
MERGE (cassandragroup)-[:HAS_MEMBERS]->(cassandraMembers7)-[:ON_DAY]->(day7)
// Setup member relationships: The San Francisco Couchbase Group
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers1)-[:ON_DAY]->(day1)
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers2)-[:ON_DAY]->(day2)
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers3)-[:ON_DAY]->(day3)
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers4)-[:ON_DAY]->(day4)
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers5)-[:ON_DAY]->(day5)
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers6)-[:ON_DAY]->(day6)
MERGE (couchbasegroup)-[:HAS_MEMBERS]->(couchbaseMembers7)-[:ON_DAY]->(day7)
// Setup member relationships: San Francisco Riak Meetup
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers1)-[:ON_DAY]->(day1)
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers2)-[:ON_DAY]->(day2)
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers3)-[:ON_DAY]->(day3)
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers4)-[:ON_DAY]->(day4)
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers5)-[:ON_DAY]->(day5)
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers6)-[:ON_DAY]->(day6)
MERGE (riakgroup)-[:HAS_MEMBERS]->(riakMembers7)-[:ON_DAY]->(day7)
----
//graph
== Reporting
The following queries answer the questions required for our meetup analytics platform.
=== What is the daily membership count for the meetup group Graph Databases - San Francisco?
Since GraphGists are only meant to test a small dataset, I've chosen to use 7 days of data for my sample. These days range from `1/1/2014` to `1/7/2014`. These are the start and end dates I will use for this query. Let's find the time series that plots membership growth for the meetup group: `Graph Database San Francisco`.
[source,cypher]
----
MATCH (dayStart:Day { day: 1, month: 1, year: 2014 }),
(dayEnd:Day { day: 7, month: 1, year: 2014 })
MATCH (dayStart)-[:NEXT*0..]->(day:Day)-[:NEXT*0..]->(dayEnd)
MATCH (day)<-[:ON_DAY]-(members:Stats),
(members)<-[:HAS_MEMBERS]-(group:Group { name: "Graph Database San Francisco" })
RETURN (day.month + "/" + day.day + "/" + day.year) AS date, group.name as group_name, members.count AS membership_count
ORDER BY day.timestamp
----
//table
=== What is the daily membership count for NoSQL meetup groups in San Francisco?
Using the same start and end dates from the last query, let's find the time series that plots the combined membership growth for the tag: `NoSQL`.
[source,cypher]
----
MATCH (dayStart:Day { day: 1, month: 1, year: 2014 }),
(dayEnd:Day { day: 7, month: 1, year: 2014 })
MATCH (dayStart)-[:NEXT*0..]->(day:Day)-[:NEXT*0..]->(dayEnd)
MATCH (day)<-[:ON_DAY]-(members:Stats),
(members)<-[:HAS_MEMBERS]-(group:Group),
(group)-[:HAS_TAG]->(tag:Tag { tag: "NoSQL" }),
(group)-[:LOCATED_IN]->(city { city: "San Francisco" })
WITH day, tag, members
ORDER by day.timestamp
WITH (day.month + "/" + day.day + "/" + day.year) AS date, tag.tag as topic, sum(members.count) AS membership_count, day.timestamp as timestamp
RETURN date, topic, membership_count
ORDER BY timestamp
----
//table
=== From the previous query, generate a time series for a basic line chart
Using the popular Javascript charting plugin `Highcharts.js`, we can return back valid JSON objects as properties from our query to use for the JSON parameters of the `Highchart.js` setup options. For this time series we want to use the basic line chart, which is documented here: http://www.highcharts.com/demo/[http://www.highcharts.com/demo/]
The two options we need to generate property data for are `categories`, which contain the ordered date labels on the X-Axis, and `series`, which contains the ordered membership counts for each meetup group. The result of that query is below, which I've used to generate a line chart of our time series.
[source,cypher]
----
MATCH (dayStart:Day { day: 1, month: 1, year: 2014 }),
(dayEnd:Day { day: 7, month: 1, year: 2014 })
MATCH (dayStart)-[:NEXT*0..]->(day:Day)-[:NEXT*0..]->(dayEnd)
MATCH (day)<-[:ON_DAY]-(members:Stats),
(members)<-[:HAS_MEMBERS]-(group:Group),
(group)-[:HAS_TAG]->(tag:Tag { tag: "NoSQL" }),
(group)-[:LOCATED_IN]->(city { city: "San Francisco" })
WITH (day.month + "/" + day.day + "/" + day.year) AS date, group.name AS name, members.count AS membership_count
WITH collect("'" + date + "'") as categories, name, collect(membership_count) as series
RETURN categories, collect({ name:name,data: series }) AS series
----
//table
@journey0621
Copy link

the created triples are not related to Einstein's riddle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment