├── adoc ├── browser.adoc ├── query-template.adoc ├── restaurant_recommendation.adoc └── template-table.adoc ├── docs ├── adoc-guides.adoc ├── html-guides.adoc ├── index.adoc └── remote.html ├── html ├── browser.html ├── restaurant_recommendation.html └── restaurant_recommendation.txt.html ├── http-server.py ├── http.rb ├── http.sh ├── img ├── sushi_restaurants_nyc.png └── sushi_restaurants_nyc.svg ├── readme.adoc ├── run.sh └── templates ├── block_admonition.html.erb ├── block_audio.html.erb ├── block_colist.html.erb ├── block_dlist.html.erb ├── block_example.html.erb ├── block_floating_title.html.erb ├── block_image.html.erb ├── block_listing.html.erb ├── block_literal.html.erb ├── block_math.html.erb ├── block_olist.html.erb ├── block_open.html.erb ├── block_page_break.html.erb ├── block_paragraph.html.erb ├── block_pass.html.erb ├── block_preamble.html.erb ├── block_quote.html.erb ├── block_ruler.html.erb ├── block_sidebar.html.erb ├── block_table.html.erb ├── block_toc.html.erb ├── block_ulist.html.erb ├── block_verse.html.erb ├── block_video.html.erb ├── document.html.erb ├── embedded.html.erb ├── inline_anchor.html.erb ├── inline_break.html.erb ├── inline_button.html.erb ├── inline_callout.html.erb ├── inline_footnote.html.erb ├── inline_image.html.erb ├── inline_indexterm.html.erb ├── inline_kbd.html.erb ├── inline_menu.html.erb ├── inline_quoted.html.erb └── section.html.erb /adoc/browser.adoc: -------------------------------------------------------------------------------- 1 | :experimental: 2 | 3 | == Get Started 4 | 5 | include::../../developer-resources/get-started/guide-neo4j-browser/guide-neo4j-browser.adoc[tags=guide] -------------------------------------------------------------------------------- /adoc/query-template.adoc: -------------------------------------------------------------------------------- 1 | = Query Template 2 | 3 | :nodeLabelA: pass:a[`Person`] 4 | :propertyKeyA: pass:a[`name`] 5 | :propertyValueA: pass:a['Keanu Reeves'] 6 | :nodeLabelB: pass:a[`Movie`] 7 | :propertyKeyB: pass:a[`title`] 8 | :propertyValueB: pass:a['The Matrix'] 9 | :depth: pass:a[3] 10 | :relationshipType: pass:a[`ACTED_IN`] 11 | 12 | == Create Nodes 13 | 14 | include::template-table.adoc[] 15 | 16 | // .Create Node A 17 | // {nodeLabelA} 18 | [source,cypher,subs=attributes] 19 | ---- 20 | CREATE (a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} }) RETURN a 21 | ---- 22 | 23 | // .Create Node B 24 | // {nodeLabelB} 25 | [source,cypher,subs=attributes] 26 | ---- 27 | CREATE (b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} }) RETURN b 28 | ---- 29 | 30 | == Create Relationship 31 | 32 | include::template-table.adoc[] 33 | 34 | // .Relate {nodeLabelA} via {relationshipType} to {nodeLabelB} 35 | [source,cypher,subs=attributes] 36 | ---- 37 | MATCH (a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} }) 38 | MATCH (b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} }) 39 | CREATE (a)-[:{relationshipType}]->(b) 40 | RETURN * 41 | ---- 42 | 43 | == Merge Nodes 44 | 45 | include::template-table.adoc[] 46 | 47 | // .Merge Node {nodeLabelA} 48 | [source,cypher,subs=attributes] 49 | ---- 50 | MERGE (a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} }) RETURN a 51 | ---- 52 | 53 | // .Merge Node {nodeLabelB} 54 | [source,cypher,subs=attributes] 55 | ---- 56 | MERGE (b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} }) RETURN b 57 | ---- 58 | 59 | == Merge Relationship 60 | 61 | include::template-table.adoc[] 62 | 63 | //.Relate {nodeLabelA} via {relationshipType} to {nodeLabelB} 64 | [source,cypher,subs=attributes] 65 | ---- 66 | MERGE (a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} }) 67 | MERGE (b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} }) 68 | MERGE (a)-[:{relationshipType}]->(b) 69 | RETURN * 70 | ---- 71 | 72 | == Find Nodes 73 | 74 | include::template-table.adoc[] 75 | 76 | // .Find Node {nodeLabelA} 77 | [source,cypher,subs=attributes] 78 | ---- 79 | MATCH (a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} }) RETURN a 80 | ---- 81 | 82 | // .Find Node {nodeLabelB} 83 | [source,cypher,subs=attributes] 84 | ---- 85 | MATCH (b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} }) RETURN b 86 | ---- 87 | 88 | == Find All Nodes 89 | 90 | include::template-table.adoc[] 91 | 92 | // .Find All Nodes {nodeLabelA} 93 | [source,cypher,subs=attributes] 94 | ---- 95 | MATCH (a:{nodeLabelA}) RETURN a LIMIT 100 96 | ---- 97 | 98 | // .Find All Nodes {nodeLabelB} 99 | [source,cypher,subs=attributes] 100 | ---- 101 | MATCH (b:{nodeLabelB}) RETURN b LIMIT 100 102 | ---- 103 | 104 | == Find Neighbours 105 | 106 | include::template-table.adoc[] 107 | 108 | // .Find Neighbours of {nodeLabelA} 109 | [source,cypher,subs=attributes] 110 | ---- 111 | MATCH (a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} })-[:{relationshipType}]->(b) RETURN * LIMIT 100 112 | ---- 113 | 114 | // .Find Neighbours of {nodeLabelB} 115 | [source,cypher,subs=attributes] 116 | ---- 117 | MATCH (b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} })<--(a) RETURN * LIMIT 100 118 | ---- 119 | 120 | == Find Network 121 | 122 | include::template-table.adoc[] 123 | 124 | // .Find {depth}-degree Network of {nodeLabelA} 125 | [source,cypher,subs=attributes] 126 | ---- 127 | MATCH path = (:{nodeLabelA} { {propertyKeyA}: {propertyValueA} })-[:{relationshipType}*1..3]-() RETURN path LIMIT 50 128 | ---- 129 | 130 | // .Find {depth}-degree Network of {nodeLabelB} 131 | [source,cypher,subs=attributes] 132 | ---- 133 | MATCH path = (:{nodeLabelB} { {propertyKeyB}: {propertyValueB} })-[*1..3]-() RETURN path LIMIT 50; 134 | ---- 135 | 136 | == Find Network 137 | 138 | include::template-table.adoc[] 139 | 140 | // .Find Shortest Path between {nodeLabelA} and {nodeLabelB} 141 | [source,cypher,subs=attributes] 142 | ---- 143 | MATCH path = shortestPath((a:{nodeLabelA} { {propertyKeyA}: {propertyValueA} })-[*]-(b:{nodeLabelB} { {propertyKeyB}: {propertyValueB} })) RETURN path LIMIT 50; 144 | ---- 145 | 146 | == Create Unique Constraints 147 | 148 | include::template-table.adoc[] 149 | 150 | // .Create Constraint for {nodeLabelA} 151 | [source,cypher,subs=attributes] 152 | ---- 153 | CREATE CONSTRAINT ON (a:{nodeLabelA}) ASSERT a.{propertyKeyA} IS UNIQUE; 154 | ---- 155 | 156 | // .Create Constraint for {nodeLabelB} 157 | [source,cypher,subs=attributes] 158 | ---- 159 | CREATE CONSTRAINT ON (b:{nodeLabelB}) ASSERT b.{propertyKeyB} IS UNIQUE; 160 | ---- 161 | 162 | == Create Indexes 163 | 164 | include::template-table.adoc[] 165 | 166 | // .Create Index for {nodeLabelA} 167 | [source,cypher,subs=attributes] 168 | ---- 169 | CREATE INDEX ON :{nodeLabelA}({propertyKeyA}); 170 | ---- 171 | 172 | // .Create Index for {nodeLabelB} 173 | [source,cypher,subs=attributes] 174 | ---- 175 | CREATE INDEX ON :{nodeLabelB}({propertyKeyB}); 176 | ---- 177 | 178 | == Create Existence Constraints 179 | 180 | include::template-table.adoc[] 181 | 182 | // .Create Existence Constraint for {nodeLabelA} 183 | [source,cypher,subs=attributes] 184 | ---- 185 | CREATE CONSTRAINT ON (a:{nodeLabelA}) ASSERT exists(a.{propertyKeyA}); 186 | ---- 187 | 188 | // .Create Existence Constraint for {nodeLabelB} 189 | [source,cypher,subs=attributes] 190 | ---- 191 | CREATE CONSTRAINT ON (b:{nodeLabelB}) ASSERT exists(b.{propertyKeyB}); 192 | ---- 193 | 194 | == More Documentation 195 | 196 | * http://neo4j.com/docs/developer-manual/[Neo4j Developer Manual^] 197 | * http://neo4j.com/developer/get-started/[Neo4j Developer Guides^] 198 | * https://neo4j.com/docs/cypher-refcard/[Cypher Reference Card^] 199 | 200 | -------------------------------------------------------------------------------- /adoc/restaurant_recommendation.adoc: -------------------------------------------------------------------------------- 1 | == Restaurant Recommendations 2 | :author: Neo Technology 3 | :twitter: neo4j 4 | :tags: Recommendation, Graph Based Search 5 | :neo4j-version: 3.0 6 | 7 | === Introduction 8 | 9 | image::https://guides.neo4j.com/sushi_restaurants_nyc.svg[height=300,float=right] 10 | 11 | We want to demonstrate how easy it is to model a domain as a graph and answer questions in almost natural language. 12 | 13 | Graph Based Search and Discovery is prominent a use-case for graph databases like http://neo4j.com[Neo4j]. 14 | 15 | Here we use a Domain of restaurants which serve cuisines and are located in a City. 16 | 17 | The domain diagram was created with the http://www.apcjones.com/arrows/#[Arrows tool] 18 | 19 | //// 20 | 52 | //// 53 | 54 | === Setup: Creating Friends, Restaurants in Cities and their Cusines 55 | 56 | //setup 57 | [source,cypher] 58 | ---- 59 | CREATE (philip:Person {name:"Philip"})-[:IS_FRIEND_OF]->(emil:Person {name:"Emil"}), 60 | (philip)-[:IS_FRIEND_OF]->(michael:Person {name:"Michael"}), 61 | (philip)-[:IS_FRIEND_OF]->(andreas:Person {name:"Andreas"}) 62 | CREATE (sushi:Cuisine {name:"Sushi"}), (nyc:City {name:"New York"}), 63 | (iSushi:Restaurant {name:"iSushi"})-[:SERVES]->(sushi),(iSushi)-[:LOCATED_IN]->(nyc), 64 | (michael)-[:LIKES]->(iSushi), 65 | (andreas)-[:LIKES]->(iSushi), 66 | (zam:Restaurant {name:"Zushi Zam"})-[:SERVES]->(sushi),(zam)-[:LOCATED_IN]->(nyc), 67 | (andreas)-[:LIKES]->(zam) 68 | ---- 69 | 70 | //graph 71 | 72 | === Philips Friends 73 | 74 | [source,cypher] 75 | ---- 76 | MATCH (philip:Person {name:"Philip"})-[:IS_FRIEND_OF]-(person) 77 | RETURn person.name 78 | ---- 79 | 80 | //table 81 | 82 | === Restaurants in NYC and their cusines 83 | 84 | [source,cypher] 85 | ---- 86 | MATCH (nyc:City {name:"New York"})<-[:LOCATED_IN]-(restaurant)-[:SERVES]->(cusine) 87 | RETURN nyc, restaurant, cusine 88 | ---- 89 | 90 | //table 91 | 92 | //graph_result 93 | 94 | === Graph Search Recommendation 95 | 96 | image::https://guides.neo4j.com/sushi_restaurants_nyc.png[height=300,float=right] 97 | 98 | We want to answer the following question 99 | 100 | "" 101 | Find Sushi Restaurants in New York that my friends like. 102 | "" 103 | 104 | To satisfy this question, we have to know who's asking: _Philip_ and he's asking for 4 connected facts 105 | 106 | * _People_ that are friends of _Philip_ 107 | * _Restaurants_ located in _New York_ 108 | * _Restaurants_ that server _Sushi_ 109 | * _Restaurants_ that his _Friends_ like 110 | 111 | [source,cypher] 112 | ---- 113 | MATCH (philip:Person {name:"Philip"}), 114 | (philip)-[:IS_FRIEND_OF]-(friend), 115 | (restaurant:Restaurant)-[:LOCATED_IN]->(:City {name:"New York"}), 116 | (restaurant)-[:SERVES]->(:Cuisine {name:"Sushi"}), 117 | (friend)-[:LIKES]->(restaurant) 118 | RETURN restaurant.name, collect(friend.name) AS likers, count(*) AS occurence 119 | ORDER BY occurence DESC 120 | ---- 121 | 122 | //table -------------------------------------------------------------------------------- /adoc/template-table.adoc: -------------------------------------------------------------------------------- 1 | [cols="7", opts="headers"] 2 | |=== 3 | 4 | | Label 5 | | Property 6 | | Value 7 | | Relationship 8 | | Label 9 | | Property 10 | | Value 11 | 12 | | pass:a[] 13 | | pass:a[] 14 | | pass:a[] 15 | 16 | | pass:a[] 17 | 18 | | pass:a[] 19 | | pass:a[] 20 | | pass:a[] 21 | 22 | |=== 23 | -------------------------------------------------------------------------------- /docs/adoc-guides.adoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-contrib/neo4j-guides/7887c8ef95010f138c9f86577626d27e7ec197cb/docs/adoc-guides.adoc -------------------------------------------------------------------------------- /docs/html-guides.adoc: -------------------------------------------------------------------------------- 1 | = Creating remote browser guides in HTML 2 | == Setting up a remote server 3 | The remote server serving these guides must be open to handle requests from all origins. This is done by setting it's `Access-Control-Allow-Origin` header to allow `*`. Read more here: http://enable-cors.org/server.html 4 | 5 | == Configure Neo4j 6 | Enterprise edition of Neo4j can have configurated whitelist of allowed hostnames to fetch guides from. Community edition have a static whitelist. 7 | The whitelist is in _neo4j-server.properties_. Set to `*` to allow from all hosts. 8 | 9 | == Create a guide 10 | Howto create a guide. 11 | 12 | === Basic HTML structure 13 | A browser guide is a partial HTML document that sould be encapsulated in `
` tags. 14 | 15 | A guide usually have multiple slides/pages by using a carousel: 16 | 17 | [source,html] 18 | ---- 19 | 20 | Slide 1 21 | Slide 2 22 | 23 | ---- 24 | 25 | The Bootstrap CSS classes can be used, see http://getbootstrap.com/css/ for info on what's available. 26 | 27 | A slide is usually split into two columns with title and lead to the left and content to the right. 28 | To get this structure, use this code: 29 | 30 | [source,html] 31 | ---- 32 |
33 |

Title

34 |

Informative text

35 |
36 |
37 | content... 38 |
39 | ---- 40 | 41 | All HTML is allowed so make sure the guides structure is valid and complete so other parts of the browser don't break. 42 | All ` 3 | 4 | 5 |
6 |

