├── README.adoc
├── meta
├── Home.adoc
├── HowTo.adoc
├── README.adoc
└── Simple.adoc
└── other
├── BankFraudDetection.adoc
├── NetworkDataCenterManagement1.adoc
├── SocialNetworkGraph.adoc
├── TVShowGraph.adoc
├── ThePublicationGraph.adoc
├── images
├── BankFraud-1.png
├── datacenter-management-1.PNG
├── network-inventory-cost-accounting-graph-1.PNG
├── social-network-graph-1.png
├── tv-show-graph-1.PNG
└── tv-show-graph-2.PNG
└── sarah.adoc
/README.adoc:
--------------------------------------------------------------------------------
1 | == GraphGists
2 |
3 | This repo contains content for use at http://gist.neo4j.org/.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/meta/Home.adoc:
--------------------------------------------------------------------------------
1 | = What is the Neo4j GraphGist Project? =
2 |
3 | :neo4j-version: 2.1.0
4 | :author: Anders Nawroth
5 | :twitter: @nawroth
6 | :tags: domain:example
7 |
8 | image:https://dl.dropboxusercontent.com/u/14493611/neo4j-logo.png[]
9 |
10 | http://neo4j.com[Neo4j] GraphGists are an easy way to create and share documents containing not just prose, structure
11 | and pictures but most importantly **example graph models and use-cases** expressed in Neo4j's query language http://docs.neo4j.org/refcard/2.1/[Cypher].
12 |
13 | These documents are written in AsciiDoc -- the simple, textual markup language -- and rendered in your browser as rich and interactive web pages that you
14 | can quickly evolve from describing simple howtos or questions to providing an extensive use-case specification.
15 |
16 | To see the expressive power of this approach, here are some winners of our past community competitions:
17 |
18 | == Examples
19 | [none]
20 | * **Identity and Access Management** link:./?4471127413fd724ed0a3[Entitlements and Access Control] by https://twitter.com/kennybastani[@kennybastani]
21 | * **Finance** link:./?github-neo4j-contrib%2Fgists%2F%2Fother%2FBankFraudDetection.adoc[Bank Fraud Detection] by https://twitter.com/kennybastani[@kennybastani]
22 | * **Sports** link:./?8019511[Alpine Skiing seasons] by https://twitter.com/pac_19[@pac_19]
23 | * **Life Science** link:./?7968633[Pharmaceutical Drugs and their Targets] by https://twitter.com/joshkunken[@joshkunken]
24 | * **Resources** link:./?8141937[Piping Water] by https://twitter.com/shaundaley1[@shaundaley1]
25 | * **Retail** link:./?8139605[Single Malt Scotch Whisky] by https://twitter.com/patbaumgartner[@patbaumgartner]
26 | * **Telecommunications** link:./?8526106[Amazon Web Services Global Infrastructure Graph] by https://twitter.com/aidanjcasey[@aidanjcasey]
27 | * **Infrastructure** link:./?8635758[Roads, Nodes and Automobiles] by http://www.jacqui.tk[@tekiegirl]
28 | * **Recommendation** link:./?8173017[Movie Recommendations with k-NN and Cosine Similarity] by http://twitter.com/_nicolemargaret[@_nicolemargaret]
29 | * **Fun** link:./?6506717[Chess Games and Positions] by https://twitter.com/wefreema[@wefreema]
30 | * **One hundred more** examples can be found in the https://github.com/neo4j-contrib/graphgist/wiki[GraphGist Collection]
31 |
32 | == Getting Started
33 |
34 | === Follow these easy steps
35 |
36 | . **Log into** your GitHub account
37 | . **Choose** one of the examples listed above, click the green **page source** button.
38 | . **Fork** the original GraphGist into your own account and **start to edit** the prose, images, setup and use-case Cypher queries.
39 | . **Paste** the Github Gist URL into the top right URL box of any GraphGist page and hit enter; your Gist will be loaded and rendered immediately.
40 | . **Reload** your GraphGist page whenever you made an update to your document.
41 | . **Share** your GraphGist with your colleagues, friends or us.
42 | Hit the submit link in the footer, when you are ready to get your t-shirt reward.
43 |
44 | ++++
45 |
46 | ++++
47 |
48 |
49 | === Setup a Graph to show you and your Likes
50 |
51 | //setup
52 | [source,cypher]
53 | ----
54 | CREATE (you:Person {name:"You"})-[like:LIKE]->(us:Database:NoSql:Graph {name:"Neo4j" })
55 | RETURN you, like, us
56 | ----
57 |
58 | And render as a graph.
59 |
60 | //graph
61 |
62 | === Now query the graph to show all the things you like
63 |
64 | [source,cypher]
65 | ----
66 | MATCH (you:Person {name:"You"})-[like:LIKE]->(liked)
67 | RETURN you.name AS who, type(like) AS how, liked.name AS what
68 | ----
69 |
70 | And render as a table.
71 |
72 | //table
73 |
74 | NOTE: This document is a GraphGist itself, so you can use the green https://github.com/neo4j-contrib/gists/blob/master/meta/Home.adoc[Page Source] button on top of the page to see the original AsciiDoc document.
75 |
76 | We'd love to get your feedback, so feel free to write us at *docs at neo4j.org* or comment in the section below.
77 |
78 | // * link:./?github-neo4j-contrib%2Fgists%2F%2Fmeta%2FSimple.adoc[A simple GraphGist]
79 | // * link:./?github-neo4j-contrib%2Fgists%2F%2Fmeta%2FHowTo.adoc[How to create a GraphGist]
80 |
--------------------------------------------------------------------------------
/meta/HowTo.adoc:
--------------------------------------------------------------------------------
1 | = How to create a GraphGist
2 |
3 | :neo4j-version: 2.0.0
4 | :author: Anders Nawroth
5 | :twitter: @nawroth
6 |
7 | You create a GraphGist by creating a https://gist.github.com/[GitHub Gist] in http://asciidoctor.org/docs/asciidoc-quick-reference/[AsciiDoc] and enter the URL to it in the form on this page.
8 | Alternatively, you can put an AsciiDoc document in https://www.dropbox.com/[Dropbox] and enter the public URL in the form.
9 |
10 | This GraphGist shows the basics of using AsciiDoc syntax and a few additions for GraphGists.
11 | The additions are entered as comments on their own line.
12 | They are: +//console+ for a query console; +//hide+, +//setup+ and +//output+ to configure a query; +//graph+ and +//table+ to visualize queries and show a result table.
13 |
14 | Click on the Page Source button in the menu to see the source for this GraphGist.
15 | Read below to get the full details.
16 |
17 | == Include a query console
18 |
19 | +//console+
20 |
21 | becomes:
22 |
23 | //console
24 |
25 | == Define a http://docs.neo4j.org/chunked/snapshot/cypher-query-lang.html[Cypher] query
26 |
27 | [source,cypher]
28 | ----
29 | CREATE (n{name:'cypher'})-[r:LIKES]->({name:'icecream'})
30 | RETURN n.name, r
31 | ----
32 |
33 | becomes:
34 |
35 | [source,cypher]
36 | ----
37 | CREATE (n{name:'cypher'})-[r:LIKES]->({name:'icecream'})
38 | RETURN n.name, r
39 | ----
40 |
41 | _Queries are executed in the order they appear on the page during rendering, so make sure they can be performed in that order._
42 | If you have a _setup query,_ it normally makes most sense to define it as the first query on the page.
43 | If it takes up too much space, hide it (see below for information on this).
44 | Each query has a green or red button to indicate if the query was successful or not.
45 | The console is set up after the executions, with an empty database, for the reader to play around with the queries.
46 |
47 | There's three additional settings you can use for queries.
48 | They all go as comments, on their own lines, before the query.
49 | The settings are:
50 |
51 | [width="50%",cols="1m,5"]
52 | |===
53 | | hide | Hide the query. The reader can still expand it to see it.
54 | Useful for long queries like setting up initial data.
55 | | setup | Initialize the console with this query.
56 | | output | Show the output from the query.
57 | The output is always there, but this option makes it visible at page load for this query.
58 | |===
59 |
60 | Let's try all the settings together, which means this query will be used to initialize the console, it will be hidden, and the raw output will be shown:
61 |
62 | //hide
63 | //setup
64 | //output
65 | [source,cypher]
66 | ----
67 | CREATE (n{name:'neo4j'})-[:LIKES]->({name:'complex data'})
68 | RETURN n.name
69 | ----
70 |
71 | which becomes:
72 |
73 | //hide
74 | //setup
75 | //output
76 | [source,cypher]
77 | ----
78 | CREATE (n{name:'neo4j'})-[:LIKES]->({name:'complex data'})
79 | RETURN n.name
80 | ----
81 |
82 | [NOTE]
83 | The test run will always run the queries from top to bottom, so it usually makes sense to have the setup query as the first query.
84 |
85 | == Show a graph visualization
86 |
87 | The visualization is based on the database contents after the preceding query in the page.
88 |
89 | +//graph+
90 |
91 | becomes:
92 |
93 | //graph
94 |
95 | == Show a result table for a query
96 |
97 | This will show a result table for the preceding query.
98 |
99 | +//table+
100 |
101 | becomes:
102 |
103 | //table
104 |
105 | == Basic AsciiDoc formatting
106 |
107 | [width="50%",cols="1m,1a"]
108 | |===
109 | | \_Italic_ | _Italic_
110 | | \*Bold* | *Bold*
111 | | \`Monospace` | `Monospace`
112 | | `http://www.neo4j.org/` | http://www.neo4j.org/
113 | | `http://www.neo4j.org/[neo4j.org]` | http://www.neo4j.org/[neo4j.org]
114 | | `link:./?5956246[Link to a GraphGist]` | link:./?5956246[Link to a GraphGist]
115 | |===
116 |
117 | === Headings:
118 |
119 | = Heading 1
120 | == Heading 2
121 | === Heading 3
122 |
123 | === Images:
124 |
125 | Static images:
126 |
127 | image::http://assets.neo4j.org/img/still/cineasts.gif[]
128 |
129 | becomes
130 |
131 | image::http://assets.neo4j.org/img/still/cineasts.gif[]
132 |
133 | Dynamically generated image (using http://yuml.me/[yUML]):
134 |
135 | image::http://yuml.me/diagram/scruffy/class/[Order]-billing%20%3E[Address%7Cname=hej],%20[Order]-shipping%20%3E[Address],[Order]-has%3E[Item][test]
136 |
137 | becomes
138 |
139 | image::http://yuml.me/diagram/scruffy/class/[Order]-billing%20%3E[Address%7Cname=hej],%20[Order]-shipping%20%3E[Address],[Order]-has%3E[Item][test]
140 |
141 | === Lists:
142 |
143 | ----
144 | * Item 1
145 | ** Item 1.1
146 | * Item 2
147 | ----
148 |
149 | * Item 1
150 | ** Item 1.1
151 | * Item 2
152 |
153 | ----
154 | . First
155 | . Second
156 | ----
157 |
158 | . First
159 | . Second
160 |
161 | Monospaced block: indent lines with one space.
162 |
163 | Tables are well supported.
164 | See http://asciidoctor.org/docs/asciidoc-quick-reference/[AsciiDoc Quick Reference] for information on that and more.
165 |
166 |
167 | == Mathematical formulas
168 |
169 | When using Math formulas, put them into a source block using LaTex syntax with `\(` as a start delimiter.
170 |
171 | The end delimiter is `\)`.
172 | The delimiters and the content between them should be placed in a AsciiDoc passthrough block without attribute substitution.
173 | The formulas are rendered through http://www.mathjax.org/demos/tex-samples/[MathJAX], supported commands are http://docs.mathjax.org/en/latest/tex.html#supported-latex-commands[here].
174 |
175 | [subs=none]
176 | ++++
177 | \(ax^2 + bx + c \ne 0\)
178 | ++++
179 |
180 | becomes
181 |
182 | ++++
183 | \(ax^2 + bx + c \ne 0\)
184 | ++++
185 |
186 | Below are some more examples.
187 |
188 | [subs=none]
189 | ++++
190 | 1.
191 | \(
192 | \begin{equation}
193 | \left[
194 | {\bf X} + {\rm a} \ \geq\
195 | \underline{\hat a} \sum_i^N \lim_{x \rightarrow k} \delta C
196 | \right]
197 | \end{equation}
198 | \)
199 |
200 | 2. \(ax^2 + bx + c \ne 0\)
201 |
202 |
203 | 4. \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}\)
204 |
205 |
206 | 5. \(\sin^{-1} \theta\)
207 |
208 |
209 | 6. \(\int_a^b f(x)~dx\)
210 |
211 |
212 | 7.\(\sum\limits_{i=1}^n X_i\)
213 |
214 | ++++
215 |
216 | for more examples, see http://gist.neo4j.org/?6725918[Some GraphGist Latex Examples]
217 |
218 |
--------------------------------------------------------------------------------
/meta/README.adoc:
--------------------------------------------------------------------------------
1 | == Meta GraphGists
2 |
3 | Gists about GraphGist.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/meta/Simple.adoc:
--------------------------------------------------------------------------------
1 | = A simple GraphGist
2 |
3 | :author: Anders Nawroth
4 | :twitter: @nawroth
5 |
6 | You create a GraphGist by creating a https://gist.github.com/[GitHub Gist] in http://asciidoctor.org/docs/asciidoc-quick-reference/[AsciiDoc] and enter the URL to it in the form on this page.
7 |
8 | Click on the Page Source button in the menu to see the source for this GraphGist!
9 |
10 | == Include a query console
11 |
12 | +//console+
13 |
14 | becomes:
15 |
16 | //console
17 |
18 | == Define a http://docs.neo4j.org/chunked/snapshot/cypher-query-lang.html[Cypher] query
19 |
20 | [source,cypher]
21 | ----
22 | CREATE (n{name:'cypher'})-[r:LIKES]->({name:'icecream'}) return n.name, r
23 | ----
24 |
25 | becomes:
26 |
27 | [source,cypher]
28 | ----
29 | CREATE (n{name:'cypher'})-[r:LIKES]->({name:'icecream'}) return n.name, r
30 | ----
31 |
32 | _Queries are executed in the order they appear on the page during rendering, so make sure they can be performed in that order._
33 | Each query has a green or red button to indicate if the query was successful or not.
34 | The console is set up after the executions, with an empty database, for the reader to play around with the queries.
35 |
36 |
--------------------------------------------------------------------------------
/other/BankFraudDetection.adoc:
--------------------------------------------------------------------------------
1 | = Bank Fraud Detection
2 | :neo4j-version: 2.0.0-RC1
3 | :author: Kenny Bastani
4 | :twitter: @kennybastani
5 | :tags: domain:finance, use-case:fraud-detection
6 |
7 | This interactive Neo4j graph tutorial covers bank fraud detection scenarios.
8 |
9 | '''
10 |
11 | *Table of Contents*
12 |
13 | * *Introduction*
14 | ** <>
15 | * *Scenario*
16 | ** <>
17 | ** <>
18 | * *Solution*
19 | ** <>
20 | ** <>
21 | * *Data Model*
22 | ** <>
23 | * *Database Setup*
24 | ** <>
25 | * *Cypher Queries*
26 | ** <>
27 | ** <>
28 | ** <>
29 |
30 | '''
31 |
32 | == Introduction to Problem
33 |
34 | Banks and Insurance companies lose billions of dollars every year to fraud. Traditional methods of fraud detection play an important role in minimizing these losses. However increasingly sophisticated fraudsters have developed a variety of ways to elude discovery, both by working together, and by leveraging various other means of constructing false identities.
35 |
36 | '''
37 |
38 | == Explanation of Scenario
39 |
40 | While no fraud prevention measures can ever be perfect, significant opportunity for improvement lies in looking beyond the individual data points, to the connections that link them. Oftentimes these connections go unnoticed until it is too late— something that is unfortunate, as these connections oftentimes hold the best clues.
41 |
42 | === Typical Scenario
43 |
44 | While the exact details behind each first-party fraud collusion vary from operation to operation, the pattern below illustrates how fraud rings commonly operate:
45 |
46 | * A group of two or more people organize into a fraud ring
47 | * The ring shares a subset of legitimate contact information, for example phone numbers and addresses, combining them to create a number of synthetic identities
48 | * Ring members open accounts using these synthetic identities
49 | * New accounts are added to the original ones: unsecured credit lines, credit cards, overdraft protection, personal loans, etc.
50 | * The accounts are used as normally, with regular purchases and timely payments
51 | * Banks increase the revolving credit lines over time, due to the observed responsible credit behavior
52 | * One day the ring “busts out”, coordinating their activity, maxing out all of their credit lines, and disappearing
53 | * Sometimes fraudsters will go a step further and bring all of their balances to zero using fake checks immediately before the prior step, doubling the damage
54 | * Collections processes ensue, but agents are never able to reach the fraudster
55 | * The uncollectible debt is written off
56 |
57 | '''
58 |
59 | == Explanation of Solution
60 |
61 | Graph databases offer new methods of uncovering fraud rings and other sophisticated scams with a high-level of accuracy, and are capable of stopping advanced fraud scenarios in real-time.
62 |
63 | === How Graph Databases Can Help
64 |
65 | Augmenting one’s existing fraud detection infrastructure to support ring detection can be done by running appropriate entity link analysis queries using a graph database, and running checks during key stages in the customer & account lifecycle, such as:
66 |
67 | * At the time the account is created
68 | * During an investigation
69 | * As soon as a credit balance threshold is hit
70 | * When a check is bounced
71 |
72 | Real-time graph traversals tied to the right kinds of events can help banks identify probable fraud rings: during or even before the Bust-Out occurs.
73 |
74 | '''
75 |
76 | == Bank Fraud Graph Data Model
77 |
78 | Graph databases have emerged as an ideal tool for overcoming these hurdles. Languages like Cypher provide a simple semantic for detecting rings in the graph, navigating connections in memory, in real time.
79 |
80 | The graph data model below represents how the data actually looks to the graph database, and illustrates how one can find rings by simply walking the graph:
81 |
82 | image::https://raw.github.com/neo4j-contrib/gists/master/other/images/BankFraud-1.png[Bank Fraud]
83 |
84 | '''
85 |
86 | == Sample Data Set
87 |
88 | //setup
89 | [source,cypher]
90 | ----
91 |
92 | // Create account holders
93 | CREATE (accountHolder1:AccountHolder {
94 | FirstName: "John",
95 | LastName: "Doe",
96 | UniqueId: "JohnDoe" })
97 |
98 | CREATE (accountHolder2:AccountHolder {
99 | FirstName: "Jane",
100 | LastName: "Appleseed",
101 | UniqueId: "JaneAppleseed" })
102 |
103 | CREATE (accountHolder3:AccountHolder {
104 | FirstName: "Matt",
105 | LastName: "Smith",
106 | UniqueId: "MattSmith" })
107 |
108 | // Create Address
109 | CREATE (address1:Address {
110 | Street: "123 NW 1st Street",
111 | City: "San Francisco",
112 | State: "California",
113 | ZipCode: "94101" })
114 |
115 | // Connect 3 account holders to 1 address
116 | CREATE (accountHolder1)-[:HAS_ADDRESS]->(address1),
117 | (accountHolder2)-[:HAS_ADDRESS]->(address1),
118 | (accountHolder3)-[:HAS_ADDRESS]->(address1)
119 |
120 | // Create Phone Number
121 | CREATE (phoneNumber1:PhoneNumber { PhoneNumber: "555-555-5555" })
122 |
123 | // Connect 2 account holders to 1 phone number
124 | CREATE (accountHolder1)-[:HAS_PHONENUMBER]->(phoneNumber1),
125 | (accountHolder2)-[:HAS_PHONENUMBER]->(phoneNumber1)
126 |
127 | // Create SSN
128 | CREATE (ssn1:SSN { SSN: "241-23-1234" })
129 |
130 | // Connect 2 account holders to 1 SSN
131 | CREATE (accountHolder2)-[:HAS_SSN]->(ssn1),
132 | (accountHolder3)-[:HAS_SSN]->(ssn1)
133 |
134 | // Create SSN and connect 1 account holder
135 | CREATE (ssn2:SSN { SSN: "241-23-4567" })<-[:HAS_SSN]-(accountHolder1)
136 |
137 | // Create Credit Card and connect 1 account holder
138 | CREATE (creditCard1:CreditCard {
139 | AccountNumber: "1234567890123456",
140 | Limit: 5000, Balance: 1442.23,
141 | ExpirationDate: "01-20",
142 | SecurityCode: "123" })<-[:HAS_CREDITCARD]-(accountHolder1)
143 |
144 | // Create Bank Account and connect 1 account holder
145 | CREATE (bankAccount1:BankAccount {
146 | AccountNumber: "2345678901234567",
147 | Balance: 7054.43 })<-[:HAS_BANKACCOUNT]-(accountHolder1)
148 |
149 | // Create Credit Card and connect 1 account holder
150 | CREATE (creditCard2:CreditCard {
151 | AccountNumber: "1234567890123456",
152 | Limit: 4000, Balance: 2345.56,
153 | ExpirationDate: "02-20",
154 | SecurityCode: "456" })<-[:HAS_CREDITCARD]-(accountHolder2)
155 |
156 | // Create Bank Account and connect 1 account holder
157 | CREATE (bankAccount2:BankAccount {
158 | AccountNumber: "3456789012345678",
159 | Balance: 4231.12 })<-[:HAS_BANKACCOUNT]-(accountHolder2)
160 |
161 | // Create Unsecured Loan and connect 1 account holder
162 | CREATE (unsecuredLoan2:UnsecuredLoan {
163 | AccountNumber: "4567890123456789-0",
164 | Balance: 9045.53,
165 | APR: .0541,
166 | LoanAmount: 12000.00 })<-[:HAS_UNSECUREDLOAN]-(accountHolder2)
167 |
168 | // Create Bank Account and connect 1 account holder
169 | CREATE (bankAccount3:BankAccount {
170 | AccountNumber: "4567890123456789",
171 | Balance: 12345.45 })<-[:HAS_BANKACCOUNT]-(accountHolder3)
172 |
173 | // Create Unsecured Loan and connect 1 account holder
174 | CREATE (unsecuredLoan3:UnsecuredLoan {
175 | AccountNumber: "5678901234567890-0",
176 | Balance: 16341.95, APR: .0341,
177 | LoanAmount: 22000.00 })<-[:HAS_UNSECUREDLOAN]-(accountHolder3)
178 |
179 | // Create Phone Number and connect 1 account holder
180 | CREATE (phoneNumber2:PhoneNumber {
181 | PhoneNumber: "555-555-1234" })<-[:HAS_PHONENUMBER]-(accountHolder3)
182 |
183 | RETURN *
184 | ----
185 |
186 | //graph
187 |
188 | '''
189 |
190 | == Entity Link Analysis
191 |
192 | Performing entity link analysis on the above data model is demonstrated below.
193 |
194 | ==== Find account holders who share more than one piece of legitimate contact information
195 |
196 | [source,cypher]
197 | ----
198 | MATCH (accountHolder:AccountHolder)-[]->(contactInformation)
199 | WITH contactInformation,
200 | count(accountHolder) AS RingSize
201 | MATCH (contactInformation)<-[]-(accountHolder)
202 | WITH collect(accountHolder.UniqueId) AS AccountHolders,
203 | contactInformation, RingSize
204 | WHERE RingSize > 1
205 | RETURN AccountHolders AS FraudRing,
206 | labels(contactInformation) AS ContactType,
207 | RingSize
208 | ORDER BY RingSize DESC
209 | ----
210 |
211 | //output
212 | //table
213 |
214 | ==== Determine the financial risk of a possible fraud ring
215 |
216 | [source,cypher]
217 | ----
218 | MATCH (accountHolder:AccountHolder)-[]->(contactInformation)
219 | WITH contactInformation,
220 | count(accountHolder) AS RingSize
221 | MATCH (contactInformation)<-[]-(accountHolder),
222 | (accountHolder)-[r:HAS_CREDITCARD|HAS_UNSECUREDLOAN]->(unsecuredAccount)
223 | WITH collect(DISTINCT accountHolder.UniqueId) AS AccountHolders,
224 | contactInformation, RingSize,
225 | SUM(CASE type(r)
226 | WHEN 'HAS_CREDITCARD' THEN unsecuredAccount.Limit
227 | WHEN 'HAS_UNSECUREDLOAN' THEN unsecuredAccount.Balance
228 | ELSE 0
229 | END) as FinancialRisk
230 | WHERE RingSize > 1
231 | RETURN AccountHolders AS FraudRing,
232 | labels(contactInformation) AS ContactType,
233 | RingSize,
234 | round(FinancialRisk) as FinancialRisk
235 | ORDER BY FinancialRisk DESC
236 | ----
237 |
238 | //output
239 | //table
240 |
--------------------------------------------------------------------------------
/other/NetworkDataCenterManagement1.adoc:
--------------------------------------------------------------------------------
1 | = Network Dependency Graph
2 | :neo4j-version: 2.0.0-RC1
3 | :author: Kenny Bastani
4 | :twitter: @kennybastani
5 | :tags: domain:networks, use-case:network-dependencies
6 |
7 | This interactive Neo4j tutorial covers a scenario in Network and Data Center Management for ACME. ACME is a fictional company with a large network infrastructure that includes a CRM application, ERP application, and a Data Warehousing solution. ACME has a series of public facing websites that they need to be up and running in order to conduct their business and support their customers.
8 |
9 | '''
10 |
11 | *Table of Contents*
12 |
13 | * *Data Model*
14 | ** <>
15 | * *Cypher Script*
16 | ** <>
17 | * *Visualization*
18 | ** <>
19 | * *Find Network Inventory*
20 | ** <>
21 | * *Find Direct Dependencies*
22 | ** <>
23 | ** <>
24 | ** <>
25 | * *Find Crticial System Dependencies*
26 | ** <>
27 | ** <>
28 | ** <>
29 |
30 | '''
31 |
32 | === ACME's Network Dependency Graph
33 |
34 | image::http://raw.github.com/neo4j-contrib/gists/master/other/images/datacenter-management-1.PNG[Network Dependency Graph]
35 |
36 | === Database Setup
37 |
38 | Below you will find the full Cypher script for creating ACME's Network Dependency Graph in Neo4j. This simple script is the full setup of the data set that we will later perform analysis on.
39 |
40 | //setup
41 | [source,cypher]
42 | ----
43 | // Create CRM
44 | CREATE (crm1:Application {
45 | ip:'10.10.32.1',
46 | host:'CRM-APPLICATION',
47 | type: 'APPLICATION',
48 | system: 'CRM'
49 | })
50 |
51 | // Create ERP
52 | CREATE (erp1:Application {
53 | ip:'10.10.33.1',
54 | host:'ERP-APPLICATION',
55 | type: 'APPLICATION',
56 | system: 'ERP'
57 | })
58 |
59 | // Create Data Warehouse
60 | CREATE (datawarehouse1:Application {
61 | ip:'10.10.34.1',
62 | host:'DATA-WAREHOUSE',
63 | type: 'DATABASE',
64 | system: 'DW'
65 | })
66 |
67 | // Create Public Website 1
68 | CREATE (Internet1:Internet {
69 | ip:'10.10.35.1',
70 | host:'global.acme.com',
71 | type: "APPLICATION",
72 | system: "INTERNET"
73 | })
74 |
75 | // Create Public Website 2
76 | CREATE (Internet2:Internet {
77 | ip:'10.10.35.2',
78 | host:'support.acme.com',
79 | type: "APPLICATION",
80 | system: "INTERNET"
81 | })
82 |
83 | // Create Public Website 3
84 | CREATE (Internet3:Internet {
85 | ip:'10.10.35.3',
86 | host:'shop.acme.com',
87 | type: "APPLICATION",
88 | system: "INTERNET"
89 | })
90 |
91 | // Create Public Website 4
92 | CREATE (Internet4:Internet {
93 | ip:'10.10.35.4',
94 | host:'training.acme.com',
95 | type: "APPLICATION",
96 | system: "INTERNET"
97 | })
98 |
99 | // Create Public Website 5
100 | CREATE (Internet5:Internet {
101 | ip:'10.10.35.1',
102 | host:'partners.acme.com',
103 | type: "APPLICATION",
104 | system: "INTERNET"
105 | })
106 |
107 | // Create Internal Website 1
108 | CREATE (Intranet1:Intranet {
109 | ip:'10.10.35.2',
110 | host:'events.acme.net',
111 | type: "APPLICATION",
112 | system: "INTRANET"
113 | })
114 |
115 | // Create Internal Website 2
116 | CREATE (Intranet2:Intranet {
117 | ip:'10.10.35.3',
118 | host:'intranet.acme.net',
119 | type: "APPLICATION",
120 | system: "INTRANET"
121 | })
122 |
123 | // Create Internal Website 3
124 | CREATE (Intranet3:Intranet {
125 | ip:'10.10.35.4',
126 | host:'humanresources.acme.net',
127 | type: "APPLICATION",
128 | system: "INTRANET"
129 | })
130 |
131 | // Create Webserver VM 1
132 | CREATE (webservervm1:VirtualMachine {
133 | ip:'10.10.35.5',
134 | host:'WEBSERVER-1',
135 | type: "WEB SERVER",
136 | system: "VIRTUAL MACHINE"
137 | })
138 |
139 | // Create Webserver VM 2
140 | CREATE (webservervm2:VirtualMachine {
141 | ip:'10.10.35.6',
142 | host:'WEBSERVER-2',
143 | type: "WEB SERVER",
144 | system: "VIRTUAL MACHINE"
145 | })
146 |
147 | // Create Database VM 1
148 | CREATE (customerdatabase1:VirtualMachine {
149 | ip:'10.10.35.7',
150 | host:'CUSTOMER-DB-1',
151 | type: "DATABASE SERVER",
152 | system: "VIRTUAL MACHINE"
153 | })
154 |
155 | // Create Database VM 2
156 | CREATE (customerdatabase2:VirtualMachine {
157 | ip:'10.10.35.8',
158 | host:'CUSTOMER-DB-2',
159 | type: "DATABASE SERVER",
160 | system: "VIRTUAL MACHINE"
161 | })
162 |
163 | // Create Database VM 3
164 | CREATE (databasevm3:VirtualMachine {
165 | ip:'10.10.35.9',
166 | host:'ERP-DB',
167 | type: "DATABASE SERVER",
168 | system: "VIRTUAL MACHINE"
169 | })
170 |
171 | // Create Database VM 4
172 | CREATE (dwdatabase:VirtualMachine {
173 | ip:'10.10.35.10',
174 | host:'DW-DATABASE',
175 | type: "DATABASE SERVER",
176 | system: "VIRTUAL MACHINE"
177 | })
178 |
179 | // Create Hardware 1
180 | CREATE (hardware1:Hardware {
181 | ip:'10.10.35.11',
182 | host:'HARDWARE-SERVER-1',
183 | type: "HARDWARE SERVER",
184 | system: "PHYSICAL INFRASTRUCTURE"
185 | })
186 |
187 | // Create Hardware 2
188 | CREATE (hardware2:Hardware {
189 | ip:'10.10.35.12',
190 | host:'HARDWARE-SERVER-2',
191 | type: "HARDWARE SERVER",
192 | system: "PHYSICAL INFRASTRUCTURE"
193 | })
194 |
195 | // Create Hardware 3
196 | CREATE (hardware3:Hardware {
197 | ip:'10.10.35.13',
198 | host:'HARDWARE-SERVER-3',
199 | type: "HARDWARE SERVER",
200 | system: "PHYSICAL INFRASTRUCTURE"
201 | })
202 |
203 | // Create SAN 1
204 | CREATE (san1:Hardware {
205 | ip:'10.10.35.14',
206 | host:'SAN',
207 | type: "STORAGE AREA NETWORK",
208 | system: "PHYSICAL INFRASTRUCTURE"
209 | })
210 |
211 | // Connect CRM to Database VM 1
212 | CREATE (crm1)-[:DEPENDS_ON]->(customerdatabase1)
213 |
214 | // Connect Public Websites 1-3 to Database VM 1
215 | CREATE (Internet1)-[:DEPENDS_ON]->(customerdatabase1),
216 | (Internet2)-[:DEPENDS_ON]->(customerdatabase1),
217 | (Internet3)-[:DEPENDS_ON]->(customerdatabase1)
218 |
219 | // Connect Database VM 1 to Hardware 1
220 | CREATE (customerdatabase1)-[:DEPENDS_ON]->(hardware1)
221 |
222 | // Connect Hardware 1 to SAN 1
223 | CREATE (hardware1)-[:DEPENDS_ON]->(san1)
224 |
225 | // Connect Public Websites 1-3 to Webserver VM 1
226 | CREATE (webservervm1)<-[:DEPENDS_ON]-(Internet1),
227 | (webservervm1)<-[:DEPENDS_ON]-(Internet2),
228 | (webservervm1)<-[:DEPENDS_ON]-(Internet3)
229 |
230 | // Connect Internal Websites 1-3 to Webserver VM 1
231 | CREATE (webservervm1)<-[:DEPENDS_ON]-(Intranet1),
232 | (webservervm1)<-[:DEPENDS_ON]-(Intranet2),
233 | (webservervm1)<-[:DEPENDS_ON]-(Intranet3)
234 |
235 | // Connect Webserver VM 1 to Hardware 2
236 | CREATE (webservervm1)-[:DEPENDS_ON]->(hardware2)
237 |
238 | // Connect Hardware 2 to SAN 1
239 | CREATE (hardware2)-[:DEPENDS_ON]->(san1)
240 |
241 | // Connect Webserver VM 2 to Hardware 2
242 | CREATE (webservervm2)-[:DEPENDS_ON]->(hardware2)
243 |
244 | // Connect Public Websites 4-6 to Webserver VM 2
245 | CREATE (webservervm2)<-[:DEPENDS_ON]-(Internet4),
246 | (webservervm2)<-[:DEPENDS_ON]-(Internet5)
247 |
248 | // Connect Database VM 2 to Hardware 2
249 | CREATE (hardware2)<-[:DEPENDS_ON]-(customerdatabase2)
250 |
251 | // Connect Public Websites 4-5 to Database VM 2
252 | CREATE (Internet4)-[:DEPENDS_ON]->(customerdatabase2),
253 | (Internet5)-[:DEPENDS_ON]->(customerdatabase2)
254 |
255 | // Connect Hardware 3 to SAN 1
256 | CREATE (hardware3)-[:DEPENDS_ON]->(san1)
257 |
258 | // Connect Database VM 3 to Hardware 3
259 | CREATE (hardware3)<-[:DEPENDS_ON]-(databasevm3)
260 |
261 | // Connect ERP 1 to Database VM 3
262 | CREATE (erp1)-[:DEPENDS_ON]->(databasevm3)
263 |
264 | // Connect Database VM 4 to Hardware 3
265 | CREATE (hardware3)<-[:DEPENDS_ON]-(dwdatabase)
266 |
267 | // Connect Data Warehouse 1 to Database VM 4
268 | CREATE (datawarehouse1)-[:DEPENDS_ON]->(dwdatabase)
269 |
270 | RETURN *
271 |
272 | ----
273 |
274 | '''
275 |
276 | === Interactive Graph Visualization
277 | //graph
278 |
279 | '''
280 |
281 | === ACME's Network Inventory
282 |
283 | The query below generates a data table that gives a quick overview of ACME's network infrastructure.
284 |
285 | [source,cypher]
286 | ----
287 | MATCH (n)
288 | RETURN labels(n)[0] as type,
289 | count(*) as count,
290 | collect(n.host) as names
291 | ----
292 |
293 | //table
294 |
295 | '''
296 |
297 | === Find direct dependencies of all public websites
298 |
299 | The query below queries the data model to find all business web applications that are on the public facing internet for ACME.
300 |
301 | [source,cypher]
302 | ----
303 | MATCH (website)-[:DEPENDS_ON]->(downstream)
304 | WHERE website.system = "INTERNET"
305 | RETURN website.host as Host,
306 | collect(downstream.host) as Dependencies
307 | ORDER BY Host
308 | ----
309 |
310 | //table
311 |
312 | '''
313 |
314 | === Find direct dependencies of all internal websites
315 |
316 | The query below queries the data model to find all business websites that are on the private intranet for ACME.
317 |
318 | [source,cypher]
319 | ----
320 | MATCH (website)-[:DEPENDS_ON]->(downstream)
321 | WHERE website.system = "INTRANET"
322 | RETURN website.host as Host,
323 | collect(downstream.host) as Dependencies
324 | ORDER BY Host
325 | ----
326 |
327 | //table
328 |
329 | '''
330 |
331 | === Find the most depended-upon component
332 |
333 | The query below finds the most heavily relied upon component within ACME's network infrastructure. As expected, the most depended upon component is the SAN (Storage Area Network).
334 |
335 | [source,cypher]
336 | ----
337 | MATCH (n)<-[:DEPENDS_ON*]-(dependent)
338 | RETURN n.host as Host,
339 | count(DISTINCT dependent) AS Dependents
340 | ORDER BY Dependents DESC
341 | LIMIT 1
342 | ----
343 |
344 | //table
345 |
346 | '''
347 |
348 | === Find dependency chain for business critical components: CRM
349 |
350 | The query below finds the path of dependent components from left to right for ACME's CRM application. If ACME's CRM (Customer Relationship Management) application goes down it will cause significant impacts to its business. If any one of the components to the right of the CRM hostname fails, the CRM application will fail.
351 |
352 | [source,cypher]
353 | ----
354 | MATCH (dependency)<-[:DEPENDS_ON*]-(dependent)
355 | WITH dependency, count(DISTINCT dependent) AS Dependents
356 | ORDER BY Dependents DESC
357 | LIMIT 1
358 | WITH dependency
359 | MATCH p=(resource)-[:DEPENDS_ON*]->(dependency)
360 | WHERE resource.system = "CRM"
361 | RETURN "[" + head(nodes(p)).host + "]" +
362 | reduce(s = "", n in tail(nodes(p)) | s + " -> " + "[" + n.host + "]") as Chain
363 | ----
364 |
365 | //table
366 |
367 | '''
368 |
369 | === Find dependency chain for business critical components: ERP
370 |
371 | The query below finds the path of dependent components from left to right for ACME's ERP (Enterprise Resource Planning) application. The ERP application represents an array of business resources dedicated to supporting ongoing business activities at ACME, including finance and supply chain management. If ACME's ERP application goes down it will cause significant impacts to its business. If any one of the components to the right of the ERP hostname fails, then the ERP application will fail. This failure will cause revenue impacts since ACME's business relies on this system to conduct business.
372 |
373 | [source,cypher]
374 | ----
375 | MATCH (dependency)<-[:DEPENDS_ON*]-(dependent)
376 | WITH dependency, count(DISTINCT dependent) AS Dependents
377 | ORDER BY Dependents DESC
378 | LIMIT 1
379 | WITH dependency
380 | MATCH p=(resource)-[:DEPENDS_ON*]->(dependency)
381 | WHERE resource.system = "ERP"
382 | RETURN "[" + head(nodes(p)).host + "]" +
383 | reduce(s = "", n in tail(nodes(p)) | s + " -> " + "[" + n.host + "]") as Chain
384 | ----
385 |
386 | //table
387 |
388 | '''
389 |
390 | === Find dependency chain for business critical components: Data Warehouse
391 |
392 | The query below finds the path of dependent components from left to right for ACME's DW (Data Warehouse) application. The DW application represents an array of business intelligence resources dedicated to supporting time-sensitive analytical processes at ACME. If ACME's DW application goes down it will cause significant impacts to the business operations at ACME on the technical side. If any one of the components to the right of the DW hostname fails, then the DW application will fail. This failure will cause public facing websites like the eCommerce application to not reflect the latest available data from ACME's ERP application.
393 |
394 | [source,cypher]
395 | ----
396 | MATCH (dependency)<-[:DEPENDS_ON*]-(dependent)
397 | WITH dependency, count(DISTINCT dependent) AS Dependents
398 | ORDER BY Dependents DESC
399 | LIMIT 1
400 | WITH dependency
401 | MATCH p=(resource)-[:DEPENDS_ON*]->(dependency)
402 | WHERE resource.system = "DW"
403 | RETURN "[" + head(nodes(p)).host + "]" +
404 | reduce(s = "", n in tail(nodes(p)) | s + " -> " + "[" + n.host + "]") as Chain
405 | ----
406 |
407 | //table
408 |
409 | === Find the impact of the removal of a network component : Hardware Server
410 |
411 | The query below finds the applications depending on ACME's HARDWARE-SERVER-3. In case a network administrator wants to plan an intervention on the server, he has to know what will be the applications impacted. This way he can warn the applications users.
412 |
413 | [source,cypher]
414 | ----
415 | MATCH (application:Application)-[:DEPENDS_ON*]->(server)
416 | WHERE server.host = "HARDWARE-SERVER-3"
417 | RETURN application.type as Type,
418 | application.host as Host
419 | ----
420 |
421 | //table
422 |
--------------------------------------------------------------------------------
/other/SocialNetworkGraph.adoc:
--------------------------------------------------------------------------------
1 | = Social Network Graph Example from Diaspora
2 |
3 | Sarah Mei recently wrote a http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/[blog post] describing how she and her colleagues modeled a collection of TV shows and a social network. A few of us at Neo felt strongly that these domains were very graphy in nature.
4 |
5 | We wanted to see if our intuition was right (we love the Diaspora project and extend our help to you) and so with the help of http://docs.neo4j.org/chunked/preview/cypher-query-lang.html[Cypher], created a mini version of each of the domains that Sarah described.
6 |
7 | This is the second of two GraphGists that are part of the http://blog.neo4j.org/2013/11/why-graph-databases-are-best-tool-for-handling-connected-data-like-diaspora.html[response to Sarah's blog post]. If you would like to quickly jump to the first one that details the TV Show Graph data model, you can find it here: http://gist.neo4j.org/?github-neo4j-contrib%2Fgists%2F%2Fother%2FTVShowGraph.adoc[TV Show Graph Diaspora example]
8 |
9 | == Social Network Data Model
10 |
11 | image::http://www.sarahmei.com/blog/wp-content/uploads/2013/11/Screen-Shot-2013-11-09-at-8.11.30-PM.png[]
12 |
13 | == Social Network Setup
14 |
15 | // setup
16 | // hide
17 | [source,cypher]
18 | ----
19 | CREATE (rachel:User {name: "Rachel Green"})
20 | CREATE (monica:User {name: "Monica Geller"})
21 | CREATE (phoebe:User {name: "Phoebe Buffay"})
22 | CREATE (joey:User {name: "Joey Tribbiani"})
23 | CREATE (chandler:User {name: "Chandler Bing"})
24 | CREATE (ross:User {name: "Ross Geller"})
25 |
26 | CREATE (rachel)-[:FRIEND]->(monica)
27 | CREATE (rachel)-[:FRIEND]->(phoebe)
28 | CREATE (rachel)-[:FRIEND]->(joey)
29 | CREATE (rachel)-[:FRIEND]->(chandler)
30 | CREATE (rachel)-[:FRIEND]->(ross)
31 |
32 | CREATE (monica)-[:FRIEND]->(phoebe)
33 | CREATE (monica)-[:FRIEND]->(joey)
34 | CREATE (monica)-[:FRIEND]->(chandler)
35 | CREATE (monica)-[:FRIEND]->(ross)
36 |
37 | CREATE (phoebe)-[:FRIEND]->(joey)
38 | CREATE (phoebe)-[:FRIEND]->(chandler)
39 | CREATE (phoebe)-[:FRIEND]->(ross)
40 |
41 | CREATE (joey)-[:FRIEND]->(chandler)
42 | CREATE (joey)-[:FRIEND]->(ross)
43 |
44 | CREATE (chandler)-[:FRIEND]->(ross)
45 |
46 | CREATE (monica)-[:POSTED]->(post0:Post { id: "0", text: "You don't like the game, because you suck at it." })
47 | CREATE (phoebe)-[:POSTED]->(post1:Post { id: "1", text: "Chandler still thinks I'm pregnant and he hasn't asked me how I'm feeling or offered to carry my bags. I feel bad for the woman who ends up with him." })
48 | CREATE (joey)-[:POSTED]->(post2:Post { id: "2", text: "Just because she went to Yale drama, she thinks she's like the greatest actress since, since, sliced bread!" })
49 | CREATE (chandler)-[:POSTED]->(post3:Post { id: "3", text: " In my defense, it was dark and he was a very pretty guy." })
50 | CREATE (rachel)-[:POSTED]->(post4:Post { id: "4", text: "I don't want my baby's first words to be 'How You Doing'" })
51 | CREATE (ross)-[:POSTED]->(post5:Post { id: "5", text: "Chandler entered a Vanilla Ice look-alike contest and *won*!" })
52 |
53 | // Comments
54 | CREATE (post0)<-[:COMMENTED{ id: "6", text: "lol" }]-(ross)
55 | CREATE (post1)<-[:COMMENTED{ id: "7", text: "lol" }]-(rachel)
56 | CREATE (post2)<-[:COMMENTED{ id: "8", text: "lol" }]-(chandler)
57 | CREATE (post3)<-[:COMMENTED{ id: "9", text: "lol" }]-(joey)
58 | CREATE (post4)<-[:COMMENTED{ id: "10", text: "lol" }]-(phoebe)
59 | CREATE (post5)<-[:COMMENTED{ id: "11", text: "lol" }]-(monica)
60 |
61 |
62 | // Likes
63 | CREATE (post0)<-[:LIKED{ id: "12", text: "Ross likes this." }]-(ross)
64 | CREATE (post1)<-[:LIKED{ id: "13", text: "Rachel likes this." }]-(rachel)
65 | CREATE (post2)<-[:LIKED{ id: "14", text: "Chandler likes this." }]-(chandler)
66 | CREATE (post3)<-[:LIKED{ id: "15", text: "Joey likes this." }]-(joey)
67 | CREATE (post4)<-[:LIKED{ id: "16", text: "Phoebe likes this." }]-(phoebe)
68 | CREATE (post5)<-[:LIKED{ id: "17", text: "Monica likes this." }]-(monica)
69 | ----
70 |
71 | image::https://raw.github.com/neo4j-contrib/gists/master/other/images/social-network-graph-1.png[]
72 |
73 | Now let's write some queries!
74 |
75 | We'll start with one to find the posts made by Rachel's friends:
76 |
77 | [source,cypher]
78 | ----
79 | MATCH (u:User)-[:FRIEND]-(f)-[:POSTED]->(post)
80 | WHERE u.name = "Rachel Green"
81 | RETURN f.name AS friend, post.text as content
82 | ----
83 |
84 | // table
85 |
86 | Our next query might be a slight variation on this to collect the people who have liked a post:
87 |
88 | [source,cypher]
89 | ----
90 | MATCH (u:User)-[:FRIEND]-(f)-[:POSTED]->(post)<-[like:LIKED]-(liker)
91 | WHERE u.name = "Rachel Green"
92 | RETURN f.name AS friend, post.text as content, COLLECT(liker.name) as liked_by
93 | ----
94 |
95 | // table
96 |
97 | A third query could collect all actions on a post, be they likes or comments:
98 |
99 | [source,cypher]
100 | ----
101 | MATCH (u:User)-[:FRIEND]-(f)-[:POSTED]->(post)<-[a:LIKED|:COMMENTED]->(person)
102 | WHERE u.name = "Rachel Green"
103 | RETURN f.name AS friend, post.text AS content,
104 | COLLECT({ text: a.text, person: person.name, action: TYPE(a)}) AS actions
105 | ----
106 |
107 | // table
108 |
109 |
110 | //graph
111 |
112 |
--------------------------------------------------------------------------------
/other/TVShowGraph.adoc:
--------------------------------------------------------------------------------
1 | = TV Show Graph
2 |
3 | :neo4j-version: 2.0.0-RC1
4 | :author: Kenny Bastani
5 | :twitter: @kennybastani
6 |
7 | Sarah Mei recently wrote a http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/[blog post] describing how she and her colleagues modeled a collection of TV shows and a social network. A few of us at Neo felt strongly that these domains were very graphy in nature.
8 |
9 | We wanted to see if our intuition was right (we love the Diaspora project and extend our help to you) and so with the help of http://docs.neo4j.org/chunked/preview/cypher-query-lang.html[Cypher], created a mini version of each of the domains that Sarah described.
10 |
11 | This is the first of two GraphGists that are part of http://blog.neo4j.org/2013/11/why-graph-databases-are-best-tool-for-handling-connected-data-like-diaspora.html[the response to Sarah's blog post]. If you would like to quickly jump to the second one that details the social networking data model, you can find it here: http://gist.neo4j.org/?github-neo4j-contrib%2Fgists%2F%2Fother%2FSocialNetworkGraph.adoc[Social Network Graph Diaspora example]
12 |
13 | == TV Show Data Model
14 |
15 | image::http://www.sarahmei.com/blog/wp-content/uploads/2013/11/Screen-Shot-2013-11-09-at-7.09.27-PM.png[]
16 |
17 | == The TV Series setup
18 |
19 | // setup
20 | // hide
21 | [source,cypher]
22 | ----
23 | CREATE (himym:TVShow {name: "How I Met Your Mother"})
24 | CREATE (himym_s1:Season {name: "HIMYM Season 1"})
25 | CREATE (himym_s1_e1:Episode {name: "Pilot"})
26 |
27 | CREATE (ted:Character {name: "Ted Mosby"})
28 | CREATE (marshall:Character {name: "Marshall Eriksen"})
29 | CREATE (robin:Character {name: "Robin Scherbatsky"})
30 | CREATE (barney:Character {name: "Barney Stinson"})
31 | CREATE (lily:Character {name: "Lily Aldrin"})
32 |
33 | CREATE (joshRadnor:Actor {name: "Josh Radnor"})
34 | CREATE (jasonSegel:Actor {name: "Jason Segel"})
35 | CREATE (cobieSmulders:Actor {name: "Cobie Smulders"})
36 | CREATE (neilPatrickHarris:Actor {name: "Neil Patrick Harris"})
37 | CREATE (alysonHannigan:Actor {name: "Alyson Hannigan"})
38 |
39 | CREATE UNIQUE (joshRadnor)-[:PLAYED_CHARACTER]->(ted)
40 | CREATE UNIQUE (jasonSegel)-[:PLAYED_CHARACTER]->(marshall)
41 | CREATE UNIQUE (cobieSmulders)-[:PLAYED_CHARACTER]->(robin)
42 | CREATE UNIQUE (neilPatrickHarris)-[:PLAYED_CHARACTER]->(barney)
43 | CREATE UNIQUE (alysonHannigan)-[:PLAYED_CHARACTER]->(lily)
44 |
45 | CREATE UNIQUE (himym)-[:HAS_SEASON]->(himym_s1)
46 |
47 | CREATE UNIQUE (himym_s1)-[:HAS_EPISODE]->(himym_s1_e1)
48 |
49 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(ted)
50 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(marshall)
51 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(robin)
52 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(barney)
53 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(lily)
54 |
55 | CREATE (himym_s1_e1_review1 {title: "Meet Me At The Bar In 15 Minutes & Suit Up", content: "It was awesome"})
56 | CREATE (wakenPayne:User {name: "WakenPayne"})
57 |
58 | CREATE (himym_s1_e1_review2 {title: "What a great pilot for a show :)", content: "The humour is great."})
59 | CREATE (atlasredux:User {name: "atlasredux"})
60 |
61 | CREATE (wakenPayne)-[:WROTE_REVIEW]->(himym_s1_e1_review1)<-[:HAS_REVIEW]-(himym_s1_e1)
62 | CREATE (atlasredux)-[:WROTE_REVIEW]->(himym_s1_e1_review2)<-[:HAS_REVIEW]-(himym_s1_e1)
63 |
64 | ----
65 |
66 | //graph
67 |
68 | image::https://raw.github.com/neo4j-contrib/gists/master/other/images/tv-show-graph-2.PNG[]
69 |
70 | == All info for a show
71 |
72 | For a particular TV show, show all the seasons and all the episodes and all the reviews and all the cast members from that show i.e. all of the information connected to that TV show.
73 |
74 | [source,cypher]
75 | ----
76 | MATCH (tvShow:TVShow)-[:HAS_SEASON]->(season)-[:HAS_EPISODE]->(episode)
77 | WHERE tvShow.name = "How I Met Your Mother"
78 | RETURN season, episode
79 | ----
80 |
81 | //table
82 |
83 | We could also grab the reviews if there are any by slightly tweaking the query:
84 |
85 | [source,cypher]
86 | ----
87 | MATCH (tvShow:TVShow)-[:HAS_SEASON]->(season)-[:HAS_EPISODE]->(episode)
88 | WHERE tvShow.name = "How I Met Your Mother"
89 | WITH season, episode
90 | OPTIONAL MATCH (episode)-[:HAS_REVIEW]->(review)
91 | RETURN season, episode, review
92 | ----
93 |
94 | //table
95 |
96 | == Chronological listing of all episodes
97 |
98 | Sarah’s next query was:
99 |
100 | Display a chronological listing of all of the episodes of all the different shows that an actor had ever been in.
101 |
102 | First let’s add another TV show that Josh Radnor appeared in:
103 |
104 | [source,cypher]
105 | ----
106 | CREATE (er:TVShow {name: "ER"})
107 | CREATE (er_s9:Season {name: "ER S7"})
108 | CREATE (er_s9_e17:Episode {name: "Peter's Progress"})
109 | CREATE (tedMosby:Character {name: "The Advocate "})
110 |
111 | CREATE UNIQUE (er)-[:HAS_SEASON]->(er_s9)
112 | CREATE UNIQUE (er_s9)-[:HAS_EPISODE]->(er_s9_e17)
113 |
114 | WITH er_s9_e17
115 |
116 | MATCH (actor:Actor), (episode:Episode)
117 | WHERE actor.name = "Josh Radnor" AND episode.name = "Peter's Progress"
118 | WITH actor, episode
119 |
120 | CREATE (keith {name: "Keith"})
121 | CREATE UNIQUE (actor)-[:PLAYED_CHARACTER]->(keith)
122 | CREATE UNIQUE (episode)-[:FEATURED_CHARACTER]->(keith)
123 | ----
124 |
125 | And now we’ll create a query to find the shows that he’s appeared in:
126 |
127 | [source,cypher]
128 | ----
129 | MATCH (actor:Actor)-[:PLAYED_CHARACTER]->(character)<-[:FEATURED_CHARACTER]-(episode)
130 | WHERE actor.name = "Josh Radnor"
131 | RETURN episode.name, character.name
132 | ----
133 | //table
134 |
135 | We could then easily add in more seasons, episodes and reviews if we wanted to.
136 |
--------------------------------------------------------------------------------
/other/ThePublicationGraph.adoc:
--------------------------------------------------------------------------------
1 | = The Publication Graph
2 |
3 | In the world of publications and CMSes, meta-data about different articles, authors, issues and other entities lends itself very obviously to a graph.
4 | This example is modeling just a small subset of a fictive domain in this area.
5 |
6 | == Prepare the schema, make `Person` unique
7 |
8 | [source,cypher]
9 | ----
10 | CREATE CONSTRAINT ON (p:Person) ASSERT p.handle IS UNIQUE
11 | ----
12 |
13 |
14 | == Insert some basic data
15 |
16 | [source,cypher]
17 | ----
18 | CREATE
19 | (JM_DE:Publication{name:'Java Magazin', language:'DE'}),
20 | (JM_DE)<-[:ISSUE_OF]-(JMNov2013{month:11, title:'Java Magazin 11/2013'})-[:IN_YEAR]->(_2013{year:2013}),
21 | (Neo4j20Tutorial:Content{title:'Neo4j 2.0 Tutorial'}),
22 | (SnS:Publisher{name:'S&S Media'})-[:PUBLISHES]->(JM_DE),
23 | (JMNov2013)-[:CONTAINS]->(Neo4j20Tutorial),
24 | (Olli:Reader{name:'Oliver Meyer',handle:'@olli'})-[:RATED{rating:4}]->(Neo4j20Tutorial),
25 | (MH:Author:Reader{name:'Michael Hunger',handle:'@mesirii'})-[:AUTHORED]->(Neo4j20Tutorial),
26 | (Neo4j20Tutorial)-[:RELATED_TO]->(Neo4j20Rel:Content{title:'Neo4j 2.0-M05 released'})-[:TAGGED]->(NoSQL:Tag{name:'NOSQL'}),
27 | (Neo4j20Tutorial)-[:TAGGED]->(NoSQL),
28 | (Neo4j20Tutorial)-[:TAGGED]->(:Tag{name:'tutorial'}),
29 | (Neo4j20Tutorial)-[:TAGGED]->(:Tag{name:'Neo4j'})
30 | ----
31 |
32 | //graph
33 |
34 | == Find all articles in 2013 tagged with `NOSQL`
35 |
36 | [source,cypher]
37 | ----
38 | MATCH
39 | (year)<-[:IN_YEAR]-(issue)-[:ISSUE_OF]->(pub:Publication),
40 | (issue)-[:CONTAINS]->(content:Content)-[:TAGGED]->(nosql:Tag)
41 | WHERE
42 | year.year = 2013 AND
43 | nosql.name='NOSQL'
44 | RETURN
45 | content.title as Title, issue.title as Issue, pub.name as Publication
46 | ----
47 |
48 | //table
49 |
--------------------------------------------------------------------------------
/other/images/BankFraud-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neo4j-contrib/gists/c390f2bda6a0cdf6bb9e1f9766d001d72510ca8c/other/images/BankFraud-1.png
--------------------------------------------------------------------------------
/other/images/datacenter-management-1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neo4j-contrib/gists/c390f2bda6a0cdf6bb9e1f9766d001d72510ca8c/other/images/datacenter-management-1.PNG
--------------------------------------------------------------------------------
/other/images/network-inventory-cost-accounting-graph-1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neo4j-contrib/gists/c390f2bda6a0cdf6bb9e1f9766d001d72510ca8c/other/images/network-inventory-cost-accounting-graph-1.PNG
--------------------------------------------------------------------------------
/other/images/social-network-graph-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neo4j-contrib/gists/c390f2bda6a0cdf6bb9e1f9766d001d72510ca8c/other/images/social-network-graph-1.png
--------------------------------------------------------------------------------
/other/images/tv-show-graph-1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neo4j-contrib/gists/c390f2bda6a0cdf6bb9e1f9766d001d72510ca8c/other/images/tv-show-graph-1.PNG
--------------------------------------------------------------------------------
/other/images/tv-show-graph-2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neo4j-contrib/gists/c390f2bda6a0cdf6bb9e1f9766d001d72510ca8c/other/images/tv-show-graph-2.PNG
--------------------------------------------------------------------------------
/other/sarah.adoc:
--------------------------------------------------------------------------------
1 | = The Diaspora data model - OMG it's a graph!
2 |
3 | Sarah Mei recently wrote a http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/[blog post] describing how she and her colleagues modelled a TV show and a social network, and as I read more of the post it felt like both domains were very graphy in nature.
4 |
5 | We wanted to see if our intuition was right (we love the Diaspora project and extend our help to you) and so with the help of http://docs.neo4j.org/chunked/preview/cypher-query-lang.html[Cypher], created a mini version of each of the domains that Sarah described.
6 |
7 | The TV show model is as follows:
8 |
9 | image::http://www.sarahmei.com/blog/wp-content/uploads/2013/11/Screen-Shot-2013-11-09-at-7.09.27-PM.png[]
10 |
11 | == The TV Series setup
12 |
13 | [source,cypher]
14 | ----
15 | CREATE (himym:TVShow {name: "How I Met Your Mother"})
16 | CREATE (himym_s1:Season {name: "HIMYM Season 1"})
17 | CREATE (himym_s1_e1:Episode {name: "Pilot"})
18 |
19 | CREATE (ted:Character {name: "Ted Mosby"})
20 | CREATE (marshall:Character {name: "Marshall Eriksen"})
21 | CREATE (robin:Character {name: "Robin Scherbatsky"})
22 | CREATE (barney:Character {name: "Barney Stinson"})
23 | CREATE (lily:Character {name: "Lily Aldrin"})
24 |
25 | CREATE (joshRadnor:Actor {name: "Josh Radnor"})
26 | CREATE (jasonSegel:Actor {name: "Jason Segel"})
27 | CREATE (cobieSmulders:Actor {name: "Cobie Smulders"})
28 | CREATE (neilPatrickHarris:Actor {name: "Neil Patrick Harris"})
29 | CREATE (alysonHannigan:Actor {name: "Alyson Hannigan"})
30 |
31 | CREATE UNIQUE (joshRadnor)-[:PLAYED_CHARACTER]->(ted)
32 | CREATE UNIQUE (jasonSegel)-[:PLAYED_CHARACTER]->(marshall)
33 | CREATE UNIQUE (cobieSmulders)-[:PLAYED_CHARACTER]->(robin)
34 | CREATE UNIQUE (neilPatrickHarris)-[:PLAYED_CHARACTER]->(barney)
35 | CREATE UNIQUE (alysonHannigan)-[:PLAYED_CHARACTER]->(lily)
36 |
37 | CREATE UNIQUE (himym)-[:HAS_SEASON]->(himym_s1)
38 |
39 | CREATE UNIQUE (himym_s1)-[:HAS_EPISODE]->(himym_s1_e1)
40 |
41 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(ted)
42 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(marshall)
43 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(robin)
44 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(barney)
45 | CREATE UNIQUE (himym_s1_e1)-[:FEATURED_CHARACTER]->(lily)
46 |
47 | CREATE (himym_s1_e1_review1 {title: "Meet Me At The Bar In 15 Minutes & Suit Up", content: "It was awesome"})
48 | CREATE (wakenPayne:User {name: "WakenPayne"})
49 |
50 | CREATE (himym_s1_e1_review2 {title: "What a great pilot for a show :)", content: "The humour is great."})
51 | CREATE (atlasredux:User {name: "atlasredux"})
52 |
53 | CREATE (wakenPayne)-[:WROTE_REVIEW]->(himym_s1_e1_review1)<-[:HAS_REVIEW]-(himym_s1_e1)
54 | CREATE (atlasredux)-[:WROTE_REVIEW]->(himym_s1_e1_review2)<-[:HAS_REVIEW]-(himym_s1_e1)
55 |
56 | ----
57 |
58 | //graph
59 |
60 |
61 | == All info for a show
62 |
63 | For a particular TV show, show all the seasons and all the episodes and all the reviews and all the cast members from that show i.e. all of the information connected to that TV show.
64 |
65 | [source,cypher]
66 | ----
67 | MATCH (tvShow:TVShow)-[:HAS_SEASON]->(season)-[:HAS_EPISODE]->(episode)
68 | WHERE tvShow.name = "How I Met Your Mother"
69 | RETURN season, episode
70 | ----
71 |
72 | //table
73 |
74 | We could also grab the reviews if there are any by slightly tweaking the query:
75 |
76 | [source,cypher]
77 | ----
78 | MATCH (tvShow:TVShow)-[:HAS_SEASON]->(season)-[:HAS_EPISODE]->(episode)
79 | WHERE tvShow.name = "How I Met Your Mother"
80 | WITH season, episode
81 | MATCH episode-[?:HAS_REVIEW]->(review)
82 | RETURN season, episode, review
83 | ----
84 |
85 | //table
86 |
87 | == Chronological listing of all episodes
88 |
89 | Sarah’s next query was:
90 |
91 | Display a chronological listing of all of the episodes of all the different shows that an actor had ever been in.
92 |
93 | First let’s add another TV show that Josh Radnor appeared in:
94 |
95 | [source,cypher]
96 | ----
97 | CREATE (er:TVShow {name: "ER"})
98 | CREATE (er_s9:Season {name: "ER S7"})
99 | CREATE (er_s9_e17:Episode {name: "Peter's Progress"})
100 | CREATE (tedMosby:Character {name: "The Advocate "})
101 |
102 | CREATE UNIQUE (er)-[:HAS_SEASON]->(er_s9)
103 | CREATE UNIQUE (er_s7)-[:HAS_EPISODE]->(er_s9_e17)
104 |
105 | WITH er_s9_e17
106 |
107 | MATCH (actor:Actor), (episode:Episode)
108 | WHERE actor.name = "Josh Radnor" AND episode.name = "Peter's Progress"
109 | WITH actor, episode
110 |
111 | CREATE (keith {name: "Keith"})
112 | CREATE UNIQUE (actor)-[:PLAYED_CHARACTER]->(keith)
113 | CREATE UNIQUE (episode)-[:FEATURED_CHARACTER]->(keith)
114 | ----
115 |
116 | And now we’ll create a query to find the shows that he’s appeared in:
117 |
118 | [source,cypher]
119 | ----
120 | MATCH (actor:Actor)-[:PLAYED_CHARACTER]->(character)<-[:FEATURED_CHARACTER]-(episode)
121 | WHERE actor.name = "Josh Radnor"
122 | RETURN episode.name, character.name
123 | ----
124 | //table
125 |
126 | We could then easily add in more seasons, episodes and reviews if we wanted to.
127 |
--------------------------------------------------------------------------------