REMOTE Cypher

7 |

Neo4j's graph query language

8 |
9 |
10 |

Neo4j's Cypher language is purpose built for working with graph data.

11 |
    12 |
  • uses patterns to describe graph data
  • 13 |
  • familiar SQL-like clauses
  • 14 |
  • declarative, describing what to find, not how to find it
  • 15 |
  • declarative, describing what to find, not how to find it 2
  • 16 |
  • declarative, describing what to find, not how to find it 2
  • 17 |
18 |
19 |
20 | 21 |
22 |

CREATE

23 |

Create a node

24 |
25 |
26 |

Let's use Cypher to generate a small social graph.

27 |
28 |
CREATE (ee:Person { name: "Emil", from: "Sweden", klout: 99 })
29 |
30 |
    31 |
  • CREATE clause to create data
  • 32 |
  • () parenthesis to indicate a node
  • 33 |
  • ee:Person a variable 'ee' and label 'Person' for the new node
  • 34 |
  • {} brackets to add properties to the node
  • 35 |
36 |
37 |
38 | 39 |
40 |

MATCH

41 |

Finding nodes

42 |
43 |
44 |

Now find the node representing Emil:

45 |
46 |
MATCH (ee:Person) WHERE ee.name = "Emil" RETURN ee;
47 |
48 |
    49 |
  • MATCH clause to specify a pattern of nodes and relationships
  • 50 |
  • (ee:Person) a single node pattern with label 'Person' which will assign matches to the variable 'ee'
  • 51 |
  • WHERE clause to constrain the results
  • 52 |
  • ee.name = "Emil" compares name property to the value "Emil"
  • 53 |
  • RETURN clause used to request particular results
  • 54 |
55 |
56 |
57 | 58 |
59 |

CREATE more

60 |

Nodes and relationships

61 |
62 |
63 |

CREATEclauses can create many nodes and relationships at once.

64 |
65 |
MATCH (ee:Person) WHERE ee.name = "Emil"
 66 | CREATE (js:Person { name: "Johan", from: "Sweden", learn: "surfing" }),
 67 | (ir:Person { name: "Ian", from: "England", title: "author" }),
 68 | (rvb:Person { name: "Rik", from: "Belgium", pet: "Orval" }),
 69 | (ally:Person { name: "Allison", from: "California", hobby: "surfing" }),
 70 | (ee)-[:KNOWS {since: 2001}]->(js),(ee)-[:KNOWS {rating: 5}]->(ir),
 71 | (js)-[:KNOWS]->(ir),(js)-[:KNOWS]->(rvb),
 72 | (ir)-[:KNOWS]->(js),(ir)-[:KNOWS]->(ally),
 73 | (rvb)-[:KNOWS]->(ally)
74 |
75 |
76 |
77 | 78 |
79 |

Pattern matching

80 |

Describe what to find in the graph

81 |
82 |
83 |

For instance, a pattern can be used to find Emil's friends:

84 |
85 |
MATCH (ee:Person)-[:KNOWS]-(friends)
 86 | WHERE ee.name = "Emil" RETURN ee, friends
87 |
88 |
    89 |
  • MATCHclause to describe the pattern from known Nodes to found Nodes
  • 90 |
  • (ee)starts the pattern with a Person (qualified by WHERE)
  • 91 |
  • -[:KNOWS]-matches "KNOWS" relationships (in either direction)
  • 92 |
  • (friends)will be bound to Emil's friends
  • 93 |
94 |
95 |
96 | 97 |
98 |

Recommend

99 |

Using patterns

100 |
101 |
102 |

103 | Pattern matching can be used to make recommendations. Johan is learning to surf, so he may want to find 104 | a new friend who already does: 105 |

106 |
107 |
MATCH (js:Person)-[:KNOWS]-()-[:KNOWS]-(surfer)
108 | WHERE js.name = "Johan" AND surfer.hobby = "surfing"
109 | RETURN DISTINCT surfer
110 |
111 |
    112 |
  • ()empty parenthesis to ignore these nodes
  • 113 |
  • DISTINCTbecause more than one path will match the pattern
  • 114 |
  • surferwill contain Allison, a friend of a friend who surfs
  • 115 |
116 |
117 |
118 | 119 |
120 |

Next steps

121 |

122 | Start your application using Cypher to create and query graph data. Use the REST API 123 | to monitor the database. In special cases, consider a plugin. 124 |

125 |
126 |
127 |

Keep getting started

128 | 134 |
135 |
136 |

Jump into code

137 | 140 |
141 |
142 |
143 | -------------------------------------------------------------------------------- /html/browser.html: -------------------------------------------------------------------------------- 1 | 24 |
30 | 31 | 49 | 50 | 51 | 52 | 53 |
54 |

Get Started

55 |
56 |
57 |
58 |

Start by typing :play intro into the command-line and pressing Enter. 59 | Please follow the guide to learn about our user interface.

60 |
61 |
62 |
63 |
64 | 65 | 66 | 67 | 68 |
69 |

Feedback & Questions

70 |
71 |
72 |
73 |

If you have feedback or questions on how to use the Neo4j Browser, there is a small messaging system in the configuration (cog) drawer. 74 | You can provide your name and send us a message.

75 |
76 |
77 |
78 |
79 | 80 | 81 | 82 | 83 |
84 |

Useful Commands and Keyboard Shortcuts

85 |
86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
ShortcutPurpose

:help

Help System

:help commands

Useful Commands

:clear

Clear Frames

:style [reset]

Styling Popup & Reset

:help keys

Keyboard Help

Ctrl+Enter or Cmd+Enter

Execute Statement

Ctrl+Up or Cmd+Up

Previous Statement

Shift+Enter

Enter Multiline Mode

/

Move Focus to Editor

ESC

Toggle Editor to Full Screen

141 |
142 |
143 |
144 | 145 | 146 | 147 | 148 |
149 |

Built in Guides

150 |
151 |
152 |
153 |

If you want to learn more first, click on any of the helpful links shown after startup: these are quick guides that introduce the different concepts. 154 | You find more helpful links in the left sidebar in the "Information" tab, with the (i).

155 |
156 |
157 |
    158 |
  • 159 |

    Intro - a guided tour of Neo4j browser :play intro

    160 |
  • 161 |
  • 162 |

    Concepts - GraphDB 101 :play concepts

    163 |
  • 164 |
  • 165 |

    Cypher - query language :play cypher

    166 |
  • 167 |
  • 168 |

    The Movie Graph - a mini graph model with use-cases :play movie graph

    169 |
  • 170 |
  • 171 |

    The Northwind Database - the classic demo database with import instructions & use-case queries :play northwind graph

    172 |
  • 173 |
  • 174 |

    Custom Guides - starting with Neo4j 2.3 you can use :play <url> to play a custom guide, e.g. :play http://guides.neo4j.com/intro/create.html from the fundamentals training.

    175 |
  • 176 |
177 |
178 |
179 |

Import our sample movie graph by entering :play movie graph.

180 |
181 |
182 |

On the second slide click the large Cypher CREATE ... statement, then hit the Run button.

183 |
184 |
185 |

After a few seconds the data is imported, and you’ll see a subset of the movie data rendered as a graph.

186 |
187 |
188 |
189 |
190 | 191 | 192 | 193 | 194 |
195 |

Styling Neo4j Browser Visualization

196 |
197 |
198 |
199 |

You can pan the visual view around and drag nodes to rearrange them.

200 |
201 |
202 |

The nodes already have a sensible captions, it auto-selects a property from the property list to be rendered as caption.

203 |
204 |
205 |

If you click on any node or relationship you see the properties of that element below the visualization, larger property sets might be folded in, there is a little triangle on the right to fold them out.

206 |
207 |
208 |

E.g. you click on one of the Movies then you can see it’s properties below the graph. 209 | Same for actors or the ACTED_IN relationships.

210 |
211 |
212 |

If you click on any label or relationship above the graph visualization, you can then chose its styling in the area below the graph.

213 |
214 |
215 |

Colors, sizes and captions are selectable from there.

216 |
217 |
218 |

For instance click on the (Movies) label above the graph and change the color, size and captions of nodes labeled with Movie.

219 |
220 |
221 |
222 | style node 223 |
224 |
225 |
226 |
227 | style relationship 228 |
229 |
230 |
231 |
232 |
233 | 234 | 235 | 236 | 237 |
238 |

Running Queries

239 |
240 |
241 |
242 |

When you continue with the guide, you will see more queries. 243 | You can get them into the editor by clicking on them. 244 | To execute, hit the triangular play button.

245 |
246 |
247 |

Query results are rendered either as visual graph or tabular result. 248 | You can switch between those with the icons on the left side of the result frame.

249 |
250 |
251 |

Remove all accumulated output frames with :clear, the cross removes a single frame and aborts a (long-)running statement.

252 |
253 |
254 |

You can click the query above the graph visualisation to pull it back into the editor.

255 |
256 |
257 |

Use the keyboard shortcuts listed above to work efficiently with the editor area.

258 |
259 |
260 |

Navigate input history with Ctrl+Up and Ctrl+Down, access all of it via :history. The history will be persisted across browser restarts.

261 |
262 |
263 |

You can switch between tabular, visual mode, query plan and x-ray mode for results with the icons on the left of each panel,

264 |
265 |
266 | 267 | 268 | 271 | 274 | 275 |
269 |
Note
270 |
272 | Don’t worry if you don’t see any output, you might just be in visual mode but returned tabular/scalar data, just switch the mode to tabular 273 |
276 |
277 |
278 |

Query time is reported in the tabular view, don’t rely on that exact timing though it includes the latency and (de-)serialization costs, not just the actual query execution time.

279 |
280 |
281 |

You can download the results as CSV from the tabular output panel (top right download icon), and as JSON (download icon above the panel). 282 | The graph visualization can be exported as PNG and SVG.

283 |
284 |
285 |
286 |
287 | 288 | 289 | 290 | 291 |
292 |

Meta Graph

293 |
294 |
295 |
296 |

In the left side drawer on the three bubbles section (that resemble the Neo4j logo) you find the currently used node-labels and relationship types. 297 | Clicking on any of those runs a quick query to show you a sample of the graph using those.

298 |
299 |
300 |
301 |
302 | 303 | 304 | 305 | 306 |
307 |

Queries and Favorites

308 |
309 |
310 |
311 |

If you start with an empty frame, display some nodes and relationships, use the Favorites (Star) drawer on the left, click on the Get Some Data entry, and run the query. 312 | This executes the statement MATCH (n) RETURN n limit 100 which fetches some nodes.

313 |
314 |
315 |

The browser helpfully also fetches and displays relationships between those nodes, even if they were not part of your query result. 316 | You can disable the latter behavior with the "Auto-Complete" switch in the bottom left corner. 317 | Then only relationships returned by the actual query will be shown.

318 |
319 |
320 |

You can save your own queries as favorites by "starring" them. 321 | Use a comment // comment above your query for a title. 322 | Use folders to organize the favorites you can rearrange them by dragging and delete if they are no longer useful.

323 |
324 |
325 | 326 | 327 | 330 | 333 | 334 |
328 |
Note
329 |
331 | Favorites are stored in your local browser storage, so they are only available per Browser and URL. 332 |
335 |
336 | 337 | 338 | 339 |
340 |

For more advanced styling you can bring up the style-viewer with :style, download the graph-style-sheet (GRASS), edit it offline and drag it back onto the drag-area of the viewer.

341 |
342 |
343 | 344 | 345 | 348 | 352 | 353 |
346 |
Note
347 |
349 | You can reset to the default styles with :style reset. 350 | Alternatively by clicking the "fire extinguisher" icon in the popup from :style. 351 |
354 |
355 |
356 |

Within the GRASS file you can change colors, fonts, sizes, outlines and titles per node-label and relationship-type. 357 | It is also possible to combine multiple properties into a caption with caption: '{name}, born in {born}';

358 |
359 |
360 |
361 | style sheet grass 362 |
363 |
364 |
365 |
366 |
367 | 368 | 369 | 370 | 371 |
372 |

Configuration

373 |
374 |
375 |
376 |
    377 |
  • 378 |

    since Neo4j 2.3 there is a config drawer on the left (with the cog), no need for the :config command anymore

    379 |
  • 380 |
  • 381 |

    you can retrieve the current configuration with :config

    382 |
  • 383 |
  • 384 |

    the individual settings are configured with:

    385 |
    386 |
      387 |
    • 388 |

      :config maxNeighbours:100 - maxiumum number of neighbours for a node

      389 |
    • 390 |
    • 391 |

      :config maxRows:100 - maximum number of rows for the tabular result

      392 |
    • 393 |
    394 |
    395 |
  • 396 |
397 |
398 |
399 |
400 |
401 | 402 | 403 | 404 | 405 |
406 |

Executing REST requests

407 |
408 |
409 |
410 |

You can also execute REST requests with the Neo4j Browser, the command-syntax is
411 | :COMMAND /a/path {"some":"data"}. 412 | The available commands are :GET, :POST, :PUT and :DELETE.

413 |
414 |
415 |

A simple query would inspect the available endpoints of the database :GET /db/data/, the results are listed as formatted JSON. 416 | Then you can for instance retrieve all labels in the database with :GET /db/data/labels.

417 |
418 |
419 |

To execute a Cypher statement you post to the transaction Cypher endpoint like this:

420 |
421 |
422 |
423 |
:POST /db/data/transaction/commit {"statements":[
424 |      {"statement":"MATCH (m:Movie)  WHERE m.title={title} RETURN m.title, m.released, labels(m)",
425 |       "parameters":{"title":"Cloud Atlas"}}]}
426 |
427 |
428 |
429 |
430 |
431 |
432 |
-------------------------------------------------------------------------------- /html/restaurant_recommendation.html: -------------------------------------------------------------------------------- 1 | 23 | 26 |
27 | 28 | 46 | 47 | 48 | 49 |

Restaurant Recommendations

50 | 51 | 52 | 53 | 54 |
55 |

Introduction

56 |
57 |
58 |
59 |
60 | sushi restaurants nyc 61 |
62 |
63 |
64 |

We want to demonstrate how easy it is to model a domain as a graph and answer questions in almost natural language.

65 |
66 |
67 |

Graph Based Search and Discovery is prominent a use-case for graph databases like Neo4j.

68 |
69 |
70 |

Here we use a Domain of restaurants which serve cuisines and are located in a City.

71 |
72 |
73 |

The domain diagram was created with the Arrows tool

74 |
75 |
76 |
77 |
78 | 79 | 80 | 81 | 82 |
83 |

Setup: Creating Friends, Restaurants in Cities and their Cusines

84 |
85 |
86 |
87 |
88 |
CREATE (philip:Person {name:"Philip"})-[:IS_FRIEND_OF]->(emil:Person {name:"Emil"}),
 89 |        (philip)-[:IS_FRIEND_OF]->(michael:Person {name:"Michael"}),
 90 |        (philip)-[:IS_FRIEND_OF]->(andreas:Person {name:"Andreas"})
 91 | CREATE (sushi:Cuisine {name:"Sushi"}), (nyc:City {name:"New York"}),
 92 |        (iSushi:Restaurant {name:"iSushi"})-[:SERVES]->(sushi),(iSushi)-[:LOCATED_IN]->(nyc),
 93 |        (michael)-[:LIKES]->(iSushi),
 94 |        (andreas)-[:LIKES]->(iSushi),
 95 |        (zam:Restaurant {name:"Zushi Zam"})-[:SERVES]->(sushi),(zam)-[:LOCATED_IN]->(nyc),
 96 |        (andreas)-[:LIKES]->(zam)
97 |
98 |
99 |
100 |
101 |
102 | 103 | 104 | 105 | 106 |
107 |

Philips Friends

108 |
109 |
110 |
111 |
112 |
MATCH (philip:Person {name:"Philip"})-[:IS_FRIEND_OF]-(person)
113 | RETURn person.name
114 |
115 |
116 |
117 |
118 |
119 | 120 | 121 | 122 | 123 |
124 |

Restaurants in NYC and their cusines

125 |
126 |
127 |
128 |
129 |
MATCH (nyc:City {name:"New York"})<-[:LOCATED_IN]-(restaurant)-[:SERVES]->(cusine)
130 | RETURN nyc, restaurant, cusine
131 |
132 |
133 |
134 |
135 |
136 | 137 | 138 | 139 | 140 |
141 |

Graph Search Recommendation

142 |
143 |
144 |
145 |
146 | sushi restaurants nyc 147 |
148 |
149 |
150 |

We want to answer the following question

151 |
152 |
153 |
154 |
155 |

Find Sushi Restaurants in New York that my friends like.

156 |
157 |
158 |
159 |
160 |

To satisfy this question, we have to know who’s asking: Philip and he’s asking for 4 connected facts

161 |
162 |
163 |
    164 |
  • 165 | People that are friends of Philip 166 |
  • 167 |
  • 168 | Restaurants located in New York 169 |
  • 170 |
  • 171 | Restaurants that server Sushi 172 |
  • 173 |
  • 174 | Restaurants that his Friends like 175 |
  • 176 |
177 |
178 |
179 |
180 |
MATCH (philip:Person {name:"Philip"}),
181 |       (philip)-[:IS_FRIEND_OF]-(friend),
182 |       (restaurant:Restaurant)-[:LOCATED_IN]->(:City {name:"New York"}),
183 |       (restaurant)-[:SERVES]->(:Cuisine {name:"Sushi"}),
184 |       (friend)-[:LIKES]->(restaurant)
185 | RETURN restaurant.name, collect(friend.name) AS likers, count(*) AS occurence
186 | ORDER BY occurence DESC
187 |
188 |
189 |
190 |
191 |
192 |
193 |
-------------------------------------------------------------------------------- /html/restaurant_recommendation.txt.html: -------------------------------------------------------------------------------- 1 | 24 |
30 | 31 | 49 | 50 | 51 | 52 | 53 |
54 |

Restaurant Recommendations

55 |
56 |
57 | 58 | 59 | 60 |
61 |
62 | sushi restaurants nyc 63 |
64 |
65 |
66 |

We want to demonstrate how easy it is to model a domain as a graph and answer questions in almost natural language.

67 |
68 |
69 |

Graph Based Search and Discovery is prominent a use-case for graph databases like Neo4j.

70 |
71 |
72 |

Here we use a Domain of restaurants which serve cuisines and are located in a City.

73 |
74 |
75 |

The domain diagram was created with the Arrows tool

76 |
77 | 78 | 79 | 80 |
81 |
82 |
CREATE (philip:Person {name:"Philip"})-[:IS_FRIEND_OF]->(emil:Person {name:"Emil"}),
 83 |        (philip)-[:IS_FRIEND_OF]->(michael:Person {name:"Michael"}),
 84 |        (philip)-[:IS_FRIEND_OF]->(andreas:Person {name:"Andreas"})
 85 | create (sushi:Cuisine {name:"Sushi"}), (nyc:City {name:"New York"}),
 86 |        (iSushi:Restaurant {name:"iSushi"})-[:SERVES]->(sushi),(iSushi)-[:LOCATED_IN]->(nyc),
 87 |        (michael)-[:LIKES]->(iSushi),
 88 |        (andreas)-[:LIKES]->(iSushi),
 89 |        (zam:Restaurant {name:"Zushi Zam"})-[:SERVES]->(sushi),(zam)-[:LOCATED_IN]->(nyc),
 90 |        (andreas)-[:LIKES]->(zam)
91 |
92 |
93 | 94 | 95 | 96 |
97 |
98 |
MATCH (philip:Person {name:"Philip"})-[:IS_FRIEND_OF]-(person)
 99 | RETURn person.name
100 |
101 |
102 | 103 | 104 | 105 |
106 |
107 |
MATCH (nyc:City {name:"New York"})<-[:LOCATED_IN]-(restaurant)-[:SERVES]->(cusine)
108 | RETURN nyc, restaurant, cusine
109 |
110 |
111 | 112 | 113 | 114 |
115 |
116 | sushi restaurants nyc 117 |
118 |
119 |
120 |

We want to answer the following question

121 |
122 |
123 |
124 |
125 |

Find Sushi Restaurants in New York that my friends like.

126 |
127 |
128 |
129 |
130 |

To satisfy this question, we have to know who’s asking: Philip and he’s asking for 4 connected facts

131 |
132 |
133 |
    134 |
  • 135 |

    People that are friends of Philip

    136 |
  • 137 |
  • 138 |

    Restaurants located in New York

    139 |
  • 140 |
  • 141 |

    Restaurants that server Sushi

    142 |
  • 143 |
  • 144 |

    Restaurants that his Friends like

    145 |
  • 146 |
147 |
148 |
149 |
150 |
MATCH (philip:Person {name:"Philip"}),
151 |       (philip)-[:IS_FRIEND_OF]-(friend),
152 |       (restaurant:Restaurant)-[:LOCATED_IN]->(:City {name:"New York"}),
153 |       (restaurant)-[:SERVES]->(:Cuisine {name:"Sushi"}),
154 |       (friend)-[:LIKES]->(restaurant)
155 | RETURN restaurant.name, collect(friend.name) as likers, count(*) as occurence
156 | ORDER BY occurence DESC
157 |
158 |
159 |
160 |
161 |
162 |
163 |
-------------------------------------------------------------------------------- /http-server.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import os 3 | try: 4 | from http.server import HTTPServer, SimpleHTTPRequestHandler 5 | except ImportError: 6 | from BaseHTTPServer import HTTPServer 7 | from SimpleHTTPServer import SimpleHTTPRequestHandler 8 | 9 | class CORSRequestHandler(SimpleHTTPRequestHandler): 10 | 11 | def end_headers (self): 12 | self.send_header('Access-Control-Allow-Origin', '*') 13 | self.send_header('Access-Control-Allow-Methods', 'GET') 14 | self.send_header('Access-Control-Allow-Headers', '*') 15 | self.send_header('Access-Control-Allow-Headers', 'Pragma,Cache-Control,If-Modified-Since,Content-Type,X-Requested-With,X-stream,X-Ajax-Browser-Auth') 16 | SimpleHTTPRequestHandler.end_headers(self) 17 | 18 | def do_OPTIONS(self): 19 | self.send_response(200) 20 | self.end_headers() 21 | 22 | if __name__ == '__main__': 23 | HTTPServer(('0.0.0.0', 8001), CORSRequestHandler).serve_forever() 24 | -------------------------------------------------------------------------------- /http.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'sinatra' 3 | 4 | before do 5 | response['Access-Control-Allow-Headers'] = '*, pragma, cache-control, x-stream, accept, If-Modified-Since, X-Ajax-Browser-Auth' 6 | response['Access-Control-Allow-Methods'] = 'GET, OPTIONS' 7 | response['Access-Control-Allow-Origin'] = request.env['HTTP_ORIGIN'] 8 | end 9 | 10 | options '*' do 11 | 200 12 | end 13 | 14 | get '/:file' do |file| 15 | send_file File.join("html", file) 16 | end -------------------------------------------------------------------------------- /http.sh: -------------------------------------------------------------------------------- 1 | PORT=${1-8001} 2 | gem install --no-ri --no-rdoc sinatra 3 | ruby http.rb -p $PORT -------------------------------------------------------------------------------- /img/sushi_restaurants_nyc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-contrib/neo4j-guides/7887c8ef95010f138c9f86577626d27e7ec197cb/img/sushi_restaurants_nyc.png -------------------------------------------------------------------------------- /img/sushi_restaurants_nyc.svg: -------------------------------------------------------------------------------- 1 | SERVESIS_LOCATED_INLIKESLIKESIS_FRIEND_OFRestaurantCityCusinePersonPerson -------------------------------------------------------------------------------- /readme.adoc: -------------------------------------------------------------------------------- 1 | == Converting Asciidoc Files to Neo4j Browser Guides 2 | 3 | This program uses AsciiDoctor erb templates to generate an outline html and a slide for each level two header. 4 | 5 | === Create HTML 6 | 7 | ---- 8 | ./run.sh path/to/a_guide.adoc [guide.html] 9 | 10 | -> html/a_guide.html 11 | ---- 12 | 13 | Full options: 14 | 15 | [source,shell] 16 | ---- 17 | ./run.sh /path/to/file.adoc [/path/to/file.html] [header-level-offset] [base-url] [additional asciidoc options] 18 | 19 | # for example for the recommendations sandbox-guide 20 | ./run.sh ../recommendations/documentation/recommendations.adoc \ 21 | ../recommendations/documentation/recommendations.neo4j-browser-guide \ 22 | +1 https://guides.neo4j.com/sandbox/recommendations -a neo4j-version=5.4 -a env-guide 23 | ---- 24 | 25 | === Start Local Server 26 | 27 | It is patched to allow CORS headers for the browser to serve the HTML directory. 28 | 29 | ---- 30 | python http-server.py & 31 | ---- 32 | 33 | === Configuration 34 | 35 | * before Neo4j 3.0 add `dbms.browser.remote_content_hostname_whitelist=*` to `conf/neo4j-server.properties` 36 | * from Neo4j 3.0 add `browser.remote_content_hostname_whitelist=*` to `conf/neo4j.conf` 37 | 38 | and restart. 39 | You can also add the specific `localhost:8001` address. 40 | 41 | For `neo4j-community` you have to use `sudo` and run the http script on port 80. 42 | For example, use `BaseHTTPServer.HTTPServer(("0.0.0.0", 80),CORSRequestHandler).serve_forever()` in `http.py` 43 | 44 | To view the guides in the Neo4j Browser, run: 45 | 46 | ---- 47 | :play http://localhost:8001/html/a_guide.html 48 | ---- 49 | 50 | You can find more details on how to create guides and their format in link:docs/remote-guides.md[] and an example in link:docs/remote.html[] 51 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | asciidoctor -v || gem install asciidoctor 3 | 4 | echo "Usage ./run.sh OR ./run.sh path/to/a_guide.adoc [guide.html] [+1 (header-offset)] [http://guide-host:port]" 5 | # mkdir -p html 6 | DIR=${0%%/run.sh} 7 | 8 | function render { 9 | ADOC=${1-../exercise/lab_01.adoc} 10 | shift 11 | HTML=${ADOC%.*}.html 12 | HTML=html/${HTML##*/} 13 | HTML=${1-$HTML} 14 | shift 15 | OFFSET=${1-+1} 16 | shift 17 | BASE_URL=${1-http://guides.neo4j.com/intro} 18 | shift 19 | echo rendering $ADOC to $HTML 20 | echo asciidoctor $ADOC -T $DIR/templates -a allow-uri-read -a experimental -a guides=$BASE_URL -a current=$BASE_URL -a img=$BASE_URL/img -a leveloffset=${OFFSET} -a env-guide= -a guide= -o ${HTML} "$@" 21 | asciidoctor $ADOC -T $DIR/templates -a allow-uri-read -a guides=$BASE_URL -a icons=font -a current=$BASE_URL -a img=$BASE_URL/img -a leveloffset=${OFFSET} -a env-guide= -a guide= -o ${HTML} "$@" 22 | } 23 | 24 | if [ $# == 0 ]; then 25 | render ../exercise/lab_01.adoc 26 | render ../exercise/lab_02.adoc 27 | render ../exercise/lab_03.adoc 28 | render ../04_create_data_interactive.adoc html/create.html 29 | render ../05_cypher_deep_dive_starter.adoc html/starter.html 30 | render ../05_cypher_deep_dive_expert.adoc html/expert.html 31 | render ../05_cypher_deep_dive_advanced.adoc html/advanced.html 32 | else 33 | render "$@" 34 | fi 35 | 36 | # s3cmd put -P --recursive html/* s3://guides.neo4j.com/intro/ 37 | -------------------------------------------------------------------------------- /templates/block_admonition.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['admonitionblock',(attr :name),role].compact * ' ' %>"> 2 | 3 | 4 | 13 | 19 | 20 |
<% 5 | if @document.attr? :icons, 'font' %> 6 | <% 7 | elsif @document.attr? :icons %> 8 | <%= @caption %><% 9 | else %> 10 |
<%= @caption %>
<% 11 | end %> 12 |
<% 14 | if title? %> 15 |
<%= title %>
<% 16 | end %> 17 | <%= content %> 18 |
21 | 22 | -------------------------------------------------------------------------------- /templates/block_audio.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['audioblock',@style,role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= captioned_title %>
<% 4 | end %> 5 |
6 | 12 |
13 | 14 | -------------------------------------------------------------------------------- /templates/block_colist.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['colist',@style,role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= title %>
<% 4 | end 5 | if @document.attr? :icons 6 | font_icons = @document.attr? :icons, 'font' %> 7 | <% 8 | items.each_with_index do |item, i| 9 | num = i + 1 %> 10 | 11 | 15 | 16 | <% 17 | end %> 18 |
<% 12 | if font_icons %><%= num %><% 13 | else %><%= num %><% 14 | end %><%= item.text %>
<% 19 | else %> 20 |
    <% 21 | items.each do |item| %> 22 |
  1. 23 |

    <%= item.text %>

    24 |
  2. <% 25 | end %> 26 |
<% 27 | end %> 28 | 29 | -------------------------------------------------------------------------------- /templates/block_dlist.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | case @style 3 | when 'qanda' 4 | %> class="<%= ['qlist','qanda',role].compact * ' ' %>"><% 5 | if title? %> 6 |
<%= title %>
<% 7 | end %> 8 |
    <% 9 | items.each do |questions, answer| %> 10 |
  1. <% 11 | [*questions].each do |question| %> 12 |

    <%= question.text %>

    <% 13 | end 14 | unless answer.nil? 15 | if answer.text? %> 16 |

    <%= answer.text %>

    <% 17 | end 18 | if answer.blocks? %> 19 | <%= answer.content %><% 20 | end 21 | end %> 22 |
  2. <% 23 | end %> 24 |
25 | <% 26 | when 'horizontal' 27 | %> class="<%= ['hdlist',role].compact * ' ' %>"><% 28 | if title? %> 29 |
<%= title %>
<% 30 | end %> 31 | <% 32 | if (attr? :labelwidth) || (attr? :itemwidth) %> 33 | 34 | > 35 | > 36 | <% 37 | end 38 | items.each do |terms, dd| %> 39 | 40 | 50 | 60 | <% 61 | end %> 62 |
<% 41 | terms = [*terms] 42 | last_term = terms.last 43 | terms.each do |dt| %> 44 | <%= dt.text %><% 45 | if dt != last_term %> 46 |
<% 47 | end 48 | end %> 49 |
<% 51 | unless dd.nil? 52 | if dd.text? %> 53 |

<%= dd.text %>

<% 54 | end 55 | if dd.blocks? %> 56 | <%= dd.content %><% 57 | end 58 | end %> 59 |
63 | <% 64 | else 65 | %> class="<%= ['dlist',@style,role].compact * ' ' %>"><% 66 | if title? %> 67 |
<%= title %>
<% 68 | end %> 69 |
<% 70 | items.each do |terms, dd| 71 | [*terms].each do |dt| %> 72 | ><%= dt.text %><% 73 | end 74 | unless dd.nil? %> 75 |
<% 76 | if dd.text? %> 77 |

<%= dd.text %>

<% 78 | end 79 | if dd.blocks? %> 80 | <%= dd.content %><% 81 | end %> 82 |
<% 83 | end 84 | end %> 85 |
86 | <% 87 | end %> 88 | -------------------------------------------------------------------------------- /templates/block_example.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['exampleblock',role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= captioned_title %>
<% 4 | end %> 5 |
6 | <%= content %> 7 |
8 | 9 | -------------------------------------------------------------------------------- /templates/block_floating_title.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><%= %(#{title}) %> 2 | -------------------------------------------------------------------------------- /templates/block_image.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['imageblock',@style,role].compact * ' ' %>"<% 2 | if (attr? :align) || (attr? :float) 3 | %> style="<%= [("text-align: #{attr :align};" if attr? :align),("float: #{attr :float};" if attr? :float)].compact * ' ' %>"<% 4 | end %>> 5 |
<% 6 | if attr? :link %> 7 | <%= attr :alt %><%= (attr? :height) ? %( height="#{attr :height}") : nil %>><% 8 | else %> 9 | <%= attr :alt %><%= (attr? :height) ? %( height="#{attr :height}") : nil %>><% 10 | end %> 11 |
<% 12 | if title? %> 13 |
<%= captioned_title %>
<% 14 | end %> 15 | 16 | -------------------------------------------------------------------------------- /templates/block_listing.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['listingblock',role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= captioned_title %>
<% 4 | end %> 5 |
<% 6 | nowrap = !(@document.attr? :prewrap) || (option? :nowrap) 7 | if @style == 'source' 8 | language = attr :language 9 | code_class = language ? [language, %(language-#{language})] : [] 10 | pre_class = ['highlight'] 11 | pre_lang = language 12 | case attr 'source-highlighter' 13 | when 'coderay' 14 | pre_class = ['CodeRay'] 15 | when 'pygments' 16 | pre_class = ['pygments','highlight'] 17 | when 'prettify' 18 | pre_class = ['prettyprint'] 19 | pre_class << 'linenums' if attr? :linenums 20 | pre_class << language if language 21 | pre_class << %(language-#{language}) if language 22 | code_class = [] 23 | when 'html-pipeline' 24 | pre_lang = language 25 | pre_class = code_class = [] 26 | nowrap = false 27 | end 28 | pre_class << "pre-scrollable" 29 | pre_class << "programlisting" 30 | pre_class << "cm-s-neo" 31 | pre_class << "code" 32 | pre_class << "runnable" 33 | pre_class << "standalone-example" 34 | pre_class << "ng-binding" 35 | pre_class << 'nowrap' if nowrap %> 36 |
<%= pre_lang && %( data-lang="#{pre_lang}") %><%= pre_lang && %( lang="#{pre_lang}") %>><%= content %>
<% 37 | else %> 38 | ><%= content %><% 39 | end %> 40 |
41 | 42 | -------------------------------------------------------------------------------- /templates/block_literal.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['literalblock',role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= title %>
<% 4 | end %> 5 |
6 | ><%= content %> 7 |
8 | 9 | -------------------------------------------------------------------------------- /templates/block_math.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['mathblock',role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= title %>
<% 4 | end %> 5 |
<% 6 | open, close = Asciidoctor::BLOCK_MATH_DELIMITERS[@style.to_sym] 7 | equation = content.strip 8 | if (@subs.nil? || @subs.empty?) && !(attr? 'subs') 9 | equation = sub_specialcharacters equation 10 | end 11 | unless (equation.start_with? open) && (equation.end_with? close) 12 | equation = %(#{open}#{equation}#{close}) 13 | end 14 | %> 15 | <%= %(#{equation}\n) %> 16 |
17 | 18 | -------------------------------------------------------------------------------- /templates/block_olist.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['olist',@style,role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= title %>
<% 4 | end %> 5 |
    <%= (keyword = list_marker_keyword) && %( type="#{keyword}") %>><% 6 | items.each do |item| %> 7 |
  1. 8 |

    <%= item.text %>

    <% 9 | if item.blocks? %> 10 | <%= item.content %><% 11 | end %> 12 |
  2. <% 13 | end %> 14 |
15 | 16 | -------------------------------------------------------------------------------- /templates/block_open.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | if @style == 'abstract' 3 | if @parent == @document && @document.doctype == 'book' 4 | puts 'asciidoctor: WARNING: abstract block cannot be used in a document without a title when doctype is book. Excluding block content.' 5 | else 6 | %> class="<%= ['quoteblock','abstract',role].compact * ' ' %>"><% 7 | if title? %> 8 |
<%= title %>
<% 9 | end %> 10 |
11 | <%= content %> 12 |
13 | <% 14 | end 15 | elsif @style == 'partintro' && (@level != 0 || @parent.context != :section || @document.doctype != 'book') 16 | puts 'asciidoctor: ERROR: partintro block can only be used when doctype is book and it\'s a child of a book part. Excluding block content.' 17 | else 18 | %> class="<%= ['openblock',(@style == 'open' ? nil : @style),role].compact * ' ' %>"><% 19 | if title? %> 20 |
<%= title %>
<% 21 | end %> 22 |
23 | <%= content %> 24 |
25 | <% 26 | end %> 27 | -------------------------------------------------------------------------------- /templates/block_page_break.html.erb: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /templates/block_paragraph.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['paragraph',role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= title %>
<% 4 | end %> 5 |

<%= content %>

6 | 7 | -------------------------------------------------------------------------------- /templates/block_pass.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><%= content %> 2 | -------------------------------------------------------------------------------- /templates/block_preamble.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%>
2 |
3 | <%= content %> 4 |
<% 5 | if (attr? :toc) && (attr? 'toc-placement', 'preamble') %> 6 |
7 |
<%= attr 'toc-title' %>
8 | <%= converter.convert @document, 'outline' %> 9 |
<% 10 | end %> 11 |
12 | -------------------------------------------------------------------------------- /templates/block_quote.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['quoteblock',role].compact * ' ' %>"><% 2 | if title? %> 3 |
<%= title %>
<% 4 | end %> 5 |
6 | <%= content %> 7 |
<% 8 | if (attr? :attribution) or (attr? :citetitle) %> 9 |
<% 10 | if attr? :citetitle %> 11 | <%= attr :citetitle %><% 12 | end 13 | if attr? :attribution 14 | if attr? :citetitle %>
<% 15 | end %> 16 | <%= "— #{attr :attribution}" %><% 17 | end %> 18 |
<% 19 | end %> 20 | 21 | -------------------------------------------------------------------------------- /templates/block_ruler.html.erb: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /templates/block_sidebar.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['sidebarblock',role].compact * ' ' %>"> 2 |
<% 3 | if title? %> 4 |
<%= title %>
<% 5 | end %> 6 | <%= content %> 7 |
8 | 9 | -------------------------------------------------------------------------------- /templates/block_table.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="table <%= 2 | ['tableblock',"frame-#{attr :frame, 'all'}","grid-#{attr :grid, 'all'}",role].compact * ' ' %>"<% 3 | if (attr? :float) || !(option? :autowidth) %> style="<%= 4 | [("width: #{attr :tablepcwidth}%;" unless option? :autowidth),("float: #{attr :float};" if attr? :float)].compact * ' ' %>"<% 5 | end %>><% 6 | if title? %> 7 | <%= captioned_title %><% 8 | end 9 | unless (attr :rowcount).zero? %> 10 | <% 11 | if option? :autowidth 12 | @columns.size.times do %> 13 | <% 14 | end 15 | else 16 | @columns.each do |col| %> 17 | <% 18 | end 19 | end %> 20 | <% 21 | [:head, :foot, :body].select {|tsec| !@rows[tsec].empty? }.each do |tsec| %> 22 | ><% 23 | @rows[tsec].each do |row| %> 24 | <% 25 | row.each do |cell| 26 | # store reference of content in advance to resolve attribute assignments in cells 27 | if tsec == :head 28 | cell_content = cell.text 29 | else 30 | case cell.style 31 | when :verse, :literal 32 | cell_content = cell.text 33 | else 34 | cell_content = cell.content 35 | end 36 | end 37 | cell_css_style = (@document.attr? :cellbgcolor) ? %(background-color: #{@document.attr :cellbgcolor};) : nil %> 38 | <<%= (cell_tag_name = (tsec == :head || cell.style == :header ? 'th' : 'td')) %> class="<%= ['tableblock',"halign-#{cell.attr :halign}","valign-#{cell.attr :valign}"] * ' ' %>"<%= cell.colspan ? %( colspan="#{cell.colspan}") : nil %><%= cell.rowspan ? %( rowspan="#{cell.rowspan}") : nil %><%= cell_css_style ? %( style="#{cell_css_style}") : nil %>><% 39 | if tsec == :head %><%= cell_content %><% 40 | else 41 | case cell.style 42 | when :asciidoc %>
<%= cell_content %>
<% 43 | when :verse %>
<%= cell_content %>
<% 44 | when :literal %>
<%= cell_content %>
<% 45 | else 46 | cell_content.each do |text| %>

<%= text %>

<% end 47 | end 48 | end %>><% 49 | end %> 50 | <% 51 | end %> 52 |
><% 53 | end 54 | end %> 55 | 56 | -------------------------------------------------------------------------------- /templates/block_toc.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | if @document.attr? :toc 3 | toc_id = @id 4 | toc_role = (attr :role, (@document.attr 'toc-class', 'toc')) 5 | toc_title_id = nil 6 | toc_title = title? ? title : (@document.attr 'toc-title') 7 | toc_levels = (attr? :levels) ? (attr :levels).to_i : (@document.attr :toclevels, 2).to_i 8 | if !toc_id && (@document.embedded? || !(@document.attr? 'toc-placement')) 9 | toc_id = 'toc' 10 | toc_title_id = 'toctitle' 11 | end 12 | %> class="<%= toc_role %>"><% 13 | if toc_title %> 14 |
><%= toc_title %>
<% 15 | end %> 16 | <%= converter.convert_with_options @document, 'outline', :toclevels => toc_levels %><% 17 | end %> 18 | -------------------------------------------------------------------------------- /templates/block_ulist.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | if (checklist = (option? :checklist) ? 'checklist' : nil) 3 | if option? :interactive 4 | marker_checked = '' 5 | marker_unchecked = '' 6 | else 7 | if @document.attr? :icons, 'font' 8 | marker_checked = '' 9 | marker_unchecked = '' 10 | else 11 | marker_checked = '✓' 12 | marker_unchecked = '❏' 13 | end 14 | end 15 | end %> class="<%= ['ulist',checklist,@style,role].compact * ' ' %>"><% 16 | if title? %> 17 |
<%= title %>
<% 18 | end %> 19 | ><% 20 | items.each do |item| %> 21 |
  • 22 |

    <% 23 | if checklist && (item.attr? :checkbox) %><%= %(#{(item.attr? :checked) ? marker_checked : marker_unchecked} #{item.text}) %><% 24 | else %><%= item.text %><% 25 | end %>

    <% 26 | if item.blocks? %> 27 | <%= item.content %><% 28 | end %> 29 |
  • <% 30 | end %> 31 | 32 | 33 | -------------------------------------------------------------------------------- /templates/block_verse.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['verseblock',role].compact * ' ' %>"><% 2 | if title? %> 3 |
    <%= title %>
    <% 4 | end %> 5 |
    <%= content %>
    <% 6 | if (attr? :attribution) or (attr? :citetitle) %> 7 |
    <% 8 | if attr? :citetitle %> 9 | <%= attr :citetitle %><% 10 | end 11 | if attr? :attribution 12 | if attr? :citetitle %>
    <% 13 | end %> 14 | <%= "— #{attr :attribution}" %><% 15 | end %> 16 |
    <% 17 | end %> 18 | 19 | -------------------------------------------------------------------------------- /templates/block_video.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%> class="<%= ['videoblock',@style,role].compact * ' ' %>"><% 2 | if title? %> 3 |
    <%= captioned_title %>
    <% 4 | end %> 5 |
    <% 6 | case attr :poster 7 | when 'vimeo' 8 | start_anchor = (attr? :start) ? %(#at=#{attr :start}) : nil 9 | delimiter = '?' 10 | autoplay_param = (option? :autoplay) ? %(#{delimiter}autoplay=1) : nil 11 | delimiter = '&' if autoplay_param 12 | loop_param = (option? :loop) ? %(#{delimiter}loop=1) : nil 13 | src = %(//player.vimeo.com/video/#{attr :target}#{start_anchor}#{autoplay_param}#{loop_param}) %> 14 | <%= (attr? :height) ? %( height="#{attr :height}") : nil %> src="<%= src %>" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen><% 15 | when 'youtube' 16 | params = ['rel=0'] 17 | params << %(start=#{attr :start}) if attr? :start 18 | params << %(end=#{attr :end}) if attr? :end 19 | params << 'autoplay=1' if option? :autoplay 20 | params << 'loop=1' if option? :loop 21 | params << 'controls=0' if option? :nocontrols 22 | src = %(//www.youtube.com/embed/#{attr :target}?#{params * '&'}) %> 23 | <%= (attr? :height) ? %( height="#{attr :height}") : nil %> src="<%= src %>" frameborder="0"<%= (option? :nofullscreen) ? nil : ' allowfullscreen' %>><% 24 | else %> 25 | <% 32 | end %> 33 |
    34 | 35 | -------------------------------------------------------------------------------- /templates/document.html.erb: -------------------------------------------------------------------------------- 1 | 21 | 24 |
    25 | 26 | 43 | <%= content %> 44 | 45 |
    46 | -------------------------------------------------------------------------------- /templates/embedded.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | if !notitle && has_header? 3 | %>><%= @header.title %><% 4 | end %><%= content %><% 5 | if footnotes? && !(attr? :nofootnotes) %> 6 |
    7 |
    <% 8 | footnotes.each do |fn| %><%= %( 9 |
    10 | #{fn.index}. #{fn.text} 11 |
    ) %><% 12 | end %> 13 |
    <% 14 | end %> 15 | -------------------------------------------------------------------------------- /templates/inline_anchor.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | case @type 3 | when :xref 4 | refid = (attr :refid) || @target 5 | %><%= %(#{@text || @document.references[:ids].fetch(refid, %[[#{refid}]]).tr_s("\n", ' ')}) %><% 6 | when :ref %><%= %() %><% 7 | when :bibref %><%= %([#{@target}]) %><% 8 | else %><%= %(#{@text}) %><% 9 | end %> 10 | -------------------------------------------------------------------------------- /templates/inline_break.html.erb: -------------------------------------------------------------------------------- 1 | <%= @text %>
    2 | -------------------------------------------------------------------------------- /templates/inline_button.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><%= @text %> 2 | -------------------------------------------------------------------------------- /templates/inline_callout.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | if @document.attr? :icons, 'font' %>(<%= @text %>)<% 3 | elsif @document.attr? :icons %>" alt="<%= @text %>"><% 4 | else %>(<%= @text %>)<% 5 | end %> 6 | -------------------------------------------------------------------------------- /templates/inline_footnote.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | idx = attr :index 3 | if @type == :xref 4 | %><%= %([#{idx}]) %><% 5 | else 6 | %><%= %([#{idx}]) %><% 7 | end %> 8 | -------------------------------------------------------------------------------- /templates/inline_image.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%>><% 2 | if @type == 'icon' && (@document.attr? :icons, 'font') 3 | style_class = [%(icon-#{@target})] 4 | style_class << %(icon-#{attr :size}) if attr? :size 5 | style_class << %(icon-rotate-#{attr :rotate}) if attr? :rotate 6 | style_class << %(icon-flip-#{attr :flip}) if attr? :flip 7 | title_attr = (attr? :title) ? %( title="#{attr :title}") : nil 8 | img = %() 9 | elsif @type == 'icon' && !(@document.attr? :icons) 10 | img = %([#{attr :alt}]) 11 | else 12 | img_src = (@type == 'icon' ? (icon_uri @target) : (image_uri @target)) 13 | img_attrs = [:alt, :width, :height, :title].map {|name| (attr? name) ? %( #{name}="#{attr name}") : nil }.join 14 | img = %() 15 | end 16 | if attr? :link %>><%= img %><% 17 | else %><%= img %><% 18 | end %> 19 | -------------------------------------------------------------------------------- /templates/inline_indexterm.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><%= @type == :visible ? @text : nil %> 2 | -------------------------------------------------------------------------------- /templates/inline_kbd.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | keys = attr 'keys' 3 | if keys.size == 1 4 | %><%= keys.first %><% 5 | else 6 | %><% 7 | idx = 0 8 | keys.map do |key| 9 | %><%= (idx += 1) == 1 ? nil : '+' %><%= key %><% 10 | end %><% 11 | end %> 12 | -------------------------------------------------------------------------------- /templates/inline_menu.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><% 2 | menu = attr 'menu' 3 | submenus = attr 'submenus' 4 | menuitem = attr 'menuitem' 5 | if !submenus.empty? 6 | submenu_path = submenus.map {|submenu| %(#{submenu} ▸ ) }.join.chop 7 | %><%= menu %> ▸ <%= submenu_path %> <%= menuitem %><% 8 | elsif !menuitem.nil? 9 | %><%= menu %> ▸ <%= menuitem %><% 10 | else 11 | %><%= menu %><% 12 | end %> 13 | -------------------------------------------------------------------------------- /templates/inline_quoted.html.erb: -------------------------------------------------------------------------------- 1 | <%#encoding:UTF-8%><%= @id && %() %><% 2 | class_attr = (style_class = role) ? %( class="#{style_class}") : nil 3 | case @type 4 | when :emphasis %><%= %(#{@text}) %><% 5 | when :strong %><%= %(#{@text}) %><% 6 | when :monospaced %><%= %(#{@text}) %><% 7 | when :superscript %><%= %(#{@text}) %><% 8 | when :subscript %><%= %(#{@text}) %><% 9 | when :double %><%= class_attr ? %(“#{@text}”) : %(“#{@text}”) %><% 10 | when :single %><%= class_attr ? %(‘#{@text}’) : %(‘#{@text}’) %><% 11 | when :asciimath, :latexmath 12 | open, close = Asciidoctor::INLINE_MATH_DELIMITERS[@type] %><%= %(#{open}#{@text}#{close}) %><% 13 | else %><%= class_attr ? %(#{@text}) : @text %><% 14 | end %> 15 | -------------------------------------------------------------------------------- /templates/section.html.erb: -------------------------------------------------------------------------------- 1 | <% slevel = @level.zero? && @special ? 1 : @level %> 2 | <% puts("#{slevel} #{title}")%> 3 | <% if slevel == 2 %> 4 | 5 |
    6 |

    <%=title %>

    7 |
    8 |
    9 | <%= content %> 10 |
    11 |
    12 |
    13 | <% else %> 14 |

    <%=title %>

    15 | <%= content %> 16 | <% end %> --------------------------------------------------------------------------------