Neo4j-Manual-2 0 0
Neo4j-Manual-2 0 0
Neo4j-Manual-2 0 0
This book is presented in open source and licensed through Creative Commons 3.0. You are free to copy, distribute, transmit, and/or adapt the work. This
license is based upon the following conditions:
Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your
use of the work).
Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.
Any of the above conditions can be waived if you get permission from the copyright holder.
In no way are any of the following rights affected by the license:
Your fair dealing or fair use rights
The authors moral rights
Rights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights
Note
For any reuse or distribution, you must make clear to the others the license terms of this work. The best way to do this is with a direct link to
this page: http://creativecommons.org/licenses/by-sa/3.0/ <http://creativecommons.org/licenses/by-sa/3.0/>
Table of Contents
Preface .................................................................................................................................................... v
I. Introduction ........................................................................................................................................ 1
1. Neo4j Highlights ....................................................................................................................... 2
2. Graph Database Concepts ......................................................................................................... 3
3. The Neo4j Graph Database ..................................................................................................... 11
II. Tutorials .......................................................................................................................................... 21
4. Getting started with Cypher .................................................................................................... 22
5. Data Modeling Examples ........................................................................................................ 34
6. Languages ................................................................................................................................ 77
III. Cypher Query Language ............................................................................................................... 83
7. Introduction .............................................................................................................................. 84
8. Syntax ...................................................................................................................................... 97
9. General Clauses ..................................................................................................................... 113
10. Reading Clauses .................................................................................................................. 129
11. Writing Clauses ................................................................................................................... 157
12. Functions .............................................................................................................................. 180
13. Schema ................................................................................................................................. 207
14. From SQL to Cypher .......................................................................................................... 211
IV. Reference ..................................................................................................................................... 218
15. Capabilities .......................................................................................................................... 219
16. Transaction Management .................................................................................................... 226
17. Data Import .......................................................................................................................... 234
18. Graph Algorithms ................................................................................................................ 235
19. REST API ............................................................................................................................ 237
20. Deprecations ........................................................................................................................ 356
V. Operations ..................................................................................................................................... 357
21. Installation & Deployment .................................................................................................. 358
22. Configuration & Performance ............................................................................................. 372
23. High Availability ................................................................................................................. 407
24. Backup ................................................................................................................................. 431
25. Security ................................................................................................................................ 437
26. Monitoring ........................................................................................................................... 443
VI. Tools ............................................................................................................................................ 459
27. Web Interface ...................................................................................................................... 460
28. Neo4j Shell .......................................................................................................................... 461
VII. Community ................................................................................................................................. 478
29. Community Support ............................................................................................................ 479
30. Contributing to Neo4j .......................................................................................................... 480
VIII. Advanced Usage ....................................................................................................................... 504
31. Extending the Neo4j Server ................................................................................................ 505
32. Using Neo4j embedded in Java applications ...................................................................... 513
33. The Traversal Framework ................................................................................................... 545
34. Legacy Indexing .................................................................................................................. 554
35. Batch Insertion ..................................................................................................................... 573
A. Manpages ...................................................................................................................................... 577
neo4j ........................................................................................................................................... 578
neo4j-installer ............................................................................................................................. 580
iii
iv
Preface
This is the reference manual for Neo4j version 2.0.0, authored by the Neo4j Team.
The main parts of the manual are:
The material is practical, technical, and focused on answering specific questions. It addresses how
things work, what to do and what to avoid to successfully run Neo4j in a production environment.
The goal is to be thumb-through and rule-of-thumb friendly.
Each section should stand on its own, so you can hop right to whatever interests you. When possible,
the sections distill rules of thumb which you can keep in mind whenever you wander out of the
house without this manual in your back pocket.
The included code examples are executed when Neo4j is built and tested. Also, the REST API request
and response examples are captured from real interaction with a Neo4j server. Thus, the examples are
always in sync with how Neo4j actually works.
Theres other documentation resources besides the manual as well:
Neo4j Cypher Refcard, see http://docs.neo4j.org/ for available versions.
Neo4j GraphGist, an online tool for creating interactive web pages with executable Cypher
statements: http://gist.neo4j.org/.
The main Neo4j site at http://www.neo4j.org/ is a good starting point to learn about Neo4j.
Who should read this?
The topics should be relevant to architects, administrators, developers and operations personnel.
PartI.Introduction
This part gives a birds eye view of what a graph database is, and then outlines some specifics of Neo4j.
Chapter1.Neo4j Highlights
As a robust, scalable and high-performance database, Neo4j is suitable for full enterprise deployment
or a subset of the full server can be used in lightweight projects.
It features:
Proper ACID behavior is the foundation of data reliability. Neo4j enforces that all operations that
modify data occur within a transaction, guaranteeing consistent data. This robustness extends from
single instance embedded graphs to multi-server high availability installations. For details, see
Chapter16, Transaction Management.
Reliable graph storage can easily be added to any application. A graph can scale in size and
complexity as the application evolves, with little impact on performance. Whether starting new
development, or augmenting existing functionality, Neo4j is only limited by physical hardware.
A single server instance can handle a graph of billions of nodes and relationships. When data
throughput is insufficient, the graph database can be distributed among multiple servers in a high
availability configuration. See Chapter23, High Availability to learn more.
The graph database storage shines when storing richly-connected data. Querying is performed through
traversals, which can perform millions of traversal steps per second. A traversal step resembles a join
in a RDBMS.
records dat a in
records dat a in
Labels
Relat ionships
group
organize
Nodes
have
have
Propert ies
Traversal
navigat es
ident ifies
Graph
Algorit hm
records dat a in
Relat ionships
expresses
Pat hs
records dat a in
order
organize
Nodes
Indexes
m ap t o
Relat ionships
m ap t o
organize
have
m ap from
Nodes
have
Propert ies
Traversal
m anages
m anages
Indexes
navigat es
ident ifies
Graph
m ap t o
m ap t o
Algorit hm
records dat a in
Relat ionships
m ap from
records dat a in
organize
have
expresses
Nodes
have
Propert ies
Pat hs
order
A1
B1
A2
B2
A3
B3
B4
B5
C1
B6
C2
B7
C3
A1
A2
C1
B2
B6
B1
B4
B5
A3
C2
B3
B7
V2
K3
K1
K3
V3
K1
K*
represents a key, V* a value. Note that some keys point to other keys as well as plain values.
Figure2.4.Graph Database as Key-Value Store
K2
K1
V2
V1
V3
K3
D2
S1
D2/S2
S3
V1
D1/S1
S2
V4
V3
V2
D1
V2
D2
S3
S1
S2
V3
V4
10
11
3.1.Nodes
The fundamental units that form a graph are nodes and relationships. In Neo4j, both nodes and
relationships can contain properties.
Nodes are often used to represent entities, but depending on the domain relationships may be used for
that purpose as well.
Apart from properties and relationships, nodes can also be labeled with zero or more labels.
A Node
can have
can have
Relat ionships
can have
Labels
can have
Propert ies
Lets start out with a really simple graph, containing only a single node with one property:
nam e: Pet er
12
3.2.Relationships
Relationships between nodes are a key part of a graph database. They allow for finding related data.
Just like nodes, relationships can have properties.
A Relat ionship
has a
St art node
has a
has a
End node
can have
Propert ies
Nam e
A relationship connects two nodes, and is guaranteed to have valid start and end nodes.
St art node
relat ionship
End node
As relationships are always directed, they can be viewed as outgoing or incoming relative to a node,
which is useful when traversing the graph:
incom ing relat ionship
Node
Relationships are equally well traversed in either direction. This means that there is no need to add
duplicate relationships in the opposite direction (with regard to traversal or performance).
While relationships always have a direction, you can ignore the direction where it is not useful in your
application.
Note that a node can have relationships to itself as well:
Node
loop
To further enhance graph traversal all relationships have a relationship type. Note that the word type
might be misleading here, you could rather think of it as a label. The following example shows a
simple social network with two relationship types.
13
Maja
follows
Alice
follows
follows
Oscar
blocks
William
How
14
3.3.Properties
Both nodes and relationships can have properties.
Properties are key-value pairs where the key is a string. Property values can be either a primitive or an
array of one primitive type. For example String, int and int[] values are valid for properties.
Note
NULL
is not a valid property value. NULLs can instead be modeled by the absence of a key.
A Propert y
has a
Value
has a
Key
is a
boolean
byt e
short
int
Prim it ive
long
float
double
char
St ring
Description
Value range
true/false
boolean
byte
8-bit integer
-128
short
16-bit integer
-32768
int
32-bit integer
-2147483648
long
64-bit integer
-9223372036854775808 to
9223372036854775807, inclusive
float
double
to 127, inclusive
to 32767, inclusive
to 2147483647, inclusive
Description
Value range
char
u0000
String
to uffff (0 to 65535)
For further details on float/double values, see Java Language Specification <http://docs.oracle.com/
javase/specs/jls/se5.0/html/typesValues.html#4.2.3>.
16
3.4.Labels
A label is a named graph construct that is used to group nodes into sets; all nodes labeled with the
same label belongs to the same set. Many database queries can work with these sets instead of the
whole graph, making queries easier to write and more efficient. A node may be labeled with any
number of labels, including none, making labels an optional addition to the graph.
A Label
has a
Nam e
groups
Node
Labels are used when defining contraints and adding indexes for properties.
An example would be a label named User that you label all your nodes representing users with. With
that in place, you can ask Neo4j to perform operations only on your user nodes, such as finding all
users with a given name.
However, you can use labels for much more. For instance, since labels can be added and removed
during runtime, they can be used to mark temporary states for your nodes. You might create an
Offline label for phones that are offline, a Happy label for happy pets, and so on.
3.4.1.Label names
Any non-empty unicode string can be used as a label name. In Cypher, you may need to use the
backtick (`) syntax to avoid clashes with Cypher identifier rules. By convention, labels are written
with CamelCase notation, with the first letter in upper case. For instance, User or CarOwner.
Labels have an id space of an int, meaning the maximum number of labels the database can contain is
roughly 2 billion.
17
3.5.Paths
A path is one or more nodes with connecting relationships, typically retrieved as a query or traversal
result.
A Pat h
St art Node
Relat ionship
accom panied by a
Node
The shortest possible path has length zero and looks like this:
Node
Relat ionship 1
Node 2
Relat ionship 1
18
has an
End Node
3.6.Traversal
Traversing a graph means visiting its nodes, following relationships according to some rules. In most
cases only a subgraph is visited, as you already know where in the graph the interesting nodes and
relationships are found.
Cypher provides a declarative way to query the graph powered by traversals and other techniques. See
PartIII, Cypher Query Language for more information.
Neo4j comes with a callback based traversal API which lets you specify the traversal rules. At a basic
level theres a choice between traversing breadth- or depth-first.
For an in-depth introduction to the traversal framework, see Chapter33, The Traversal Framework.
For Java code examples see Section32.7, Traversal.
19
3.7.Schema
Neo4j is a schema-optional graph database. You can use Neo4j without any schema. Optionally you
can introduce it in order to gain performance or modeling benefits. This allows a way of working
where the schema does not get in your way until you are at a stage where you want to reap the benefits
of having one.
3.7.1.Indexes
Note
This feature was introduced in Neo4j 2.0, and is not the same as the legacy indexes (see
Chapter34, Legacy Indexing).
Performance is gained by creating indexes, which improve the speed of looking up nodes in the
database. Once youve specified which properties to index, Neo4j will make sure your indexes are
kept up to date as your graph evolves. Any operation that looks up nodes by the newly indexed
properties will see a significant performance boost.
Indexes in Neo4j are eventually available. That means that when you first create an index, the
operation returns immediately. The index is populating in the background and so is not immediately
available for querying. When the index has been fully populated it will eventually come online. That
means that it is now ready to be used in queries.
If something should go wrong with the index, it can end up in a failed state. When it is failed, it will
not be used to speed up queries. To rebuild it, you can drop and recreate the index. Look at logs for
clues about the failure.
You can track the status of your index by asking for the index state through the API you are using.
Note, however, that this is not yet possible through Cypher.
How to use indexes in the different APIs:
3.7.2.Constraints
Note
PartII.Tutorials
The tutorial part describes how use Neo4j. It takes you from Hello World to advanced usage of graphs.
22
Now lets create a movie and connect it to the Tom Hanks node with an ACTED_IN relationship:
MATCH (actor:Actor)
WHERE actor.name = "Tom Hanks"
CREATE (movie:Movie { title:'Sleepless IN Seattle' })
CREATE (actor)-[:ACTED_IN]->(movie);
Using a WHERE clause in the query above to get the Tom Hanks node does the same thing as the pattern
in the MATCH clause of the previous query.
This is how our graph looks now:
Act or
nam e = 'Tom Hanks'
ACTED_IN
Movie
t it le = 'Sleepless in Seat t le'
We can do more of the work in a single clause. CREATE UNIQUE will make sure we dont create duplicate
patterns. Using this: [r:ACTED_IN] lets us return the relationship.
MATCH (actor:Actor { name: "Tom Hanks" })
CREATE UNIQUE (actor)-[r:ACTED_IN]->(movie:Movie { title:"Forrest Gump" })
RETURN r;
The labels Actor and Movie help us organize the graph. Lets list all Movie nodes:
MATCH (movie:Movie)
RETURN movie AS `All Movies`;
All Movies
Node[1]{title:"Sleepless in Seattle"}
Node[2]{title:"Forrest Gump"}
2 rows
23
4.2.Movie Database
Our example graph consists of movies with title and year and actors with a name. Actors have ACTS_IN
relationships to movies, which represents the role they played. This relationship also has a role
attribute.
Well go with three movies and three actors:
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
ACTS_IN
role = 'Neo'
ACTS_IN
role = 'Trinit y'
Act or
ACTS_IN
role = 'Neo'
Movie
t it le = 'The Mat rix Reloaded'
year = '2003-05-07'
Act or
ACTS_IN
role = 'Trinit y'
ACTS_IN
role = 'Morpheus'
ACTS_IN
role = 'Neo'
Movie
ACTS_IN
role = 'Trinit y'
Movie
movie.year
"The Matrix"
"1999-03-31"
1 row
Show all actors:
MATCH (actor:Actor)
RETURN actor;
24
ACTS_IN
role = 'Morpheus'
ACTS_IN
role = 'Morpheus'
Heres some exploratory queries for unknown datasets. Dont do this on live production databases!
Count nodes:
MATCH (n)
RETURN count(*);
type(r)
count(*)
"ACTS_IN"
1 row
List all nodes and their relationships:
MATCH (n)-[r]->(m)
RETURN n AS from, r AS `->`, m AS to;
from
->
to
Node[3]{name:"Keanu Reeves"}
:ACTS_IN[0]{role:"Neo"}
Node[0]{title:"The Matrix",
year:"1999-03-31"}
Node[3]{name:"Keanu Reeves"}
:ACTS_IN[1]{role:"Neo"}
Node[1]{title:"The Matrix
Reloaded", year:"2003-05-07"}
Node[3]{name:"Keanu Reeves"}
:ACTS_IN[2]{role:"Neo"}
Node[2]{title:"The Matrix
Revolutions", year:"2003-10-27"}
Node[4]{name:"Laurence
Fishburne"}
:ACTS_IN[3]{role:"Morpheus"}
Node[0]{title:"The Matrix",
year:"1999-03-31"}
Node[4]{name:"Laurence
Fishburne"}
:ACTS_IN[4]{role:"Morpheus"}
Node[1]{title:"The Matrix
Reloaded", year:"2003-05-07"}
Node[4]{name:"Laurence
Fishburne"}
:ACTS_IN[5]{role:"Morpheus"}
Node[2]{title:"The Matrix
Revolutions", year:"2003-10-27"}
Node[5]{name:"Carrie-Anne
Moss"}
:ACTS_IN[6]{role:"Trinity"}
Node[0]{title:"The Matrix",
year:"1999-03-31"}
9 rows
25
->
to
Node[5]{name:"Carrie-Anne
Moss"}
:ACTS_IN[7]{role:"Trinity"}
Node[1]{title:"The Matrix
Reloaded", year:"2003-05-07"}
Node[5]{name:"Carrie-Anne
Moss"}
:ACTS_IN[8]{role:"Trinity"}
Node[2]{title:"The Matrix
Revolutions", year:"2003-10-27"}
9 rows
26
me
Node[6]{name:"Me"}
1 row
Nodes created: 1
Properties set: 1
Labels added: 1
Lets check if the node is there:
MATCH (me:User { name: "Me" })
RETURN me.name;
movie.title
rating.stars
rating.comment
"The Matrix"
1 row
27
Add our friendship idempotently, so we can re-run the query without adding it several times. We
return the relationship to check that it has not been created several times.
MATCH (me:User { name: "Me" }),(friend:User { name: "A Friend" })
CREATE UNIQUE (me)-[friendship:FRIEND]->(friend)
RETURN friendship;
You can rerun the query, see that it doesnt change anything the second time!
Lets update our friendship with a since property:
MATCH (me:User { name: "Me" })-[friendship:FRIEND]->(friend:User { name: "A Friend" })
SET friendship.since='forever'
RETURN friendship;
Lets pretend us being our friend and wanting to see which movies our friends have rated.
MATCH (me:User { name: "A Friend" })-[:FRIEND]-(friend)-[rating:RATED]->(movie)
RETURN movie.title, avg(rating.stars) AS stars, collect(rating.comment) AS comments, count(*);
movie.title
stars
comments
count(*)
"The Matrix"
5. 0
1 row
Thats too little data, lets add some more friends and friendships.
MATCH (me:User { name: "Me" })
FOREACH (i IN range(1,10)| CREATE (friend:User { name: "Friend " + i }),(me)-[:FRIEND]->(friend));
friendship
friend.name
"FRIEND"
"A Friend"
"FRIEND"
"Friend 1"
"FRIEND"
"Friend 2"
"FRIEND"
"Friend 3"
"FRIEND"
"Friend 4"
"FRIEND"
"Friend 5"
"FRIEND"
"Friend 6"
"FRIEND"
"Friend 7"
"FRIEND"
"Friend 8"
"FRIEND"
"Friend 9"
"FRIEND"
"Friend 10"
11 rows
28
4.4.Finding Paths
Our example graph consists of movies with title and year and actors with a name. Actors have ACTS_IN
relationships to movies, which represents the role they played. This relationship also has a role
attribute.
We queried and updated the data so far, now lets find interesting constellations, a.k.a. paths.
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
CREATE
All other movies that actors in The Matrix acted in ordered by occurrence:
MATCH (:Movie { title: "The Matrix" })<-[:ACTS_IN]-(actor)-[:ACTS_IN]->(movie)
RETURN movie.title, count(*)
ORDER BY count(*) DESC ;
movie.title
count(*)
2 rows
Lets see who acted in each of these movies:
MATCH (:Movie { title: "The Matrix" })<-[:ACTS_IN]-(actor)-[:ACTS_IN]->(movie)
RETURN movie.title, collect(actor.name), count(*) AS count
ORDER BY count DESC ;
movie.title
collect(actor.name)
count
2 rows
What about co-acting, that is actors that acted together:
MATCH (:Movie { title: "The Matrix"
})<-[:ACTS_IN]-(actor)-[:ACTS_IN]->(movie)<-[:ACTS_IN]-(colleague)
RETURN actor.name, collect(DISTINCT colleague.name);
actor.name
collect(distinct colleague.name)
"Carrie-Anne Moss"
3 rows
29
collect(distinct colleague.name)
"Laurence Fishburne"
"Keanu Reeves"
3 rows
Who of those other actors acted most often with anyone from the matrix cast?
MATCH (:Movie { title: "The Matrix"
})<-[:ACTS_IN]-(actor)-[:ACTS_IN]->(movie)<-[:ACTS_IN]-(colleague)
RETURN colleague.name, count(*)
ORDER BY count(*) DESC LIMIT 10;
colleague.name
count(*)
"Carrie-Anne Moss"
"Keanu Reeves"
"Laurence Fishburne"
3 rows
Starting with paths, a path is a sequence of nodes and relationships from a start node to an end node.
We know that Trinity loves Neo, but how many paths exist between their actors? Well limit the path
length and the query as it exhaustively searches the graph otherwise
MATCH p =(:Actor { name: "Keanu Reeves" })-[:ACTS_IN*0..5]-(:Actor { name: "Carrie-Anne Moss" })
RETURN p, length(p)
LIMIT 10;
length(p)
9 rows
30
length(p)
9 rows
Bur thats a lot of data, we just want to look at the names and titles of the nodes of the path.
MATCH p =(:Actor { name: "Keanu Reeves" })-[:ACTS_IN*0..5]-(:Actor { name: "Carrie-Anne Moss" })
31
length(p)
9 rows
32
Note that adding the unique constraint will add an index on that property, so we wont do that
separately. If we drop the constraint, we will have to add an index instead, as needed.
In this case we want an index to speed up finding actors by name in the database:
CREATE INDEX ON :Actor(name)
Indexes can be added at any time. Constraints can be added after a label is already in use, but that
requires that the existing data complies with the constraints. Note that it will take some time for an
index to come online when theres existing data.
Now, lets add some data.
CREATE (actor:Actor { name:"Tom Hanks" }),(movie:Movie { title:'Sleepless IN Seattle' }),
(actor)-[:ACTED_IN]->(movie);
Normally you dont specify indexes when querying for data. They will be used automatically. This
means we can simply look up the Tom Hanks node, and the index will kick in behind the scenes to
boost performance.
MATCH (actor:Actor { name: "Tom Hanks" })
RETURN actor;
Now lets say we want to add another label for a node. Heres how to do that:
MATCH (actor:Actor { name: "Tom Hanks" })
SET actor :American;
Section3.4, Labels
Chapter13, Schema
Section13.2, Constraints
Section13.1, Indexes
Section9.7, Using
Section11.4, Set
Section11.6, Remove
33
34
5.1.Linked Lists
A powerful feature of using a graph database, is that you can create your own in-graph data
structures for example a linked list.
This data structure uses a single node as the list reference. The reference has an outgoing relationship
to the head of the list, and an incoming relationship from the last element of the list. If the list is
empty, the reference will point to itself.
To make it clear what happens, we will show how the graph looks after each query.
To initialize an empty linked list, we simply create a node, and make it link to itself. Unlike the actual
list elements, it doesnt have a value property.
CREATE (root { name: 'ROOT' })-[:LINK]->(root)
RETURN root
nam e = 'ROOT'
LINK
Adding values is done by finding the relationship where the new value should be placed in, and
replacing it with a new node, and two relationships to it. We also have to handle the fact that the
before and after nodes could be the same as the root node. The case where before, after and the root
node are all the same, makes it necessary to use CREATE UNIQUE to not create two new value nodes by
mistake.
MATCH (root)-[:LINK*0..]->(before),(after)-[:LINK*0..]->(root),(before)-[old:LINK]->(after)
WHERE root.name = 'ROOT' AND (before.value < 25 OR before = root) AND (25 < after.value OR after =
root)
CREATE UNIQUE (before)-[:LINK]->({ value:25 })-[:LINK]->(after)
DELETE old
nam e = 'ROOT'
LINK
LINK
value = 25
35
nam e = 'ROOT'
LINK
value = 10
LINK
LINK
value = 25
Deleting a value, conversely, is done by finding the node with the value, and the two relationships
going in and out from it, and replacing the relationships with a new one.
MATCH (root)-[:LINK*0..]->(before),(before)-[delBefore:LINK]->(del)-[delAfter:LINK]->(after),
(after)-[:LINK*0..]->(root)
WHERE root.name = 'ROOT' AND del.value = 10
CREATE UNIQUE (before)-[:LINK]->(after)
DELETE del, delBefore, delAfter
nam e = 'ROOT'
LINK
LINK
value = 25
Deleting the last value node is what requires us to use CREATE UNIQUE when replacing the relationships.
Otherwise, we would end up with two relationships from the root node to itself, as both before and
after nodes are equal to the root node, meaning the pattern would match twice.
MATCH (root)-[:LINK*0..]->(before),(before)-[delBefore:LINK]->(del)-[delAfter:LINK]->(after),
(after)-[:LINK*0..]->(root)
WHERE root.name = 'ROOT' AND del.value = 25
CREATE UNIQUE (before)-[:LINK]->(after)
DELETE del, delBefore, delAfter
nam e = 'ROOT'
36
LINK
5.2.TV Shows
This example show how TV Shows with Seasons, Episodes, Characters, Actors, Users and Reviews
can be modeled in a graph database.
5.2.1.Data Model
Lets start out with an entity-relationship model of the domain at hand:
TV Show
has
Season
has
User
Episode
wrot e
has
Review
feat ured
Act or
played
Charact er
Description
HAS_SEASON
HAS_EPISODE
FEATURED_CHARACTER
PLAYED_CHARACTER
HAS_REVIEW
WROTE_REVIEW
5.2.2.Sample Data
Lets create some data and see how the domain plays out in practice:
CREATE (himym:TVShow { name: "How I Met Your Mother" })
37
HAS_SEASON
Season
nam e = 'HIMYM Season 1'
HAS_EPISODE
Act or
nam e = 'Josh Radnor'
PLAYED_CHARACTER
Episode
User
nam e = 'WakenPayne'
FEATURED_CHARACTER
Charact er
HAS_REVIEW
WROTE_REVIEW
Note that even though we could have modeled the reviews as relationships with title and content
properties on them, we made them nodes instead. We gain a lot of flexibility in this way, for example
if we want to connect comments to each review.
Now lets add more data:
MATCH (himym:TVShow { name: "How I Met Your Mother" }),(himym_s1:Season),
(himym_s1_e1:Episode { name: "Pilot" }),
(himym)-[:HAS_SEASON]->(himym_s1)-[:HAS_EPISODE]->(himym_s1_e1)
CREATE (marshall:Character { name: "Marshall Eriksen" })
CREATE (robin:Character { name: "Robin Scherbatsky" })
CREATE (barney:Character { name: "Barney Stinson" })
CREATE (lily:Character { name: "Lily Aldrin" })
CREATE (jasonSegel:Actor { name: "Jason Segel" })
CREATE (cobieSmulders:Actor { name: "Cobie Smulders" })
CREATE (neilPatrickHarris:Actor { name: "Neil Patrick Harris" })
CREATE (alysonHannigan:Actor { name: "Alyson Hannigan" })
CREATE UNIQUE (jasonSegel)-[:PLAYED_CHARACTER]->(marshall)
CREATE UNIQUE (cobieSmulders)-[:PLAYED_CHARACTER]->(robin)
CREATE UNIQUE (neilPatrickHarris)-[:PLAYED_CHARACTER]->(barney)
CREATE UNIQUE (alysonHannigan)-[:PLAYED_CHARACTER]->(lily)
38
season.name
episode.name
"Pilot"
1 row
We could also grab the reviews if there are any by slightly tweaking the query:
MATCH (tvShow:TVShow)-[:HAS_SEASON]->(season)-[:HAS_EPISODE]->(episode)
WHERE tvShow.name = "How I Met Your Mother"
WITH season, episode
OPTIONAL MATCH (episode)-[:HAS_REVIEW]->(review)
RETURN season.name, episode.name, review
season.name
episode.name
review
"Pilot"
Node[5]{title:"Meet Me At The
Bar In 15 Minutes & Suit Up",
content:"It was awesome"}
"Pilot"
Node[15]{title:"What a
great pilot for a show :)",
content:"The humour is great. "}
2 rows
Now lets list the characters featured in a show. Note that in this query we only put identifiers on the
nodes we actually use later on. The other nodes of the path pattern are designated by ().
MATCH (tvShow:TVShow)-[:HAS_SEASON]->()-[:HAS_EPISODE]->()-[:FEATURED_CHARACTER]->(character)
WHERE tvShow.name = "How I Met Your Mother"
RETURN DISTINCT character.name
character.name
"Ted Mosby"
"Marshall Eriksen"
"Robin Scherbatsky"
"Barney Stinson"
5 rows
39
5 rows
Now lets look at how to get all cast members of a show.
MATCH
(tvShow:TVShow)-[:HAS_SEASON]->()-[:HAS_EPISODE]->(episode)-[:FEATURED_CHARACTER]->()<-[:PLAYED_CHARACTER]-(actor)
WHERE tvShow.name = "How I Met Your Mother"
RETURN DISTINCT actor.name
actor.name
"Josh Radnor"
"Jason Segel"
"Cobie Smulders"
"Neil Patrick Harris"
"Alyson Hannigan"
5 rows
And now well create a query to find the episodes that he has appeared in:
MATCH (actor:Actor)-[:PLAYED_CHARACTER]->(character)<-[:FEATURED_CHARACTER]-(episode)
WHERE actor.name = "Josh Radnor"
RETURN episode.name AS Episode, character.name AS Character
Episode
Character
"Pilot"
"Ted Mosby"
"Peter's Progress"
"Keith"
2 rows
Now lets go for a similar query, but add the season and show to it as well.
MATCH (actor:Actor)-[:PLAYED_CHARACTER]->(character)<-[:FEATURED_CHARACTER]-(episode),
(episode)<-[:HAS_EPISODE]-(season)<-[:HAS_SEASON]-(tvshow)
WHERE actor.name = "Josh Radnor"
RETURN tvshow.name AS Show, season.name AS Season, episode.name AS Episode,
40
Show
Season
Episode
Character
"Pilot"
"Ted Mosby"
"ER"
"Peter's Progress"
"Keith"
"ER S7"
2 rows
41
5.3.1.Generic approach
In many scenarios, an application needs to handle security on some form of managed objects. This
example describes one pattern to handle this through the use of a graph structure and traversers
that build a full permissions-structure for any managed object with exclude and include overriding
possibilities. This results in a dynamic construction of ACLs based on the position and context of the
managed object.
The result is a complex security scheme that can easily be implemented in a graph structure,
supporting permissions overriding, principal and content composition, without duplicating data
anywhere.
Technique
As seen in the example graph layout, there are some key concepts in this domain model:
The managed content (folders and files) that are connected by HAS_CHILD_CONTENT relationships
The Principal subtree pointing out principals that can act as ACL members, pointed out by the
PRINCIPAL relationships.
The aggregation of principals into groups, connected by the IS_MEMBER_OF relationship. One principal
(user or group) can be part of many groups at the same time.
The SECURITY relationships, connecting the content composite structure to the principal composite
structure, containing a addition/removal modifier property ("+RW").
42
5.3.2.Read-permission example
In this example, we are going to examine a tree structure of directories and files. Also, there are
users that own files and roles that can be assigned to users. Roles can have permissions on directory or
files structures (here we model only canRead, as opposed to full rwx Unix permissions) and be nested.
A more thorough example of modeling ACL structures can be found at How to Build Role-Based
Access Control in SQL <http://www.xaprb.com/blog/2006/08/16/how-to-build-role-based-accesscontrol-in-sql/>.
43
has
Node[ 21]
'nam e' = 'Role'
subRole
subRole
has
Node[ 7]
Node[ 22]
m em ber
m em ber
m em ber
m em ber
canRead
Node[ 13]
Node[ 14]
Node[ 8]
Node[ 9]
Node[ 18]
cont ains
Node[ 10]
Node[ 12]
owns
cont ains
cont ains
cont ains
Node[ 20]
Node[ 15]
Node[ 11]
owns
leaf
cont ains
Node[ 17]
Node[ 23]
Node[ 16]
'nam e' = 'File2'
resulting in:
file
Node[10]{name:"File1"}
Node[9]{name:"File2"}
2 rows
What files are owned by whom?
If we introduce the concept of ownership on files, we then can ask for the owners of the files we
find connected via owns relationships to file nodes.
MATCH ({ name: 'FileRoot' })-[:contains*0..]->()-[:leaf]->(file)<-[:owns]-(user)
RETURN file, user
user
Node[10]{name:"File1"}
Node[7]{name:"User1"}
Node[9]{name:"File2"}
Node[6]{name:"User2"}
2 rows
44
This will return the file, and the directory where the user has the canRead permission along with the
user and their role.
file.name
dir.name
role.name
readUser.name
"File1"
"HomeU1"
<null>
<null>
"File1"
"Home"
<null>
<null>
"File1"
"FileRoot"
"SUDOers"
"Admin1"
"File1"
"FileRoot"
"SUDOers"
"Admin2"
"File2"
"Desktop"
<null>
<null>
"File2"
"HomeU2"
<null>
<null>
"File2"
"Home"
<null>
<null>
"File2"
"FileRoot"
"SUDOers"
"Admin1"
"File2"
"FileRoot"
"SUDOers"
"Admin2"
9 rows
The results listed above contain null for optional path segments, which can be mitigated by either
asking several queries or returning just the really needed values.
45
5.4.Hyperedges
Imagine a user being part of different groups. A group can have different roles, and a user can be part
of different groups. He also can have different roles in different groups apart from the membership.
The association of a User, a Group and a Role can be referred to as a HyperEdge. However, it can be
easily modeled in a property graph as a node that captures this n-ary relationship, as depicted below in
the U1G2R1 node.
Figure5.1.Graph
nam e = 'User1'
hasRoleInGroup
nam e = 'U1G2R1'
hasRoleInGroup
in
in
nam e = 'U1G1R2'
hasGroup
hasRole
hasGroup
nam e = 'Group2'
canHave
nam e = 'Group1'
isA
canHave
nam e = 'Role1'
canHave
isA
nam e = 'Group'
isA
hasRole
canHave
nam e = 'Role2'
isA
nam e = 'Role'
5.4.1.Find Groups
To find out in what roles a user is for a particular groups (here Group2), the following query can
traverse this HyperEdge node and provide answers.
Query.
MATCH ({ name: 'User1' })-[:hasRoleInGroup]->(hyperEdge)-[:hasGroup]->({ name: 'Group2' }),
(hyperEdge)-[:hasRole]->(role)
RETURN role.name
46
1 row
group.name
"Role1"
"Group2"
"Role2"
"Group1"
2 rows
The graph for this looks like the following (nodes like U1G2R23 representing the HyperEdges):
Figure5.2.Graph
nam e = 'User2'
hasRoleInGroup
nam e = 'U2G2R34'
hasGroup
nam e = 'Group2'
hasGroup
hasRole
hasRole
nam e = 'Role3'
hasRoleInGroup
nam e = 'U1G2R23'
hasRole
hasRole
nam e = 'Role4'
hasRoleInGroup
nam e = 'U1G3R34'
hasRole
nam e = 'User1'
hasGroup
nam e = 'Group3'
hasRoleInGroup
hasRoleInGroup
hasRoleInGroup
nam e = 'U2G3R56'
hasGroup
hasRole
nam e = 'Role6'
nam e = 'U2G1R25'
hasRole
hasRole
nam e = 'Role5'
hasRole
nam e = 'U1G1R12'
hasRole
nam e = 'Role2'
hasRole
hasGroup
hasGroup
nam e = 'Group1'
hasRole
nam e = 'Role1'
To return Group1 and Group2 as User1 and User2 share at least one common role in these two groups, the
query looks like this:
Query.
MATCH (u1)-[:hasRoleInGroup]->(hyperEdge1)-[:hasGroup]->(group),(hyperEdge1)-[:hasRole]->(role),
(u2)-[:hasRoleInGroup]->(hyperEdge2)-[:hasGroup]->(group),(hyperEdge2)-[:hasRole]->(role)
WHERE u1.name = 'User1' AND u2.name = 'User2'
RETURN group.name, count(role)
47
The groups where User1 and User2 share at least one common role:
Result
group.name
count(role)
"Group1"
"Group2"
2 rows
48
knows
knows
nam e = 'Sara'
knows
knows
nam e = 'Bill'
knows
nam e = 'Derrick'
knows
nam e = 'Jill'
knows
nam e = 'Ian'
To find out the friends of Joes friends that are not already his friends, the query looks like this:
Query.
MATCH (joe { name: 'Joe' })-[:knows*2..2]-(friend_of_friend)
WHERE NOT (joe)-[:knows]-(friend_of_friend)
RETURN friend_of_friend.name, COUNT(*)
ORDER BY COUNT(*) DESC , friend_of_friend.name
This returns a list of friends-of-friends ordered by the number of connections to them, and secondly by
their name.
Result
friend_of_friend.name
COUNT(*)
"Ian"
"Derrick"
"Jill"
3 rows
49
5.6.Co-favorited places
Figure5.4.Graph
nam e = 'Joe'
favorit e
nam e = 'SaunaX'
favorit e
nam e = 'CoffeeShop1'
nam e = 'Jill'
favorit e
favorit e
favorit e
nam e = 'CoffeeShop3'
t agged
nam e = 'MelsPlace'
t agged
t agged
t agged
nam e = 'Cosy'
favorit e
nam e = 'CoffeeShop2'
t agged
nam e = 'CoffeShop2'
t agged
nam e = 'Cool'
The list of places that are favorited by people that favorited the start place.
Result
stuff.name
count(*)
"MelsPlace"
"CoffeShop2"
"SaunaX"
3 rows
This query returns other places than CoffeeShop1 which share the same tags; they are ranked by the
number of tags.
50
collect(tag.name)
"MelsPlace"
["Cool", "Cosy"]
"CoffeeShop2"
["Cool"]
"CoffeeShop3"
["Cosy"]
3 rows
51
friend
nam e = 'Jill'
favorit e
favorit e
nam e = 'Sara'
favorit e
favorit e
nam e = 'Bikes'
nam e = 'Derrick'
favorit e
favorit e
favorit e
To find out the possible new friends based on them liking similar things as the asking person, use a
query like this:
Query.
MATCH (me { name: 'Joe' })-[:favorite]->(stuff)<-[:favorite]-(person)
WHERE NOT (me)-[:friend]-(person)
RETURN person.name, count(stuff)
ORDER BY count(stuff) DESC
The list of possible friends ranked by them liking similar stuff that are not yet friends is returned.
Result
person.name
count(stuff)
"Derrick"
"Jill"
2 rows
52
knows
m em ber_of_group
knows
m em ber_of_group
m em ber_of_group
m em ber_of_group
In this scenario, the problem is to determine mutual friends and groups, if any, between persons. If no
mutual groups or friends are found, there should be a 0 returned.
Query.
MATCH (me { name: 'Joe' }),(other)
WHERE other.name IN ['Jill', 'Bob']
OPTIONAL MATCH pGroups=(me)-[:member_of_group]->(mg)<-[:member_of_group]-(other)
OPTIONAL MATCH pMutualFriends=(me)-[:knows]->(mf)<-[:knows]-(other)
RETURN other.name AS name, count(DISTINCT pGroups) AS mutualGroups,
count(DISTINCT pMutualFriends) AS mutualFriends
ORDER BY mutualFriends DESC
The question we are asking is how many unique paths are there between me and Jill, the paths
being common group memberships, and common friends. If the paths are mandatory, no results will
be returned if me and Bob lack any common friends, and we dont want that. To make a path optional,
you have to make at least one of its relationships optional. That makes the whole path optional.
Result
name
mutualGroups
mutualFriends
"Jill"
"Bob"
2 rows
53
favorit e
favorit e
nam e = 'Sara'
favorit e
nam e = 'Horses'
t agged
favorit e
favorit e
nam e = 'Surfing'
t agged
t agged
nam e = 'Derrick'
favorit e
favorit e
nam e = 'Bikes'
t agged
nam e = 'Hobby'
To find people similar to me based on the taggings of their favorited items, one approach could be:
Query.
MATCH
(me)-[:favorite]->(myFavorites)-[:tagged]->(tag)<-[:tagged]-(theirFavorites)<-[:favorite]-(people)
WHERE me.name = 'Joe' AND NOT me=people
RETURN people.name AS name, count(*) AS similar_favs
ORDER BY similar_favs DESC
The query returns the list of possible friends ranked by them liking similar stuff that are not yet
friends.
Result
name
similar_favs
"Sara"
"Derrick"
2 rows
54
LIKES
nam e = 'Ben'
LIKES
nam e = 'Maria'
FOLLOWS
nam e = 'Sara'
LIKES
nam e = 'cat s'
FOLLOWS
LOVES
FOLLOWS
LOVES
LIKES
FOLLOWS
FOLLOWS
nam e = 'Joe'
LIKES
nam e = 'bikes'
LIKES
This example shows a multi-relational network between persons and things they like. A multirelational graph is a graph with more than one kind of relationship between nodes.
Query.
MATCH (me { name: 'Joe' })-[r1:FOLLOWS|:LOVES]->(other)-[r2]->(me)
WHERE type(r1)=type(r2)
RETURN other.name, type(r1)
type(r1)
"Sara"
"FOLLOWS"
"Maria"
"FOLLOWS"
"Maria"
"LOVES"
3 rows
55
STATUS
nam e = 'bob_s1'
t ext = 'bobs st at us1'
dat e = 1
NEXT
nam e = 'bob_s2'
t ext = 'bobs st at us2'
dat e = 4
FRIEND
st at us = 'CONFIRMED'
FRIEND
st at us = 'CONFIRMED'
nam e = 'Alice'
STATUS
FRIEND
st at us = 'PENDING'
nam e = 'alice_s1'
t ext = 'Alices st at us1'
dat e = 2
nam e = 'Joe'
NEXT
nam e = 'alice_s2'
t ext = 'Alices st at us2'
dat e = 5
STATUS
nam e = 'joe_s1'
t ext = 'Joe st at us1'
dat e = 3
NEXT
nam e = 'joe_s2'
t ext = 'Joe st at us2'
dat e = 6
Implementation of newsfeed or timeline feature is a frequent requirement for social applications. The
following exmaples are inspired by Newsfeed feature powered by Neo4j Graph Database <http://
techfin.in/2012/10/newsfeed-feature-powered-by-neo4j-graph-database/>. The query asked here is:
Starting at me, retrieve the time-ordered status feed of the status updates of me and and all friends that
are connected via a CONFIRMED FRIEND relationship to me.
Query.
MATCH (me { name: 'Joe' })-[rels:FRIEND*0..1]-(myfriend)
WHERE ALL (r IN rels WHERE r.status = 'CONFIRMED')
WITH myfriend
MATCH (myfriend)-[:STATUS]-(latestupdate)-[:NEXT*0..1]-(statusupdates)
RETURN myfriend.name AS name, statusupdates.date AS date, statusupdates.text AS text
ORDER BY statusupdates.date DESC LIMIT 3
To understand the strategy, lets divide the query into five steps:
1. First Get the list of all my friends (along with me) through FRIEND relationship (MATCH (me {name:
'Joe'})-[rels:FRIEND*0..1]-(myfriend)). Also, the WHERE predicate can be added to check whether
the friend request is pending or confirmed.
56
date
text
"Joe"
"Joe status2"
"Bob"
"bobs status2"
"Joe"
"Joe status1"
3 rows
Here, the example shows how to add a new status update into the existing data for a user.
Query.
MATCH (me)
WHERE me.name='Bob'
OPTIONAL MATCH (me)-[r:STATUS]-(secondlatestupdate)
DELETE r
CREATE (me)-[:STATUS]->(latest_update { text:'Status',date:123 })
WITH latest_update, collect(secondlatestupdate) AS seconds
FOREACH (x IN seconds | CREATE latest_update-[:NEXT]->x)
RETURN latest_update.text AS new_status
Dividing the query into steps, this query resembles adding new item in middle of a doubly linked list:
1. Get the latest update (if it exists) of the user through the STATUS relationship (OPTIONAL MATCH (me)[r:STATUS]-(secondlatestupdate)).
2. Delete the STATUS relationship between user and secondlatestupdate (if it exists), as this would
become the second latest update now and only the latest update would be added through a STATUS
relationship; all earlier updates would be connected to their subsequent updates through a NEXT
relationship. (DELETE r).
3. Now, create the new statusupdate node (with text and date as properties) and connect
this with the user through a STATUS relationship (CREATE me-[:STATUS]->(latest_update
{ text:'Status',date:123 })).
4. Pipe over statusupdate or an empty collection to the next query part (WITH latest_update,
collect(secondlatestupdate) AS seconds).
5. Now, create a NEXT relationship between the latest status update and the second latest status update
(if it exists) (FOREACH(x in seconds | CREATE latest_update-[:NEXT]->x)).
57
1 row
Nodes created: 1
Relationships created: 2
Properties set: 2
Relationships deleted: 1
STATUS
NEXT
58
KNOWS
weight = 4
KNOWS
weight = 4
KNOWS
weight = 4
WORKS_AT
weight = 2
act ivit y = 56
KNOWS
weight = 4
WORKS_AT
weight = 2
act ivit y = 10
WORKS_AT
weight = 2
act ivit y = 2
WORKS_AT
weight = 2
act ivit y = 3
nam e = 'CNN'
WORKS_AT
weight = 2
act ivit y = 45
KNOWS
weight = 4
WORKS_AT
weight = 2
act ivit y = 6
This query finds the recommended friends for the origin that are working at the same place as the
origin, or know a person that the origin knows, also, the origin should not already know the target.
This recommendation is weighted for the weight of the relationship r2, and boosted with a factor of 2,
if there is an activity-property on that relationship
Query.
MATCH (origin)-[r1:KNOWS|WORKS_AT]-(c)-[r2:KNOWS|WORKS_AT]-(candidate)
WHERE origin.name = "Clark Kent" AND type(r1)=type(r2) AND NOT (origin)-[:KNOWS]-(candidate)
RETURN origin.name AS origin, candidate.name AS candidate, SUM(ROUND(r2.weight
+(COALESCE(r2.activity,
0)* 2))) AS boost
ORDER BY boost DESC LIMIT 10
This returns the recommended friends for the origin nodes and their recommendation score.
Result
origin
candidate
boost
"Clark Kent"
"Perry White"
22
"Clark Kent"
"Anderson Cooper"
2 rows
59
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
In this example, adapted from Niko Gamulins blog post on Neo4j for Social Network Analysis
<http://mypetprojects.blogspot.se/2012/06/social-network-analysis-with-neo4j.html>, the graph in
question is showing the 2-hop relationships of a sample person as nodes with KNOWS relationships.
The clustering coefficient <http://en.wikipedia.org/wiki/Clustering_coefficient> of a selected node
is defined as the probability that two randomly selected neighbors are connected to each other. With
the number of neighbors as n and the number of mutual connections between the neighbors r the
calculation is:
The number of possible connections between two neighbors is n!/(2!(n-2)!) = 4!/(2!(4-2)!) = 24/4 =
6, where n is the number of neighbors n = 4 and the actual number r of connections is 1. Therefore the
clustering coefficient of node 1 is 1/6.
and r are quite simple to retrieve via the following query:
Query.
MATCH (a { name: "startnode" })--(b)
WITH a, count(DISTINCT b) AS n
MATCH (a)--()-[r]-()--(a)
RETURN n, count(DISTINCT r) AS r
1 row
60
5.14.Pretty graphs
This section is showing how to create some of the named pretty graphs on Wikipedia <http://
en.wikipedia.org/wiki/Gallery_of_named_graphs>.
5.14.1.Star graph
The graph is created by first creating a center node, and then once per element in the range, creates a
leaf node and connects it to the center.
Query.
CREATE (center)
FOREACH (x IN range(1,6)| CREATE (leaf),(center)-[:X]->(leaf))
RETURN id(center) AS id;
1 row
Nodes created: 7
Relationships created: 6
Figure5.11.Graph
X
X
X
X
X
X
5.14.2.Wheel graph
This graph is created in a number of steps:
1 row
Nodes created: 7
Relationships created: 12
Properties set: 6
Figure5.12.Graph
count = 4
X
count = 3
X
X
X
count = 5
X
X
count = 2
X
X
count = 6
X
count = 1
5.14.3.Complete graph
For this graph, a root node is created, and used to hang a number of nodes from. Then, two nodes are
selected, hanging from the center, with the requirement that the id of the first is less than the id of the
next. This is to prevent double relationships and self relationships. Using said match, relationships
between all these nodes are created. Lastly, the center node and all relationships connected to it are
removed.
Query.
CREATE (center)
62
Nodes created: 7
Relationships created: 21
Properties set: 6
Nodes deleted: 1
Relationships deleted: 6
Figure5.13.Graph
count = 4
count = 5
X
X
count = 3
X
X
count = 2
count = 6
count = 1
5.14.4.Friendship graph
This query first creates a center node, and then once per element in the range, creates a cycle graph
and connects it to the center
Query.
CREATE (center)
FOREACH (x IN range(1,3)| CREATE (leaf1),(leaf2),(center)-[:X]->(leaf1),(center)-[:X]->(leaf2),
(leaf1)-[:X]->(leaf2))
RETURN ID(center) AS id
1 row
Nodes created: 7
Relationships created: 9
Figure5.14.Graph
X
X
X
X
X
X
X
X
64
2010
2011
Year 2010
Year 2011
12
01
Mont h 12
Mont h 01
31
Day 31
VALUE
01
NEXT
VALUE
Event 1
Day 01
02
NEXT
VALUE
03
Day 02
NEXT
Day 03
VALUE
Event 2
Event 3
2010
2011
Year 2010
Year 2011
12
01
Mont h 12
Mont h 01
31
Day 31
VALUE
Event 1
01
NEXT
Day 01
VALUE
02
NEXT
03
Day 02
NEXT
VALUE
Day 03
VALUE
Event 2
Event 3
Query.
MATCH rootPath=(root)-[:`2010`]->()-[:`12`]->()-[:`31`]->(leaf),(leaf)-[:VALUE]->(event)
WHERE root.name = 'Root'
RETURN event.name
ORDER BY event.name ASC
Returning all events on the date 2010-12-31, in this case Event1 and Event2
Result
event.name
"Event1"
"Event2"
2 rows
66
2010
2011
Year 2010
Year 2011
12
01
Mont h 12
Mont h 01
31
Day 31
VALUE
Event 1
01
NEXT
VALUE
Day 01
02
NEXT
03
Day 02
NEXT
VALUE
Day 03
VALUE
Event 2
Event 3
Query.
MATCH startPath=(root)-[:`2010`]->()-[:`12`]->()-[:`31`]->(startLeaf),
endPath=(root)-[:`2011`]->()-[:`01`]->()-[:`03`]->(endLeaf),
valuePath=(startLeaf)-[:NEXT*0..]->(middle)-[:NEXT*0..]->(endLeaf),
vals=(middle)-[:VALUE]->(event)
WHERE root.name = 'Root'
RETURN event.name
ORDER BY event.name ASC
Returning all events between 2010-12-31 and 2011-01-03, in this case all events.
Result
event.name
"Event1"
"Event2"
"Event2"
"Event3"
4 rows
67
2010
2011
Year 2010
Year 2011
12
01
Mont h 12
Mont h 01
31
Day 31
VALUE
Event 1
01
NEXT
VALUE
Day 01
02
NEXT
03
Day 02
NEXT
VALUE
Day 03
VALUE
Event 2
Event 3
Query.
MATCH commonPath=(root)-[:`2011`]->()-[:`01`]->(commonRootEnd),
startPath=(commonRootEnd)-[:`01`]->(startLeaf), endPath=(commonRootEnd)-[:`03`]->(endLeaf),
valuePath=(startLeaf)-[:NEXT*0..]->(middle)-[:NEXT*0..]->(endLeaf),
vals=(middle)-[:VALUE]->(event)
WHERE root.name = 'Root'
RETURN event.name
ORDER BY event.name ASC
Returning all events between 2011-01-01 and 2011-01-03, in this case Event2 and Event3.
Result
event.name
"Event2"
2 rows
68
2 rows
69
1 row
Figure5.19.Graph
nam e = 'm e'
nam e = 'you'
ATE
t im es = 10
70
ATE
t im es = 5
Tip
This approach of course makes excessive use of relationship types. This needs to be
taken into consideration when designing a production system with this approach. See
Section15.5, Capacity for the maximum number of relationship types.
To find the activity stream for a person, just follow the linked list of the friend list, and retrieve the
needed amount of activities form the respective activity list of the friends.
Query.
MATCH p=(me { name: 'Jane' })-[:jane_knows*]->(friend),(friend)-[:has]->(status)
RETURN me.name, friend.name, status.name, length(p)
ORDER BY length(p)
friend.name
status.name
length(p)
"Jane"
"Bill"
"Bill_s1"
"Jane"
"Joe"
"Joe_s1"
"Jane"
"Bob"
"Bob_s1"
3 rows
71
jane_knows
nam e = 'Bill'
has
jane_knows
nam e = 'Bill_s1'
nam e = 'Joe'
next
nam e = 'Bill_s2'
has
nam e = 'Joe_s1'
jane_knows
bob_knows
nam e = 'Bob'
next
has
nam e = 'Joe_s2'
nam e = 'Bob_s1'
bob_knows
nam e = 'Ted'
has
nam e = 'Ted_s1'
next
nam e = 'Ted_s2'
72
This is an example showing a hierarchy of roles. Whats interesting is that a tree is not sufficient for
storing this kind of structure, as elaborated below.
This is an implementation of an example found in the article A Model to Represent Directed Acyclic
Graphs (DAG) on SQL Databases <http://www.codeproject.com/Articles/22824/A-Model-toRepresent-Directed-Acyclic-Graphs-DAG-o> by Kemal Erdogan <http://www.codeproject.com/
script/Articles/MemberArticles.aspx?amid=274518>. The article discusses how to store directed
acyclic graphs <http://en.wikipedia.org/wiki/Directed_acyclic_graph> (DAGs) in SQL based DBs.
DAGs are almost trees, but with a twist: it may be possible to reach the same node through different
paths. Trees are restricted from this possibility, which makes them much easier to handle. In our case
it is Ali and Engin, as they are both admins and users and thus reachable through these group
nodes. Reality often looks this way and cant be captured by tree structures.
In the article an SQL Stored Procedure solution is provided. The main idea, that also have some
support from scientists, is to pre-calculate all possible (transitive) paths. Pros and cons of this
approach:
In Neo4j storing the roles is trivial. In this case we use PART_OF (green edges) relationships to model
the group hierarchy and MEMBER_OF (blue edges) to model membership in groups. We also connect the
top level groups to the reference node by ROOT relationships. This gives us a useful partitioning of the
graph. Neo4j has no predefined relationship types, you are free to create any relationship types and
give them the semantics you want.
Lets now have a look at how to retrieve information from the graph. The the queries are done using
Cypher, the Java code is using the Neo4j Traversal API (see Section33.2, Traversal Framework Java
API, which is part of PartVIII, Advanced Usage).
73
resulting in:
user.name
group.name
"Demet"
"HelpDesk"
"Ali"
"Admins"
"Engin"
"HelpDesk"
3 rows
And heres the code when using the Java Traversal API:
Node admins = getNodeByName( "Admins" );
TraversalDescription traversalDescription = db.traversalDescription()
.breadthFirst()
.evaluator( Evaluators.excludeStartPosition() )
.relationships( RoleRels.PART_OF, Direction.INCOMING )
.relationships( RoleRels.MEMBER_OF, Direction.INCOMING );
Traverser traverser = traversalDescription.traverse( admins );
HelpDesk at depth: 0
Ali at depth: 0
Engin at depth: 1
Demet at depth: 1
group.name
"ABCTechnicians"
"Technicians"
"Users"
3 rows
Using the Neo4j Java Traversal API, this query looks like:
Node jale = getNodeByName( "Jale" );
74
resulting in:
Found: ABCTechnicians at depth: 0
Found: Technicians at depth: 1
Found: Users at depth: 2
group.name
"Users"
"ABCTechnicians"
"Managers"
"HelpDesk"
"Technicians"
"Admins"
6 rows
In Java:
Node referenceNode = getNodeByName( "Reference_Node") ;
traversalDescription = db.traversalDescription()
.breadthFirst()
.evaluator( Evaluators.excludeStartPosition() )
.relationships( RoleRels.ROOT, Direction.INCOMING )
.relationships( RoleRels.PART_OF, Direction.INCOMING );
traverser = traversalDescription.traverse( referenceNode );
resulting in:
Found:
Found:
Found:
Found:
Found:
Found:
Admins at depth: 0
Users at depth: 0
HelpDesk at depth: 1
Managers at depth: 1
Technicians at depth: 1
ABCTechnicians at depth: 2
min(length(p))
"Ali"
"Burcu"
"Can"
"Engin"
"Demet"
"Fuat"
"Gul"
"Hakan"
"Irmak"
"Jale"
10 rows
in Java:
traversalDescription = db.traversalDescription()
.breadthFirst()
.evaluator(
Evaluators.includeWhereLastRelationshipTypeIs( RoleRels.MEMBER_OF ) );
traverser = traversalDescription.traverse( referenceNode );
Found:
Found:
Found:
Found:
Found:
Found:
Found:
Found:
Found:
Found:
Ali at depth: 1
Engin at depth: 1
Burcu at depth: 1
Can at depth: 1
Demet at depth: 2
Gul at depth: 2
Fuat at depth: 2
Hakan at depth: 2
Irmak at depth: 2
Jale at depth: 3
As seen above, querying even more complex scenarios can be done using comparatively short
constructs in Cypher or Java.
76
Chapter6.Languages
Please see http://www.neo4j.org/drivers for the current set of drivers!
Theres an included Java example which shows a low-level approach to using the Neo4j REST API
from Java.
77
Languages
If the status of the response is 200 OK, then we know the server is running fine and we can continue. If
the code fails to conenct to the server, then please have a look at PartV, Operations.
Note
If you get any other response than 200 OK (particularly 4xx or 5xx responses) then please
check your configuration and look in the log files in the data/log directory.
6.1.3.Creating a node
The REST API uses POST to create nodes. Encapsulating that in Java is straightforward using the
Jersey client:
final String nodeEntryPointUri = SERVER_ROOT_URI + "node";
// http://localhost:7474/db/data/node
WebResource resource = Client.create()
.resource( nodeEntryPointUri );
// POST {} to the node entry point URI
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.entity( "{}" )
.post( ClientResponse.class );
final URI location = response.getLocation();
System.out.println( String.format(
"POST to [%s], status code [%d], location header [%s]",
nodeEntryPointUri, response.getStatus(), location.toString() ) );
response.close();
return location;
If the call completes successfully, under the covers it will have sent a HTTP request containing
a JSON payload to the server. The server will then have created a new node in the database and
responded with a 201 Created response and a Location header with the URI of the newly created node.
78
Languages
In our example, we call this functionality twice to create two nodes in our database.
6.1.4.Adding properties
Once we have nodes in our datatabase, we can use them to store useful data. In this case, were going
to store information about music in our database. Lets start by looking at the code that we use to
create nodes and add properties. Here weve added nodes to represent "Joe Strummer" and a band
called "The Clash".
URI firstNode = createNode();
addProperty( firstNode, "name", "Joe Strummer" );
URI secondNode = createNode();
addProperty( secondNode, "band", "The Clash" );
Inside the addProperty method we determine the resource that represents properties for the node and
decide on a name for that property. We then proceed to PUT the value of that property to the server.
String propertyUri = nodeUri.toString() + "/properties/" + propertyName;
// http://localhost:7474/db/data/node/{node_id}/properties/{property_name}
WebResource resource = Client.create()
.resource( propertyUri );
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.entity( "\"" + propertyValue + "\"" )
.put( ClientResponse.class );
System.out.println( String.format( "PUT to [%s], status code [%d]",
propertyUri, response.getStatus() ) );
response.close();
If everything goes well, well get a 204 No Content back indicating that the server processed the request
but didnt echo back the property value.
6.1.5.Adding relationships
Now that we have nodes to represent Joe Strummer and The Clash, we can relate them. The REST
API supports this through a POST of a relationship representation to the start node of the relationship.
Correspondingly in Java we POST some JSON to the URI of our node that represents Joe Strummer, to
establish a relationship between that node and the node representing The Clash.
URI relationshipUri = addRelationship( firstNode, secondNode, "singer",
"{ \"from\" : \"1976\", \"until\" : \"1986\" }" );
Inside the addRelationship method, we determine the URI of the Joe Strummer nodes relationships,
and then POST a JSON description of our intended relationship. This description contains the
destination node, a label for the relationship type, and any attributes for the relation as a JSON
collection.
private static URI addRelationship( URI startNode, URI endNode,
String relationshipType, String jsonAttributes )
throws URISyntaxException
{
URI fromUri = new URI( startNode.toString() + "/relationships" );
String relationshipJson = generateJsonRelationship( endNode,
relationshipType, jsonAttributes );
WebResource resource = Client.create()
.resource( fromUri );
// POST JSON to the relationships URI
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON )
79
Languages
.type( MediaType.APPLICATION_JSON )
.entity( relationshipJson )
.post( ClientResponse.class );
final URI location = response.getLocation();
System.out.println( String.format(
"POST to [%s], status code [%d], location header [%s]",
fromUri, response.getStatus(), location.toString() ) );
response.close();
return location;
}
If all goes well, we receive a 201 Created status code and a Location header which contains a URI of
the newly created relation.
Inside the addMetadataToProperty method, we determine the URI of the properties of the relationship
and PUT our new values (since its PUT it will always overwrite existing values, so be careful).
private static void addMetadataToProperty( URI relationshipUri,
String name, String value ) throws URISyntaxException
{
URI propertyUri = new URI( relationshipUri.toString() + "/properties" );
String entity = toJsonNameValuePairCollection( name, value );
WebResource resource = Client.create()
.resource( propertyUri );
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.entity( entity )
.put( ClientResponse.class );
System.out.println( String.format(
"PUT [%s] to [%s], status code [%d]", entity, propertyUri,
response.getStatus() ) );
response.close();
}
Assuming all goes well, well get a 204 OK response back from the server (which we can check by
calling ClientResponse.getStatus()) and weve now established a very small graph that we can query.
6.1.7.Querying graphs
As with the embedded version of the database, the Neo4j server uses graph traversals to look for data
in graphs. Currently the Neo4j server expects a JSON payload describing the traversal to be POST-ed at
the starting node for the traversal (though this is likely to change in time to a GET-based approach).
To start this process, we use a simple class that can turn itself into the equivalent JSON, ready for
POST-ing to the server, and in this case weve hardcoded the traverser to look for all nodes with
outgoing relationships with the type "singer".
// TraversalDefinition turns into JSON to send to the Server
TraversalDefinition t = new TraversalDefinition();
t.setOrder( TraversalDefinition.DEPTH_FIRST );
t.setUniqueness( TraversalDefinition.NODE );
80
Languages
t.setMaxDepth( 10 );
t.setReturnFilter( TraversalDefinition.ALL );
t.setRelationships( new Relation( "singer", Relation.OUT ) );
Once we have defined the parameters of our traversal, we just need to transfer it. We do this by
determining the URI of the traversers for the start node, and then POST-ing the JSON representation of
the traverser to it.
URI traverserUri = new URI( startNode.toString() + "/traverse/node" );
WebResource resource = Client.create()
.resource( traverserUri );
String jsonTraverserPayload = t.toJson();
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.entity( jsonTraverserPayload )
.post( ClientResponse.class );
System.out.println( String.format(
"POST [%s] to [%s], status code [%d], returned data: "
+ System.getProperty( "line.separator" ) + "%s",
jsonTraverserPayload, traverserUri, response.getStatus(),
response.getEntity( String.class ) ) );
response.close();
Once that request has completed, we get back our dataset of singers and the bands they belong to:
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/82/relationships/out",
"data" : {
"band" : "The Clash",
"name" : "Joe Strummer"
},
"traverse" : "http://localhost:7474/db/data/node/82/traverse/{returnType}",
"all_typed_relationships" : "http://localhost:7474/db/data/node/82/relationships/all/{-list|&|types}",
"property" : "http://localhost:7474/db/data/node/82/properties/{key}",
"all_relationships" : "http://localhost:7474/db/data/node/82/relationships/all",
"self" : "http://localhost:7474/db/data/node/82",
"properties" : "http://localhost:7474/db/data/node/82/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/82/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/82/relationships/in",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/82/relationships/in/{-list|&|types}",
"create_relationship" : "http://localhost:7474/db/data/node/82/relationships"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/83/relationships/out",
"data" : {
},
"traverse" : "http://localhost:7474/db/data/node/83/traverse/{returnType}",
"all_typed_relationships" : "http://localhost:7474/db/data/node/83/relationships/all/{-list|&|types}",
"property" : "http://localhost:7474/db/data/node/83/properties/{key}",
"all_relationships" : "http://localhost:7474/db/data/node/83/relationships/all",
"self" : "http://localhost:7474/db/data/node/83",
"properties" : "http://localhost:7474/db/data/node/83/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/83/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/83/relationships/in",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/83/relationships/in/{-list|&|types}",
"create_relationship" : "http://localhost:7474/db/data/node/83/relationships"
} ]
Languages
6.1.9.Whats next?
The HTTP API provides a good basis for implementers of client libraries, its also great for HTTP
and REST folks. In the future though we expect that idiomatic language bindings will appear to take
advantage of the REST API while providing comfortable language-level constructs for developers to
use, much as there are similar bindings for the embedded database.
82
The Cypher part is the authoritative source for details on the Cypher Query Language. For an
introduction, see Section7.1, What is Cypher?.
Chapter7.Introduction
To get an overview of Cypher, continue reading Section7.1, What is Cypher?. The rest of this
chapter deals with the context of Cypher statements, like for example transaction management and
how to use parameters. For the Cypher language reference itself see other chapters at PartIII, Cypher
Query Language.
84
Introduction
7.1.What is Cypher?
7.1.1.Introduction
Cypher is a declarative graph query language that allows for expressive and efficient querying and
updating of the graph store. Cypher is a relatively simple but still very powerful language. Very
complicated database queries can easily be expressed through Cypher. This allows you to focus on
your domain instead of getting lost in database access.
Cypher is designed to be a humane query language, suitable for both developers and (importantly, we
think) operations professionals. Our guiding goal is to make the simple things easy, and the complex
things possible. Its constructs are based on English prose and neat iconography which helps to make
queries more self-explanatory. We have tried to optimize the language for reading and not for writing.
Being a declarative language, Cypher focuses on the clarity of expressing what to retrieve from
a graph, not on how to retrieve it. This is in contrast to imperative languages like Java, scripting
languages like Gremlin <http://gremlin.tinkerpop.com>, and the JRuby Neo4j bindings <http://
neo4j.rubyforge.org/>. This approach makes query optimization an implementation detail instead of
burdening the user with it and requiring her to update all traversals just because the physical database
structure has changed (new indexes etc.).
Cypher is inspired by a number of different approaches and builds upon established practices for
expressive querying. Most of the keywords like WHERE and ORDER BY are inspired by SQL <http://
en.wikipedia.org/wiki/SQL>. Pattern matching borrows expression approaches from SPARQL
<http://en.wikipedia.org/wiki/SPARQL>. Some of the collection semantics have been borrowed from
languages such as Haskell and Python.
7.1.2.Structure
Cypher borrows it structure from SQL queries are built up using various clauses.
Clauses are chained together, and the they feed intermediate result sets between each other. For
example, the matching identifiers from one MATCH clause will be the context that the next clause exists
in.
The query language is comprised of several distinct clauses. You can read more details about them
later in the manual.
Here are a few clauses used to read from the graph:
MATCH: The graph pattern to match. This is the most common way to get data from the graph.
WHERE: Not a clause in its own right, but rather part of MATCH, OPTIONAL MATCH and WITH. Adds
constraints to a pattern, or filters the intermediate result passing through WITH.
RETURN: What to return.
Lets see MATCH and RETURN in action.
Imagine an example graph like the following one:
85
Introduction
Figure7.1.Example Graph
Node[ 3] nam e = 'John'
friend
friend
friend
friend
For example, here is a query which finds a user called John and Johns friends (though not his direct
friends) before returning both John and any friends-of-friends that are found.
MATCH (john {name: 'John'})-[:friend]->()-[:friend]->(fof)
RETURN john, fof
Resulting in:
john
fof
Node[3]{name:"John"}
Node[1]{name:"Maria"}
Node[3]{name:"John"}
Node[2]{name:"Steve"}
2 rows
Next up we will add filtering to set more parts in motion:
We take a list of user names and find all nodes with names from this list, match their friends and
return only those followed users who have a name property starting with S.
MATCH (user)-[:friend]->(follower)
WHERE user.name IN ['Joe', 'John', 'Sara', 'Maria', 'Steve'] AND follower.name =~ 'S.*'
RETURN user, follower.name
Resulting in:
user
follower.name
Node[3]{name:"John"}
"Sara"
Node[4]{name:"Joe"}
"Steve"
2 rows
And here are examples of clauses that are used to update the graph:
CREATE (and DELETE): Create (and delete) nodes and relationships.
SET (and REMOVE): Set values to properties and add labels on nodes using SET and use REMOVE to
remove them.
86
Introduction
MERGE: Match existing or create new nodes and patterns. This is especially useful together with
uniqueness constraints.
For more Cypher examples, see Chapter5, Data Modeling Examples as well as the rest of the Cypher
part with details on the language. To use Cypher from Java, see Section32.12, Execute Cypher
Queries from Java.
87
Introduction
Using WITH, you specify how you want the aggregation to happen, and that the aggregation has to be
finished before Cypher can start filtering.
You can chain together as many query parts as you have JVM heap for.
7.2.2.Returning data
Any query can return data. If your query only reads, it has to return data it serves no purpose if it
doesnt, and it is not a valid Cypher query. Queries that update the graph dont have to return anything,
but they can.
After all the parts of the query comes one final RETURN clause. RETURN is not part of any query part it
is a period symbol at the end of a query. The RETURN clause has three sub-clauses that come with it
SKIP/LIMIT and ORDER BY.
If you return graph elements from a query that has just deleted them beware, you are holding a
pointer that is no longer valid. Operations on that node might fail mysteriously and unpredictably.
88
Introduction
7.3.Transactions
Any query that updates the graph will run in a transaction. An updating query will always either fully
succeed, or not succeed at all.
Cypher will either create a new transaction or run inside an existing one:
If no transaction exists in the running context Cypher will create one and commit it once the query
finishes.
In case there already exists a transaction in the running context, the query will run inside it, and
nothing will be persisted to disk until that transaction is successfully committed.
This can be used to have multiple queries be committed as a single transaction:
1. Open a transaction,
2. run multiple updating Cypher queries,
3. and commit all of them in one go.
Note that a query will hold the changes in memory until the whole query has finished executing. A
large query will consequently need a JVM with lots of heap space.
For using transactions over the REST API, see Section19.1, Transactional HTTP endpoint.
When using Neo4j embedded, remember all iterators returned from an execution result should be
exhausted fully to ensure that resources bound to them will be properly closed. Resources include
transactions started by the query, so failing to do so may, for example, lead to deadlocks or other
weird behavior.
89
Introduction
7.4.Uniqueness
While pattern matching, Cypher makes sure to not include matches where the same graph relationship
is found multiple times in a single pattern. In most use cases, this is a sensible thing to do.
Example: looking for a users friends of friends should not return said user.
Lets create a few nodes and relationships:
CREATE (adam:User { name: 'Adam' }),(pernilla:User { name: 'Pernilla' }),(david:User { name: 'David'
}),
(adam)-[:FRIEND]->(pernilla),(pernilla)-[:FRIEND]->(david)
FRIEND
User
nam e = 'Pernilla'
FRIEND
User
nam e = 'David'
friend_of_a_friend
Node[2]{name:"David"}
1 row
In this query, Cypher makes sure to not return matches where the pattern relationships r1 and r2 point
to the same graph relationship.
This is however not always desired. If the query should return the user, it is possible to spread the
matching over multiple MATCH clauses, like so:
MATCH (user:User { name: 'Adam' })-[r1:FRIEND]-(friend)
WITH friend
MATCH (friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend
friend_of_a_friend
Node[2]{name:"David"}
2 rows
90
Introduction
friend_of_a_friend
Node[0]{name:"Adam"}
2 rows
91
Introduction
7.5.Parameters
Cypher supports querying with parameters. This means developers dont have to resort to string
building to create a query. In addition to that, it also makes caching of execution plans much easier for
Cypher.
Parameters can be used for literals and expressions in the WHERE clause, for the index value in the
START clause, index queries, and finally for node/relationship ids. Parameters can not be used as for
property names, relationship types and labels, since these patterns are part of the query structure that is
compiled into a query plan.
Accepted names for parameters are letters and numbers, and any combination of these.
For details on parameters when using the Neo4j embedded Java API, see Section32.13, Query
Parameters. For details on using parameters via the Neo4j REST API, see Section19.5, Cypher
queries via REST.
Below follows a comprehensive set of examples of parameter usage. The parameters are given as
JSON here. Exactly how to submit them depends on the driver in use.
7.5.1.String literal
Parameters.
{
"name" : "Johan"
}
Query.
MATCH (n)
WHERE n.name = { name }
RETURN n
7.5.2.Regular expression
Parameters.
{
"regex" : ".*h.*"
}
Query.
MATCH (n)
WHERE n.name =~ { regex }
RETURN n.name
Query.
CREATE ({ props })
92
Introduction
Query.
CREATE (n:Person { props })
RETURN n
Query.
MATCH (n)
WHERE n.name='Michaela'
SET n = { props }
Query.
MATCH (n)
RETURN n.name
SKIP { s }
LIMIT { l }
7.5.7.Node id
Parameters.
{
"id" : 0
}
Query.
START n=node({ id })
93
Introduction
RETURN n.name
Query.
START n=node({ id })
RETURN n.name
Query.
START n=node:people(name = { value })
RETURN n
Query.
START n=node:people({ query })
RETURN n
94
Introduction
7.6.Compatibility
Cypher is still changing rather rapidly. Parts of the changes are internal we add new pattern
matchers, aggregators and other optimizations, which hopefully makes your queries run faster.
Other changes are directly visible to our users the syntax is still changing. New concepts are being
added and old ones changed to fit into new possibilities. To guard you from having to keep up with
our syntax changes, Cypher allows you to use an older parser, but still gain the speed from new
optimizations.
There are two ways you can select which parser to use. You can configure your database with the
configuration parameter cypher_parser_version, and enter which parser youd like to use (1.9, 2.0 are
supported now). Any Cypher query that doesnt explicitly say anything else, will get the parser you
have configured.
The other way is on a query by query basis. By simply putting "CYPHER 1.9" at the beginning, that
particular query will be parsed with the 1.9 version of the parser. Example:
CYPHER 1.9 START n=node(0)
WHERE n.foo = "bar"
RETURN n
95
Introduction
7.7.Query Performance
Cypher works very hard to execute queries as fast as possible.
However, when optimizing for maximum query execution performance, it may be helpful to rephrase
queries using knowledge about the domain and the application.
The overall goal of manual query performance optimization is to ensure that only necessary data is
retrieved from the graph. At least data should get filtered out as early as possible in order to reduce
the amount of work that has to be done at later stages of query execution. This also goes for what gets
returned: avoid returning whole nodes and relationships instead, pick the data you need and return
only that. You should also make sure to set an upper limit on variable length patterns, so they dont
cover larger portions of the dataset than needed.
Each Cypher query gets optimized and transformed into an execution plan by the Cypher execution
engine. To minimize the resources used for this, make sure to use parameters instead of literals
when possible. This allows Cypher to re-use your queries instead of having to parse and build new
execution plans.
Note
When Cypher is building execution plans, it looks at the schema to see if it can find useful
indexes. These index decisions are only valid until the schema changes, so adding or
removing indexes leads to the execution plan cache being flushed.
96
Chapter8.Syntax
The nitty-gritty details of Cypher syntax.
97
Syntax
8.1.Operators
8.1.1.Mathematical operators
The mathematical operators are +, -, *, / and %, ^.
8.1.2.Comparison operators
The comparison operators are =, <>, <, >, <=, >=.
8.1.3.Boolean operators
The boolean operators are AND, OR, XOR, NOT.
8.1.4.String operators
Strings can be concatenated using the + operator.
8.1.5.Collection operators
Collections can be concatenated using the + operator. To check if an element exists in a collection, you
can use the IN operator.
8.1.6.Property operators
Note
Since version 2.0, the previously existing property operators ? and ! have been removed.
This syntax is no longer supported. Missing properties are now returned as null. Please use
(NOT(has(<ident>.prop)) OR <ident>.prop=<value>) if you really need the old behavior of the
? operator. Also, the use of ? for optional relationships has been removed in favor of the
newly introduced OPTIONAL MATCH clause.
98
Syntax
8.2.Expressions
8.2.1.Expressions in general
An expression in Cypher can be:
a CASE expression
Tab
\b
Backspace
\n
Newline
\r
Carriage return
\f
Form feed
\'
Single quote
\"
Double quote
\\
Backslash
8.2.3.Case Expressions
Cypher supports CASE expressions, which is a generic conditional expression, similar to if/else
statements in other languages. Two variants of CASE exist the simple form and the generic form.
8.2.4.Simple CASE
The expression is calculated, and compared in order with the WHEN clauses until a match is found. If no
match is found the expression in the ELSE clause is used, or null, if no ELSE case exists.
Syntax:
CASE test
WHEN value THEN result
99
Syntax
[WHEN ...]
[ELSE default]
END
Arguments:
Query.
MATCH n
RETURN CASE n.eyes
WHEN 'blue'
THEN 1
WHEN 'brown'
THEN 2
ELSE 3 END AS result
Result
result
2
1
2
1
3
5 rows
8.2.5.Generic CASE
The predicates are evaluated in order until a true value is found, and the result value is used. If no
match is found the expression in the ELSE clause is used, or null, if no ELSE case exists.
Syntax:
CASE
WHEN predicate THEN result
[WHEN ...]
[ELSE default]
END
Arguments:
predicate: A predicate that is tested to find a valid alternative.
result: This is the result expression used if the predicate matches.
default: The expression to use if no match is found.
Query.
MATCH n
RETURN CASE
WHEN n.eyes = 'blue'
THEN 1
100
Syntax
WHEN n.age < 40
THEN 2
ELSE 3 END AS result
Result
result
3
1
2
1
3
5 rows
101
Syntax
8.3.Identifiers
When you reference parts of the pattern, you do so by naming them. The names you give the different
parts are called identifiers.
In this example:
MATCH (n)-->(b) RETURN b
102
Syntax
8.4.Comments
To add comments to your queries, use double slash. Examples:
MATCH (n) RETURN n //This is an end of line comment
MATCH (n)
//This is a whole line comment
RETURN n
MATCH (n) WHERE n.property = "//This is NOT a comment" RETURN n
103
Syntax
8.5.Patterns
Patterns and pattern-matching are at the very heart of Cypher, so being effective with Cypher requires
a good understanding of patterns.
Using patterns, you describe the shape of the data youre looking for. For example, in the MATCH clause
you describe the shape with a pattern, and Cypher will figure out how to get that data for you.
The pattern describes the data using a form that is very similar to how one typically draws the shape
of property graph data on a whiteboard: usually as circles (representing nodes) and arrows between
them to represent relationships.
Patterns appear in multiple places in Cypher: in MATCH, CREATE and MERGE clauses, and in pattern
expressions. Each of these is described in more details in:
Section10.1, Match
Section10.2, Optional Match
Section11.1, Create
Section11.3, Merge
Section10.3.3, Using patterns in WHERE
This simple pattern describes a single node, and names that node using the identifier a.
Note that the parentheses may be omitted, but only when there are no labels or properties specified for
the node pattern.
This pattern describes a very simple data shape: two nodes, and a single relationship from one to the
other. In this example, the two nodes are both named as a and b respectively, and the relationship is
directed: it goes from a to b.
This way of describing nodes and relationships can be extended to cover an arbitrary number of nodes
and the relationships between them, for example:
(a)-->(b)<--(c)
104
Syntax
8.5.3.Labels
In addition to simply describing the shape of a node in the pattern, one can also describe attributes.
The most simple attribute that can be described in the pattern is a label that the node must have. For
example:
(a:User)-->(b)
8.5.4.Specifying properties
Nodes and relationships are the fundamental structures in a graph. Neo4j uses properties on both of
these to allow for far richer models.
Properties can be expressed in patterns using a map-construct: curly brackets surrounding a number of
key-expression pairs, separated by commas. E.g. a node with two properties on it would look like: (a
{ name: "Andres", sport: "Brazilian Ju-Jitsu" }).
8.5.5.Describing relationships
The simplest way to describe a relationship is by using the arrow between two nodes, as in the
previous examples. Using this technique, you can describe that the relationship should exist and the
directionality of it. If you dont care about the direction of the relationship, the arrow head can be
omitted, like so:
(a)--(b)
As with nodes, relationships may also be given names. In this case, a pair of square brackets is used to
break up the arrow and the identifier is placed between. For example:
(a)-[r]->(b)
Much like labels on nodes, relationships can have types. To describe a relationship with a specific
type, you can specify this like so:
(a)-[r:REL_TYPE]->(b)
Unlike labels, relationships can only have one type. But if wed like to describe some data such
that the relationship could have any one of a set of types, then they can all be listed in the pattern,
separating them with the pipe symbol | like this:
(a)-[r:TYPE1|TYPE2]->(b)
Note that this form of pattern can only be used to describe existing data (ie. when using a pattern
with MATCH or as an expression). It will not work with CREATE or MERGE, since its not possible to create a
relationship with multiple types.
105
Syntax
As with nodes, the name of the relationship can always be omitted, in this case like so:
(a)-[:REL_TYPE]->(b)
Variable length
Rather than describing a long path using a sequence of many node and relationship descriptions in a
pattern, many relationships (and the intermediate nodes) can be described by specifying a length in the
relationship description of a pattern. For example:
(a)-[*2]->(b)
This describes a graph of three nodes and two relationship, all in one path (a path of length 2). This is
equivalent to:
(a)-->()-->(b)
A range of lengths can also be specified: such relationship patterns are called variable length
relationships. For example:
(a)-[*3..5]->(b)
This is a minimum length of 3, and a maximum of 5. It describes a graph of either 4 nodes and 3
relationships, 5 nodes and 4 relationships or 6 nodes and 5 relationships, all connected together in a
single path.
Either bound can be omitted. For example, to describe paths of length 3 or more, use:
(a)-[*3..]->(b)
Result
remote_friend.name
"Dilshad"
"Anders"
2 rows
This query finds data in the graph which a shape that fits the pattern: specifically a node (with the
name property Filipa) and then the KNOWS related nodes, one or two steps out. This is a typical example
of finding first and second degree friends.
Note that variable length relationships can not be used with CREATE and MERGE.
Syntax
p = (a)-[*3..5]->(b)
You can do this in MATCH, CREATE and MERGE, but not when using patterns as expressions.
107
Syntax
8.6.Collections
Cypher has good support for collections.
8.6.1.Collections in general
A literal collection is created by using brackets and separating the elements in the collection with
commas.
Query.
RETURN [0,1,2,3,4,5,6,7,8,9] AS collection
Result
collection
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 row
In our examples, well use the range function. It gives you a collection containing all numbers
between given start and end numbers.
To access individual elements in the collection, we use the square brackets again.
Query.
RETURN range(0,10)[3]
Result
range(0,10)[3]
3
1 row
You can also use negative numbers, to start from the end of the collection instead.
Query.
RETURN range(0,10)[-3]
Result
range(0,10)[-3]
8
1 row
Finally, you can use ranges inside the brackets to return ranges of the collection.
Query.
RETURN range(0,10)[0..3]
Result
range(0,10)[0..3]
[0, 1, 2]
1 row
Query.
108
Syntax
RETURN range(0,10)[0..-5]
Result
range(0,10)[0..-5]
[0, 1, 2, 3, 4, 5]
1 row
Query.
RETURN range(0,10)[-5..]
Result
range(0,10)[-5..]
[6, 7, 8, 9, 10]
1 row
Query.
RETURN range(0,10)[..4]
Result
range(0,10)[..4]
[0, 1, 2, 3]
1 row
Note: Out-of-bound slices are simply truncated, but out-of-bound single elements return null.
Query.
RETURN range(0,10)[15]
Result
range(0,10)[15]
<null>
1 row
Query.
RETURN range(0,10)[5..15]
Result
range(0,10)[5..15]
[5, 6, 7, 8, 9, 10]
1 row
8.6.2.List comprehension
List comprehension is a syntactic construct available in Cypher for creating a collection based on
existing collections. It follows the form of the mathematical set-builder notation (set comprehension)
instead of the use of map and filter functions.
Query.
109
Syntax
RETURN [x IN range(0,10) WHERE x % 2 = 0 | x^3] AS result
Result
result
[0. 0, 8. 0, 64. 0, 216. 0, 512. 0, 1000. 0]
1 row
Either the WHERE part, or the expression, can be omitted, if you only want to filter or map respectively.
Query.
RETURN [x IN range(0,10) WHERE x % 2 = 0] AS result
Result
result
[0, 2, 4, 6, 8, 10]
1 row
Query.
RETURN [x IN range(0,10)| x^3] AS result
Result
result
[0. 0, 1. 0, 8. 0, 27. 0, 64. 0, 125. 0, 216. 0, 343. 0, 512. 0, 729. 0, 1000. 0]
1 row
8.6.3.Literal maps
From Cypher, you can also construct maps. In Java, they will be java.util.Map<String,Object>,
through REST you will get JSON objects.
Query.
RETURN { key : "Value", collectionKey: [{ inner: "Map1" }, { inner: "Map2" }]}
Result
{ key : "Value", collectionKey: [ { inner: "Map1" }, { inner: "Map2" } ] }
{key -> "Value", collectionKey -> [{inner -> "Map1"}, {inner -> "Map2"}]}
1 row
110
Syntax
The logical operators (i.e. AND, OR, XOR, IN) treat NULL as the unknown value of three-valued logic.
Here is the truth table for AND, OR and XOR.
a
a AND b
a OR b
a XOR b
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
NULL
FALSE
NULL
NULL
FALSE
TRUE
FALSE
TRUE
TRUE
TRUE
FALSE
FALSE
TRUE
TRUE
TRUE
NULL
NULL
TRUE
NULL
TRUE
TRUE
TRUE
TRUE
FALSE
NULL
FALSE
FALSE
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
TRUE
NULL
TRUE
NULL
The IN operator follows similar logic. If Cypher knows that something exists in a collection, the result
will be TRUE. Any collection that contains a NULL and doesnt have a matching element will return NULL.
Otherwise, the result will be false. Here is a table with examples:
Expression
Result
2 IN [1, 2, 3]
TRUE
2 IN [1, NULL, 3]
NULL
2 IN [1, 2, NULL]
TRUE
2 IN [1]
FALSE
2 IN []
FALSE
IN [1,2,3]
NULL
IN [1,NULL,3]
NULL
NULL
NULL
NULL IN
[]
FALSE
Using ALL, ANY, NONE, and SINGLE follows a similar rule. If the result can be calculated definitely, TRUE or
FALSE is returned. Otherwise NULL is produced.
Syntax
Trying to access a property that does not exist on a node or relationship: n.missingProperty
Comparisons when either side is NULL: 1 < NULL
Arithmetic expressions containing NULL: 1 + NULL
Function calls where any arguments are NULL: sin(NULL)
112
Chapter9.General Clauses
113
General Clauses
9.1.Return
In the RETURN part of your query, you define which parts of the pattern you are interested in. It can be
nodes, relationships, or properties on these.
Figure9.1.Graph
nam e = 'A'
happy = 'Yes! '
age = 55
KNOWS BLOCKS
nam e = 'B'
9.1.1.Return nodes
To return a node, list it in the RETURN statement.
Query.
MATCH (n { name: "B" })
RETURN n
1 row
9.1.2.Return relationships
To return a relationship, just include it in the RETURN list.
Query.
MATCH (n { name: "A" })-[r:KNOWS]->(c)
RETURN r
1 row
9.1.3.Return property
To return a property, use the dot separator, like this:
Query.
MATCH (n { name: "A" })
RETURN n.name
114
General Clauses
The value of the property name gets returned.
Result
n.name
"A"
1 row
This returns the two nodes, the relationship and the path used in the query.
Result
b
Node[1]{name:"B"}
Node[0]{name:"A",
happy:"Yes!", age:55}
:KNOWS[0]{}
[Node[0]{name:"A",
happy:"Yes!",
age:55}, :KNOWS[0]{},
Node[1]{name:"B"}]
Node[1]{name:"B"}
Node[0]{name:"A",
happy:"Yes!", age:55}
:BLOCKS[1]{}
[Node[0]{name:"A",
happy:"Yes!",
age:55}, :BLOCKS[1]{},
Node[1]{name:"B"}]
2 rows
1 row
9.1.6.Column alias
If the name of the column should be different from the expression used, you can rename it by using AS
<new name>.
115
General Clauses
Query.
MATCH (a { name: "A" })
RETURN a.age AS SomethingTotallyDifferent
1 row
9.1.7.Optional properties
If a property might or might not be there, you can still select it as usual. It will be treated as NULL if it is
missing
Query.
MATCH (n)
RETURN n.age
This example returns the age when the node has that property, or null if the property is not there.
Result
n.age
55
<null>
2 rows
9.1.8.Other expressions
Any expression can be used as a return item literals, predicates, properties, functions, and
everything else.
Query.
MATCH (a { name: "A" })
RETURN a.age > 30, "I'm a literal",(a)-->()
Returns a predicate, a literal and function call with a pattern expression parameter.
Result
a.age > 30
"I'm a literal"
(a)-->()
true
"I'm a literal"
[[Node[0]{name:"A",
happy:"Yes!", age:55}, :KNOWS[0]
{}, Node[1]{name:"B"}], [Node[0]
{name:"A", happy:"Yes!",
age:55}, :BLOCKS[1]{}, Node[1]
{name:"B"}]]
1 row
9.1.9.Unique results
DISTINCT
retrieves only unique rows depending on the columns that have been selected to output.
116
General Clauses
Query.
MATCH (a { name: "A" })-->(b)
RETURN DISTINCT b
1 row
117
General Clauses
9.2.Order by
To sort the output, use the ORDER BY clause. Note that you can not sort on nodes or relationships, just
on properties on these.
Figure9.2.Graph
nam e = 'A'
age = 34
lengt h = 170
KNOWS
nam e = 'B'
age = 34
KNOWS
nam e = 'C'
age = 32
lengt h = 185
Query.
MATCH (n)
RETURN n
ORDER BY n.name
3 rows
This returns the nodes, sorted first by their age, and then by their name.
118
General Clauses
Result
n
Node[2]{name:"C", age:32, length:185}
Node[0]{name:"A", age:34, length:170}
Node[1]{name:"B", age:34}
3 rows
3 rows
9.2.4.Ordering NULL
When sorting the result set, NULL will always come at the end of the result set for ascending sorting,
and first when doing descending sort.
Query.
MATCH (n)
RETURN n.length, n
ORDER BY n.length
The nodes are returned sorted by the length property, with a node without that property last.
Result
n.length
170
185
<null>
Node[1]{name:"B", age:34}
3 rows
119
General Clauses
9.3.Limit
LIMIT
KNOWS
nam e = 'D'
KNOWS
nam e = 'E'
KNOWS
KNOWS
nam e = 'B'
3 rows
120
nam e = 'C'
General Clauses
9.4.Skip
enables the return of only subsets of the total result. By using SKIP, the result set will get trimmed
from the top. Please note that no guarantees are made on the order of the result unless the query
specifies the ORDER BY clause.
SKIP
Figure9.4.Graph
nam e = 'A'
KNOWS
nam e = 'D'
KNOWS
nam e = 'E'
KNOWS
nam e = 'B'
KNOWS
nam e = 'C'
The first three nodes are skipped, and only the last two are returned in the result.
Result
n
Node[0]{name:"D"}
Node[1]{name:"E"}
2 rows
2 rows
121
General Clauses
n
Node[4]{name:"C"}
2 rows
122
General Clauses
9.5.With
With WITH, you can manipulate the result sequence before it is passed on to the following query parts.
The manipulations can be of the shape and/or number of entries in the result set.
One common usage of WITH is to limit the number of entries that are then passed on to other MATCH
clauses. By combining ORDER BY and LIMIT, its possible to get the top X entries by some criteria, and
then bring in additional data from the graph.
Another use is to filter on aggregated values. WITH is used to introduce aggregates which can then by
used in predicates in WHERE. These aggregate expressions create new bindings in the results. WITH can
also, just like RETURN, alias expressions that are introduced into the results using the aliases as binding
name.
is also used to separate reading from updating of the graph. Every part of a query must be either
read-only or write-only. When going from a writing part to a reading part, the switch must be done
with a WITH clause.
WITH
Figure9.5.Graph
nam e = 'David'
KNOWS
nam e = 'Anders'
BLOCKS
BLOCKS
KNOWS
nam e = 'Ceasar'
KNOWS
KNOWS
The person connected to David with the at least more than one outgoing relationship will be returned
by the query.
123
General Clauses
Result
otherPerson
Node[2]{name:"Anders"}
1 row
1 row
Starting at Anders, find all matching nodes, order by name descending and get the top result, then find
all the nodes connected to that top result, and return their names.
Result
o.name
"Anders"
"Bossman"
2 rows
124
General Clauses
9.6.Union
Combining results from multiple queries is done through the UNION operator.
Combines the results of two or more queries into a single result set that includes all the rows that
belong to all queries in the union.
The number and the names of the columns must be identical in all queries combined by using UNION.
To keep all the result rows, use UNION ALL. Using just UNION will combine and remove duplicates from
the result set.
Figure9.6.Graph
Act or
Act or
KNOWS
Act or
ACTS_IN
nam e = 'Helen Mirren'
ACTS_IN
Movie
t it le = 'Hit chcock'
4 rows
General Clauses
Query.
MATCH (n:Actor)
RETURN n.name AS name
UNION
MATCH (n:Movie)
RETURN n.title AS name
3 rows
126
General Clauses
9.7.Using
If you do not specify an explicit START clause, Cypher needs to infer where in the graph to start your
query. This is done by looking at the WHERE clause and the MATCH clause and using that information to
find a useful index.
This index might not be the best choice though sometimes multiple indexes could be used, and
Cypher has picked the wrong one (from a performance point of view).
You can force Cypher to use a specific starting point by using the USING clause. This is called giving
Cypher an index hint.
If your query matches large parts of an index, it might be faster to scan the label and filter out nodes
that do not match. To do this, you can use USING SCAN. It will force Cypher to not use an index that
could have been used, and instead do a label scan.
Note
You cannot use index hints if your query has a START clause.
1 row
1 row
127
General Clauses
This query does its work by finding all :German labeled nodes and filtering them by the surname
property.
Result
m
Node[1]{name:"Stefan", surname:"Plantikow"}
1 row
128
Chapter10.Reading Clauses
The flow of data within a Cypher query is an unordered sequence of maps with key-value pairs a
set of possible bindings between the identifiers in the query and values derived from the database.
This set is refined and augmented by subsequent parts of the query.
129
Reading Clauses
10.1.Match
10.1.1.Introduction
MATCH
is the primary way of getting data from the database into the current set of bindings.
The MATCH clause allows you to specify the patterns Cypher will search for in the database.
Nodes and relationships that are already known at this stage are called bound pattern elements.
Cypher will now try to find the unknown parts of the pattern.
If MATCH is the first clause in your query, nothing is bound at this stage. Cypher needs starting points to
do its pattern matching.
If no bound nodes exist, Cypher can scan all nodes in the database, all nodes with a certain label, or
it can use indexes to quickly find the relevant starting points. For more information on indexes, see
Section13.1, Indexes. If you want to use index hints to force Cypher to use a specific index, read
more in Section9.7, Using.
defines the MATCH patterns in more detail. The predicates are part of the pattern description, not a
filter applied after the matching is done. This means that WHERE should always be put together with the
MATCH clause it belongs to.
WHERE
After finding starting points either by using scans, indexes or already bound points the execution
engine will use pattern matching to find matching subgraphs. As Cypher is declarative, it can change
the order of these operations. Predicates in WHERE clauses can be evaluated before pattern matching,
during pattern matching, or after finding matches.
Tip
To understand the patterns used in the MATCH clause, read Section8.5, Patterns.
The following graph is used for the examples below:
Figure10.1.Graph
Person
nam e = 'Charlie Sheen'
FATHER
Person
Person
Person
Person
ACTED_IN
nam e = 'Oliver St one'
DIRECTED
ACTED_IN
ACTED_IN
Movie
ACTED_IN
ACTED_IN
DIRECTED
Movie
130
Reading Clauses
RETURN n
7 rows
Get all nodes with a label
Getting all nodes with a label on them is done with a single node pattern where the node has a label on
it.
Query.
MATCH (movie:Movie)
RETURN movie
2 rows
Related nodes
The symbol -- means related to, without regard to type or direction of the relationship.
Query.
MATCH (director { name:'Oliver Stone' })--(movie)
RETURN movie.title
1 row
Match with labels
To constrain your pattern with labels on nodes, you add it to your pattern nodes, using the label
syntax.
131
Reading Clauses
Query.
MATCH (charlie:Person { name:'Charlie Sheen' })--(movie:Movie)
RETURN movie
Return any nodes connected with the Person Charlie that are labeled Movie.
Result
movie
Node[4]{name:"WallStreet", title:"Wall Street"}
1 row
10.1.3.Relationship basics
Outgoing relationships
When the direction of a relationship is interesting, it is shown by using --> or <--, like this:
Query.
MATCH (martin { name:'Martin Sheen' })-->(movie)
RETURN movie.title
2 rows
Directed relationships and identifier
If an identifier is needed, either for filtering on properties of the relationship, or to return the
relationship, this is how you introduce the identifier.
Query.
MATCH (martin { name:'Martin Sheen' })-[r]->(movie)
RETURN r
2 rows
Match by relationship type
When you know the relationship type you want to match on, you can specify it by using a colon
together with the relationship type.
Query.
132
Reading Clauses
MATCH (wallstreet { title:'Wall Street' })<-[:ACTED_IN]-(actor)
RETURN actor
3 rows
Match by multiple relationship types
To match on one of multiple types, you can specify this by chaining them together with the pipe
symbol |.
Query.
MATCH (wallstreet { title:'Wall Street' })<-[:ACTED_IN|:DIRECTED]-(person)
RETURN person
4 rows
Match by relationship type and use an identifier
If you both want to introduce an identifier to hold the relationship, and specify the relationship type
you want, just add them both, like this.
Query.
MATCH (wallstreet { title:'Wall Street' })<-[r:ACTED_IN]-(actor)
RETURN r
3 rows
133
Reading Clauses
10.1.4.Relationships in depth
Relationship types with uncommon characters
Sometime your database will have types with non-letter characters, or with spaces in them. Use `
(backtick) to quote these.
Query.
MATCH (n { name:'Rob Reiner' })-[r:`TYPE THAT HAS SPACE IN IT`]->()
RETURN r
1 row
Multiple relationships
Relationships can be expressed by using multiple statements in the form of ()--(), or they can be
strung together, like this:
Query.
MATCH (charlie { name:'Charlie Sheen' })-[:ACTED_IN]->(movie)<-[:DIRECTED]->(director)
RETURN charlie,movie,director
movie
director
Node[1]{name:"Charlie Sheen"}
Node[4]{name:"WallStreet",
title:"Wall Street"}
Node[0]{name:"Oliver Stone"}
1 row
Variable length relationships
Nodes that are a variable number of relationshipnode hops away can be found using the following
syntax: -[:TYPE*minHops..maxHops]->. minHops and maxHops are optional and default to 1 and infinity
respectively. When no bounds are given the dots may be omitted.
Query.
MATCH (martin { name:"Martin Sheen" })-[:ACTED_IN*1..2]-(x)
RETURN x
5 rows
134
Reading Clauses
x
Node[6]{name:"Michael Douglas"}
Node[3]{name:"TheAmericanPresident", title:"The American President"}
Node[6]{name:"Michael Douglas"}
5 rows
Relationship identifier in variable length relationships
When the connection between two nodes is of variable length, a relationship identifier becomes an
collection of relationships.
Query.
MATCH (actor { name:'Charlie Sheen' })-[r:ACTED_IN*2]-(co_actor)
RETURN r
2 rows
Match with properties on a variable length path
A variable length relationship with properties defined on in it means that all relationships in the path
must have the property set to the given value. In this query, there are two paths between Charile Sheen
and his dad Martin Sheen. One of the includes a blocked relationship and the other doesnt. In this
case we first alter the original graph by using the following query to add blocked and unblocked
relationships:
MATCH (charlie:Person { name:'Charlie Sheen' }),(martin:Person { name:'Martin Sheen' })
CREATE (charlie)-[:X { blocked:false }]->(:Unblocked)<-[:X { blocked:false }]-(martin)
CREATE (charlie)-[:X { blocked:true }]->(:Blocked)<-[:X { blocked:false }]-(martin);
This means that we are starting out with the following graph:
Person
nam e = 'Charlie Sheen'
FATHER
Person
Person
ACTED_IN
DIRECTED
ACTED_IN
Movie
X
blocked = t rue
ACTED_IN
Person
Person
ACTED_IN
Blocked
X
blocked = false
ACTED_IN
Movie
X
blocked = false
X
blocked = false
DIRECTED
Unblocked
Query.
MATCH p =(charlie:Person)-[* { blocked:false }]-(martin:Person)
WHERE charlie.name = 'Charlie Sheen' AND martin.name = 'Martin Sheen'
RETURN p
Returns the paths between Charlie and Martin Sheen where all relationships have the blocked property
set to FALSE.
135
Reading Clauses
Result
p
[Node[1]{name:"Charlie Sheen"}, :X[12]{blocked:false}, Node[9]{}, :X[13]{blocked:false}, Node[2]
{name:"Martin Sheen"}]
1 row
Zero length paths
Using variable length paths that have the lower bound zero means that two identifiers can point
to the same node. If the distance between two nodes is zero, they are by definition the same node.
Note that when matching zero length paths the result may contain a match even when matching on a
relationship type not in use.
Query.
MATCH (wallstreet:Movie { title:'Wall Street' })-[*0..1]-(x)
RETURN x
Returns all nodes that are zero or one relationships away from Wall Street.
Result
x
Node[4]{name:"WallStreet", title:"Wall Street"}
Node[1]{name:"Charlie Sheen"}
Node[2]{name:"Martin Sheen"}
Node[6]{name:"Michael Douglas"}
Node[0]{name:"Oliver Stone"}
5 rows
Named path
If you want to return or filter on a path in your pattern graph, you can a introduce a named path.
Query.
MATCH p =(michael { name:'Michael Douglas' })-->()
RETURN p
2 rows
Matching on a bound relationship
When your pattern contains a bound relationship, and that relationship pattern doesnt specify
direction, Cypher will try to match the relationship in both directions.
136
Reading Clauses
Query.
MATCH (a)-[r]-(b)
WHERE id(r)= 0
RETURN a,b
This returns the two connected nodes, once as the start node, and once as the end node.
Result
a
Node[1]{name:"Charlie Sheen"}
Node[1]{name:"Charlie Sheen"}
2 rows
10.1.5.Shortest path
Single shortest path
Finding a single shortest path between two nodes is as easy as using the shortestPath function. Its
done like this:
Query.
MATCH (martin:Person { name:"Martin Sheen" }),(oliver:Person { name:"Oliver Stone" }),
p = shortestPath((martin)-[*..15]-(oliver))
RETURN p
This means: find a single shortest path between two nodes, as long as the path is max 15 relationships
long. Inside of the parentheses you define a single link of a path the starting node, the connecting
relationship and the end node. Characteristics describing the relationship like relationship type, max
hops and direction are all used when finding the shortest path. You can also mark the path as optional.
Result
p
[Node[2]{name:"Martin Sheen"}, :ACTED_IN[1]{}, Node[4]{name:"WallStreet", title:"Wall
Street"}, :DIRECTED[5]{}, Node[0]{name:"Oliver Stone"}]
1 row
All shortest paths
Finds all the shortest paths between two nodes.
Query.
MATCH (martin:Person { name:"Martin Sheen" }),(michael:Person { name:"Michael Douglas" }),
p = allShortestPaths((martin)-[*]-(michael))
RETURN p
2 rows
137
Reading Clauses
p
[Node[2]{name:"Martin Sheen"}, :ACTED_IN[1]{}, Node[4]{name:"WallStreet", title:"Wall
Street"}, :ACTED_IN[2]{}, Node[6]{name:"Michael Douglas"}]
2 rows
138
Reading Clauses
10.2.Optional Match
10.2.1.Introduction
matches patterns against your graph database, just like MATCH does. The difference is
that if no matches are found, OPTIONAL MATCH will use NULLs for missing parts of the pattern. OPTIONAL
MATCH could be considered the Cypher equivalent of the outer join in SQL.
OPTIONAL MATCH
Either the whole pattern is matched, or nothing is matched. Remember that WHERE is part of the pattern
description, and the predicates will be considered while looking for matches, not after. This matters
especially in the case of multiple (OPTIONAL) MATCH clauses, where it is crucial to put WHERE together with
the MATCH it belongs to.
Tip
To understand the patterns used in the OPTIONAL MATCH clause, read Section8.5, Patterns.
The following graph is used for the examples below:
Figure10.2.Graph
Person
nam e = 'Charlie Sheen'
FATHER
Person
Person
Person
Person
ACTED_IN
nam e = 'Oliver St one'
DIRECTED
ACTED_IN
ACTED_IN
Movie
ACTED_IN
ACTED_IN
DIRECTED
Movie
10.2.2.Relationship
If a relationship is optional, use the OPTIONAL MATCH clause. This is similar to how a SQL outer join
works. If the relationship is there, it is returned. If its not, NULL is returned in its place.
Query.
MATCH (a:Movie { title: 'Wall Street' })
OPTIONAL MATCH (a)-->(x)
RETURN x
1 row
Reading Clauses
Query.
MATCH (a:Movie { title: 'Wall Street' })
OPTIONAL MATCH (a)-->(x)
RETURN x, x.name
Returns the element x (NULL in this query), and NULL as its name.
Result
x
x.name
<null>
<null>
1 row
This returns a node, and NULL, since the node has no outgoing ACTS_IN relationships.
Result
r
<null>
1 row
140
Reading Clauses
10.3.Where
WHERE
is not a clause in its own right rather, its part of MATCH, OPTIONAL MATCH, START and WITH.
In the case of WITH and START, WHERE simply filters the results.
For MATCH and OPTIONAL MATCH on the other hand, WHERE adds constraints to the patterns described. It
should not be seen as a filter after the matching is finished.
Note
In the case of multiple (OPTIONAL) MATCH clauses, the predicate in WHERE is always a part of
the patterns in the directly preceding MATCH. Both results and performance may be impacted
if the WHERE is put inside the wrong MATCH clause.
Figure10.3.Graph
Swedish
nam e = 'Andres'
age = 36
belt = 'whit e'
KNOWS
nam e = 'Tobias'
age = 25
KNOWS
10.3.1.Basic usage
Boolean operations
You can use the expected boolean operators AND and OR, and also the boolean function NOT. See
Section8.7, Working with NULL for more information on how this works with NULL.
Query.
MATCH (n)
WHERE n.name = 'Peter' XOR (n.age < 30 AND n.name = "Tobias") OR NOT (n.name = "Tobias" OR
n.name="Peter")
RETURN n
3 rows
Filter on node label
To filter nodes by label, write a label predicate after the WHERE keyword using WHERE n:foo.
Query.
141
Reading Clauses
MATCH (n)
WHERE n:Swedish
RETURN n
1 row
Filter on node property
To filter on a property, write your clause after the WHERE keyword. Filtering on relationship properties
works just the same way.
Query.
MATCH (n)
WHERE n.age < 30
RETURN n
1 row
Property exists
To only include nodes/relationships that have a property, use the HAS() function and just write out the
identifier and the property you expect it to have.
Query.
MATCH (n)
WHERE HAS (n.belt)
RETURN n
1 row
10.3.2.Regular expressions
Regular expressions
You can match on regular expressions by using =~ "regexp", like this:
Query.
MATCH (n)
WHERE n.name =~ 'Tob.*'
142
Reading Clauses
RETURN n
1 row
Escaping in regular expressions
If you need a forward slash inside of your regular expression, escape it. Remember that back slash
needs to be escaped in string literals
Query.
MATCH (n)
WHERE n.name =~ 'Some\/thing'
RETURN n
0 row
Case insensitive regular expressions
By pre-pending a regular expression with (?i), the whole expression becomes case insensitive.
Query.
MATCH (n)
WHERE n.name =~ '(?i)ANDR.*'
RETURN n
1 row
Reading Clauses
Note that you can not introduce new identifiers here. Although it might look very similar to the MATCH
patterns, the WHERE clause is all about eliminating matched subgraphs. MATCH (a)-[*]->(b) is very
different from WHERE (a)-[*]->(b); the first will produce a subgraph for every path it can find between
a and b, and the latter will eliminate any matched subgraphs where a and b do not have a directed
relationship chain between them.
Query.
MATCH (tobias { name: 'Tobias' }),(others)
WHERE others.name IN ['Andres', 'Peter'] AND (tobias)<--(others)
RETURN others
Nodes that have an outgoing relationship to the "Tobias" node are returned.
Result
others
Node[2]{name:"Andres", age:36, belt:"white"}
1 row
Filter on patterns using NOT
The NOT function can be used to exclude a pattern.
Query.
MATCH (persons),(peter { name: 'Peter' })
WHERE NOT (persons)-->(peter)
RETURN persons
Nodes that do not have an outgoing relationship to the "Peter" node are returned.
Result
persons
Node[0]{name:"Tobias", age:25}
Node[1]{name:"Peter", age:34}
2 rows
Filter on patterns with properties
You can also add properties to your patterns:
Query.
MATCH (n)
WHERE (n)-[:KNOWS]-({ name:'Tobias' })
RETURN n
Finds all nodes that have a KNOWS relationship to a node with the name Tobias.
Result
n
Node[2]{name:"Andres", age:36, belt:"white"}
1 row
144
Reading Clauses
Filtering on relationship type
You can put the exact relationship type in the MATCH pattern, but sometimes you want to be able to do
more advanced filtering on the type. You can use the special property TYPE to compare the type with
something else. In this example, the query does a regular expression comparison with the name of the
relationship type.
Query.
MATCH (n)-[r]->()
WHERE n.name='Andres' AND type(r)=~ 'K.*'
RETURN r
This returns relationships that has a type whose name starts with K.
Result
r
:KNOWS[0]{}
:KNOWS[1]{}
2 rows
10.3.4.Collections
IN operator
To check if an element exists in a collection, you can use the IN operator.
Query.
MATCH (a)
WHERE a.name IN ["Peter", "Tobias"]
RETURN a
2 rows
Reading Clauses
Result
n
Node[2]{name:"Andres", age:36, belt:"white"}
1 row
Default to true if property is missing
If you want to compare a property on a graph element, but only if it exists, you can compare the
property against both the value you are looking for and NULL, like:
Query.
MATCH (n)
WHERE n.belt = 'white' OR n.belt IS NULL RETURN n
ORDER BY n.name
This returns all nodes, even those without the belt property.
Result
n
Node[2]{name:"Andres", age:36, belt:"white"}
Node[1]{name:"Peter", age:34}
Node[0]{name:"Tobias", age:25}
3 rows
Filter on NULL
Sometimes you might want to test if a value or an identifier is NULL. This is done just like SQL does it,
with IS NULL. Also like SQL, the negative is IS NOT NULL, although NOT(IS NULL x) also works.
Query.
MATCH (person)
WHERE person.name = 'Peter' AND person.belt IS NULL RETURN person
1 row
146
Reading Clauses
10.4.Start
Every query describes a pattern, and in that pattern one can have multiple starting points. A starting
point is a relationship or a node where a pattern is anchored. You can either introduce starting points
by id, or by index lookups. Note that trying to use an index that doesnt exist will generate an error.
Note
is optional. If you do not specify explicit starting points, Cypher will try and infer
starting points from your query. This is done based on node labels and predicates contained
in your query. See Chapter13, Schema for more information. In general, the START clause is
only really needed when using legacy indexes.
START
KNOWS
KNOWS
The query returns the node indexed with the name "A".
Result
n
Node[0]{name:"A"}
1 row
Relationship by index lookup
When the starting point can be found by using index lookups, it can be done like this:
relationship:index-name(key = "value").
Query.
START r=relationship:rels(name = "Andrs")
RETURN r
The relationship indexed with the name property set to "Andrs" is returned by the query.
147
Reading Clauses
Result
r
:KNOWS[0]{name:"Andrs"
1 row
Node by index query
When the starting point can be found by more complex Lucene queries, this is the syntax to use:
node:index-name("query").This allows you to write more advanced index queries.
Query.
START n=node:nodes("name:A")
RETURN n
1 row
Note
Neo4j reuses its internal ids when nodes and relationships are deleted, which means its
bad practice to refer to them this way. Instead, use application generated ids.
Query.
START n=node(0)
RETURN n
1 row
Relationship by id
Binding a relationship as a starting point is done with the relationship(*) function, which can also be
abbreviated rel(*). See the section called Node by id for more information on Neo4j ids.
Query.
START r=relationship(0)
RETURN r
Reading Clauses
Result
r
:KNOWS[0]{}
1 row
Multiple nodes by id
Multiple nodes are selected by listing them separated by commas.
Query.
START n=node(0, 1, 2)
RETURN n
3 rows
Tip
The preferred way to do this is to use a MATCH clause, see the section called Get all nodes
in Section10.1, Match for how to do that.
Query.
START n=node(*)
RETURN n
3 rows
Multiple starting points
Sometimes you want to bind multiple starting points. Just list them separated by commas.
Query.
149
Reading Clauses
START a=node(0), b=node(1)
RETURN a,b
Node[0]{name:"A"}
Node[1]{name:"B"}
1 row
150
Reading Clauses
10.5.Aggregation
10.5.1.Introduction
To calculate aggregated data, Cypher offers aggregation, much like SQLs GROUP BY.
Aggregate functions take multiple input values and calculate an aggregated value from them.
Examples are avg that calculates the average of multiple numeric values, or min that finds the smallest
numeric value in a set of values.
Aggregation can be done over all the matching sub graphs, or it can be further divided by introducing
key values. These are non-aggregate expressions, that are used to group the values going into the
aggregate functions.
So, if the return statement looks something like this:
RETURN n, count(*)
We have two return expressions n, and count(*). The first, n, is no aggregate function, and so it will
be the grouping key. The latter, count(*) is an aggregate expression. So the matching subgraphs will
be divided into different buckets, depending on the grouping key. The aggregate function will then run
on these buckets, calculating the aggregate values.
If you want to use aggregations to sort your result set, the aggregation must be included in the RETURN
to be used in your ORDER BY.
The last piece of the puzzle is the DISTINCT keyword. It is used to make all values unique before
running them through an aggregate function.
An example might be helpful. In this case, we are running the query against the following data:
Person
nam e = 'A'
propert y = 13
KNOWS
Person
nam e = 'D'
eyes = 'brown'
KNOWS
KNOWS
Person
Person
nam e = 'B'
propert y = 33
eyes = 'blue'
nam e = 'C'
propert y = 44
eyes = 'blue'
KNOWS
Person
nam e = 'D'
Query.
MATCH (me:Person)-->(friend:Person)-->(friend_of_friend:Person)
WHERE me.name = 'A'
RETURN count(DISTINCT friend_of_friend), count(friend_of_friend)
151
KNOWS
Reading Clauses
In this example we are trying to find all our friends of friends, and count them. The first aggregate
function, count(DISTINCT friend_of_friend), will only see a friend_of_friend once DISTINCT removes
the duplicates. The latter aggregate function, count(friend_of_friend), might very well see the same
friend_of_friend multiple times. In this case, both B and C know D and thus D will get counted twice,
when not using DISTINCT.
Result
count(distinct friend_of_friend)
count(friend_of_friend)
1 row
The following examples are assuming the example graph structure below.
Figure10.5.Graph
Person
nam e = 'A'
propert y = 13
KNOWS
Person
nam e = 'D'
eyes = 'brown'
KNOWS
KNOWS
Person
Person
nam e = 'B'
propert y = 33
eyes = 'blue'
nam e = 'C'
propert y = 44
eyes = 'blue'
10.5.2.COUNT
is used to count the number of rows. COUNT can be used in two forms COUNT(*) which just
counts the number of matching rows, and COUNT(<identifier>), which counts the number of non-NULL
values in <identifier>.
COUNT
Count nodes
To count the number of nodes, for example the number of nodes connected to one node, you can use
count(*).
Query.
MATCH (n { name: 'A' })-->(x)
RETURN n, count(*)
This returns the start node and the count of related nodes.
Result
n
count(*)
Node[1]{name:"A", property:13}
1 row
Group Count Relationship Types
To count the groups of relationship types, return the types and count them with count(*).
152
Reading Clauses
Query.
MATCH (n { name: 'A' })-[r]->()
RETURN type(r), count(*)
The relationship types and their group count is returned by the query.
Result
type(r)
count(*)
"KNOWS"
1 row
Count entities
Instead of counting the number of results with count(*), it might be more expressive to include the
name of the identifier you care about.
Query.
MATCH (n { name: 'A' })-->(x)
RETURN count(x)
The example query returns the number of connected nodes from the start node.
Result
count(x)
3
1 row
Count non-null values
You can count the non-null values by using count(<identifier>).
Query.
MATCH (n:Person)
RETURN count(n.property)
The count of related nodes with the property property set is returned by the query.
Result
count(n.property)
3
1 row
10.5.3.Statistics
sum
The sum aggregation function simply sums all the numeric values it encounters. NULLs are silently
dropped.
Query.
MATCH (n:Person)
RETURN sum(n.property)
153
Reading Clauses
This returns the sum of all the values in the property property.
Result
sum(n.property)
90
1 row
avg
avg
Query.
MATCH (n:Person)
RETURN avg(n.property)
The average of all the values in the property property is returned by the example query.
Result
avg(n.property)
30. 0
1 row
percentileDisc
calculates the percentile of a given value over a group, with a percentile from 0.0 to
1.0. It uses a rounding method, returning the nearest value to the percentile. For interpolated values,
see percentileCont.
percentileDisc
Query.
MATCH (n:Person)
RETURN percentileDisc(n.property, 0.5)
The 50th percentile of the values in the property property is returned by the example query. In this
case, 0.5 is the median, or 50th percentile.
Result
percentileDisc(n.property, 0.5)
33
1 row
percentileCont
calculates the percentile of a given value over a group, with a percentile from 0.0 to
1.0. It uses a linear interpolation method, calculating a weighted average between two values, if the
desired percentile lies between them. For nearest values using a rounding method, see percentileDisc.
percentileCont
Query.
MATCH (n:Person)
RETURN percentileCont(n.property, 0.4)
The 40th percentile of the values in the property property is returned by the example query, calculated
with a weighted average.
154
Reading Clauses
Result
percentileCont(n.property, 0.4)
29. 0
1 row
stdev
calculates the standard deviation for a given value over a group. It uses a standard two-pass
method, with N - 1 as the denominator, and should be used when taking a sample of the population for
an unbiased estimate. When the standard variation of the entire population is being calculated, stdevp
should be used.
stdev
Query.
MATCH (n)
WHERE n.name IN ['A', 'B', 'C']
RETURN stdev(n.property)
The standard deviation of the values in the property property is returned by the example query.
Result
stdev(n.property)
15. 716233645501712
1 row
stdevp
calculates the standard deviation for a given value over a group. It uses a standard two-pass
method, with N as the denominator, and should be used when calculating the standard deviation for an
entire population. When the standard variation of only a sample of the population is being calculated,
stdev should be used.
stdevp
Query.
MATCH (n)
WHERE n.name IN ['A', 'B', 'C']
RETURN stdevp(n.property)
The population standard deviation of the values in the property property is returned by the example
query.
Result
stdevp(n.property)
12. 832251036613439
1 row
max
max
Query.
MATCH (n:Person)
RETURN max(n.property)
Reading Clauses
Result
max(n.property)
44
1 row
min
min
takes a numeric property as input, and returns the smallest value in that column.
Query.
MATCH (n:Person)
RETURN min(n.property)
This returns the smallest of all the values in the property property.
Result
min(n.property)
13
1 row
10.5.4.collect
collect
Query.
MATCH (n:Person)
RETURN collect(n.property)
1 row
10.5.5.DISTINCT
All aggregation functions also take the DISTINCT modifier, which removes duplicates from the values.
So, to count the number of unique eye colors from nodes related to a, this query can be used:
Query.
MATCH (a:Person { name: 'A' })-->(b)
RETURN count(DISTINCT b.eyes)
1 row
156
Chapter11.Writing Clauses
Write data to the database.
157
Writing Clauses
11.1.Create
Tip
In the CREATE clause, patterns are used a lot. Read Section8.5, Patterns for an
introduction.
11.1.1.Create nodes
Create single node
Creating a single node is done by issuing the following query.
Query.
CREATE (n)
Nothing is returned from this query, except the count of affected nodes.
Result
(empty result)
Nodes created: 1
Create a node with a label
To add a label when creating a node, use the syntax below.
Query.
CREATE (n:Person)
Nodes created: 1
Labels added: 1
Create a node with multiple labels
To add labels when creating a node, use the syntax below. In this case, we add two labels.
Query.
CREATE (n:Person:Swedish)
Nodes created: 1
Labels added: 2
Create node and add labels and properties
When creating a new node with labels, you can add properties at the same time.
158
Writing Clauses
Query.
CREATE (n:Person { name : 'Andres', title : 'Developer' })
Nodes created: 1
Properties set: 2
Labels added: 1
Return created node
Creating a single node is done by issuing the following query.
Query.
CREATE (a { name : 'Andres' })
RETURN a
1 row
Nodes created: 1
Properties set: 1
11.1.2.Create relationships
Create a relationship between two nodes
To create a relationship between two nodes, we first get the two nodes. Once the nodes are loaded, we
simply create a relationship between them.
Query.
MATCH (a:Person),(b:Person)
WHERE a.name = 'Node A' AND b.name = 'Node B'
CREATE (a)-[r:RELTYPE]->(b)
RETURN r
1 row
Relationships created: 1
159
Writing Clauses
Create a relationship and set properties
Setting properties on relationships is done in a similar manner to how its done when creating nodes.
Note that the values can be any expression.
Query.
MATCH (a:Person),(b:Person)
WHERE a.name = 'Node A' AND b.name = 'Node B'
CREATE (a)-[r:RELTYPE { name : a.name + '<->' + b.name }]->(b)
RETURN r
1 row
Relationships created: 1
Properties set: 1
This query creates three nodes and two relationships in one go, assigns it to a path identifier, and
returns it.
Result
p
[Node[3]{name:"Andres"}, :WORKS_AT[2]{}, Node[4]{}, :WORKS_AT[3]{}, Node[5]{name:"Michael"}]
1 row
Nodes created: 3
Relationships created: 2
Properties set: 2
160
Writing Clauses
"position" : "Developer"
}
}
Query.
CREATE (n:Person { props })
RETURN n
Result
n
Node[1]{name:"Andres", position:"Developer"}
1 row
Nodes created: 1
Properties set: 2
Labels added: 1
Create multiple nodes with a parameter for their properties
By providing Cypher an array of maps, it will create a node for each map.
Note
When you do this, you cant create anything else in the same CREATE clause.
Parameters.
{
"props" : [ {
"name" : "Andres",
"position" : "Developer"
}, {
"name" : "Michael",
"position" : "Developer"
} ]
}
Query.
CREATE (n { props })
RETURN n
Result
n
Node[2]{name:"Andres", position:"Developer"}
Node[3]{name:"Michael", position:"Developer"}
2 rows
Nodes created: 2
Properties set: 4
161
Writing Clauses
11.2.Create Unique
CREATE UNIQUE is in the middle of MATCH and CREATE it will match what it can, and create what is
missing. CREATE UNIQUE will always make the least change possible to the graph if it can use parts
of
Another difference to MATCH is that CREATE UNIQUE assumes the pattern to be unique. If multiple
matching subgraphs are found an error will be generated.
Tip
In the CREATE UNIQUE clause, patterns are used a lot. Read Section8.5, Patterns for an
introduction.
The examples start out with the following data set:
nam e = 'root '
nam e = 'A'
nam e = 'B'
KNOWS
nam e = 'C'
The root node doesnt have any LOVES relationships, and so a node is created, and also a relationship to
that node.
Result
someone
Node[5]{}
1 row
Nodes created: 1
Relationships created: 1
162
Writing Clauses
Create nodes with values
The pattern described can also contain values on the node. These are given using the following syntax:
prop : <expression>.
Query.
MATCH (root { name: 'root' })
CREATE UNIQUE (root)-[:X]-(leaf { name:'D' })
RETURN leaf
No node connected with the root node has the name D, and so a new node is created to match the
pattern.
Result
leaf
Node[5]{name:"D"}
1 row
Nodes created: 1
Relationships created: 1
Properties set: 1
Create labeled node if missing
If the pattern described needs a labeled node and there is none with the given labels, Cypher will
create a new one.
Query.
MATCH (a { name: 'A' })
CREATE UNIQUE (a)-[:KNOWS]-(c:blue)
RETURN c
The A node is connected in a KNOWS relationship to the c node, but since C doesnt have the :blue label,
a new node labeled as :blue is created along with a KNOWS relationship from A to it.
Result
c
Node[5]{}
1 row
Nodes created: 1
Relationships created: 1
Labels added: 1
163
Writing Clauses
RETURN r
The left node is matched agains the two right nodes. One relationship already exists and can be
matched, and the other relationship is created before it is returned.
Result
r
:KNOWS[5]{}
:KNOWS[3]{}
2 rows
Relationships created: 1
Create relationship with values
Relationships to be created can also be matched on values.
Query.
MATCH (root { name: 'root' })
CREATE UNIQUE (root)-[r:X { since:'forever' }]-()
RETURN r
In this example, we want the relationship to have a value, and since no such relationship can be found,
a new node and relationship are created. Note that since we are not interested in the created node, we
dont name it.
Result
r
:X[5]{since:"forever"}
1 row
Nodes created: 1
Relationships created: 1
Properties set: 1
1 row
Nodes created: 1
Relationships created: 2
164
Writing Clauses
11.3.Merge
11.3.1.Introduction
Provisional Feature
is a new addition and a provisional feature of Cypher. Its scope and syntax are
subject to change. Therefore, please be aware that you may need to change queries that use
MERGE in the future.
MERGE
ensures that a pattern exists in the graph. Either the pattern already exists, or it needs to be
created.
MERGE
either matches existing nodes and binds them, or it creates new data and binds that. Its like a
combination of MATCH and CREATE that additionally allows you to specify what happens if the data was
matched or created.
MERGE
For example, you can specify that the graph must contain a node for a user with a certain name. If
there isnt a node with the correct name, a new node will be created and its name property set.
When using MERGE on full patterns, the behavior is that either the whole pattern matches, or the whole
pattern is created. MERGE will not partially use existing patterns its all or nothing. If partial matches
are needed, this can be accomplished by splitting a pattern up into multiple MERGE clauses.
As with MATCH, MERGE can match multiple occurrences of a pattern. If there are multiple matches, they
will all be passed on to later stages of the query.
The last part of MERGE is the ON CREATE and ON MATCH. These allow a query to express additional changes
to the properties of a node or relationship, depending on if the element was MATCHed in the database or
if it was CREATEd.
The following graph is used for the examples below:
Figure11.1.Graph
Person
nam e = 'Charlie Sheen'
FATHER
Person
Person
Person
Person
ACTED_IN
nam e = 'Oliver St one'
DIRECTED
ACTED_IN
ACTED_IN
Movie
ACTED_IN
ACTED_IN
Movie
11.3.2.Merge nodes
Merge single node with a label
Merging a single node with a given label.
Query.
165
DIRECTED
Writing Clauses
MERGE (robert:Critic)
RETURN robert, labels(robert)
Because there are no nodes labeled Critic in the database, a new node is created.
Result
robert
labels(robert)
Node[8]{}
["Critic"]
1 row
Nodes created: 1
Labels added: 1
Merge single node with properties
Merging a single node with properties where not all properties match any existing node.
Query.
MERGE (charlie { name:'Charlie Sheen', age:10 })
RETURN charlie
A new node with the name Charlie Sheen will be created since not all properties matched the existing
Charlie Sheen node.
Result
charlie
Node[8]{name:"Charlie Sheen", age:10}
1 row
Nodes created: 1
Properties set: 2
Merge single node specifying both label and property
Merging a single node with both label and property matching an existing node.
Query.
MERGE (michael:Person { name:'Michael Douglas' })
RETURN michael
1 row
Writing Clauses
Query.
MERGE (keanu:Person { name:'Keanu Reeves' })
ON CREATE SET keanu.created = timestamp()
RETURN keanu
1 row
Nodes created: 1
Properties set: 2
Labels added: 1
Merge with ON MATCH
Merging nodes and setting properties on found nodes.
Query.
MERGE (person:Person)
ON MATCH SET person.found = TRUE RETURN person
Finds all the Person nodes, sets a property on them, and returns them.
Result
person
Node[0]{name:"Oliver Stone", found:true}
Node[1]{name:"Charlie Sheen", found:true}
Node[2]{name:"Martin Sheen", found:true}
Node[5]{name:"Rob Reiner", found:true}
Node[6]{name:"Michael Douglas", found:true}
5 rows
Properties set: 5
Merge with ON CREATE and ON MATCH
Merge a node and set properties if the node needs to be created.
Query.
MERGE (keanu:Person { name:'Keanu Reeves' })
ON CREATE SET keanu.created = timestamp()
ON MATCH SET keanu.lastSeen = timestamp()
RETURN keanu
The query creates the Keanu node, and sets a timestamp on creation time. If Keanu already existed, a
different property would have been set.
167
Writing Clauses
Result
keanu
Node[8]{created:1386514447501, name:"Keanu Reeves"}
1 row
Nodes created: 1
Properties set: 2
Labels added: 1
11.3.4.Merge relationships
Merge on a relationship
MERGE
Query.
MATCH (charlie:Person { name:'Charlie Sheen' }),(wallStreet:Movie { title:'Wall Street' })
MERGE (charlie)-[r:ACTED_IN]->(wallStreet)
RETURN r
Charlie Sheen had already been marked as acting on Wall Street, so the existing relationship is found
and returned. Note that in order to match or create a relationship when using MERGE, at least one bound
node must be specified, which is done via the MATCH clause in the above example.
Result
r
:ACTED_IN[0]{}
1 row
Merge on multiple relationships
When MERGE is used on a whole pattern, either everything matches, or everything is created.
Query.
MATCH (oliver:Person { name:'Oliver Stone' }),(reiner:Person { name:'Rob Reiner' })
MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:ACTED_IN]-(reiner)
RETURN movie
In our example graph, Oliver Stone and Rob Reiner have never worked together. When we try to MERGE
a movie between them, Cypher will not use any of the existing movies already connected to either
person. Instead, a new movie node is created.
Result
movie
Node[8]{}
1 row
Nodes created: 1
Relationships created: 2
Labels added: 1
168
Writing Clauses
The query creates the laurence node. If laurence already existed, merge would just return the existing
node.
Result
laurence
Node[8]{name:"Laurence Fishburne"}
1 row
Nodes created: 1
Properties set: 1
Labels added: 1
Merge using unique constraints matches an existing node
Merge using unique constraints matches an existing node.
Query.
MERGE (oliver:Person { name:'Oliver Stone' })
RETURN oliver
1 row
Merge with unique constraints and partial matches
Merge using unique constraints fails when finding partial matches.
Query.
169
Writing Clauses
MERGE (michael:Person { name:'Michael Douglas', role:'Gordon Gekko' })
RETURN michael
While there is a matching unique michael node with the name Michael Douglas, there is no unique
node with the role of Gordon Gekko and merge fails to match.
Error message.
Merge did not find a matching node and can not create a new node due to conflicts
with both existing and missing unique nodes. The conflicting constraints are on:
:Person.name and :Person.role
While there is a matching unique oliver node with the name Oliver Stone, there is also another unique
node with the role of Gordon Gekko and merge fails to match.
Error message.
Merge did not find a matching node and can not create a new node due to conflicts
with both existing and missing unique nodes. The conflicting constraints are on:
:Person.name and :Person.role
Query.
MERGE (oliver:Person { name: { param }.name, role: { param }.role })
RETURN oliver
Result
oliver
Node[8]{name:"Keanu Reeves", role:"Neo"}
1 row
Nodes created: 1
Properties set: 2
Labels added: 1
170
Writing Clauses
11.4.Set
Updating labels on nodes and properties on nodes and relationships is done with the SET clause. SET
can also be used with maps from parameters to set properties.
Note
Setting labels on a node is an idempotent operations if you try to set a label on a node
that already has that label on it, nothing happens. The query statistics will tell you if
something needed to be done or not.
The examples use this graph as a starting point:
nam e = 'St efan'
KNOWS
Swedish
nam e = 'Andres'
age = 36
awesom e = t rue
KNOWS
KNOWS
11.4.1.Set a property
To set a property on a node or relationship, use SET.
Query.
MATCH (n { name: 'Andres' })
SET n.surname = 'Taylor'
RETURN n
1 row
Properties set: 1
11.4.2.Remove a property
Normally you remove a property by using REMOVE, but its sometimes handy to do it using the SET
command. One example is if the property comes from a parameter.
Query.
171
Writing Clauses
MATCH (n { name: 'Andres' })
SET n.name = NULL RETURN n
The node is returned by the query, and the name property is now missing.
Result
n
Node[3]{age:36, awesome:true}
1 row
Properties set: 1
The Andres node has had all its properties replaced by the properties in the Peter node.
Result
at
pn
Node[3]{age:34, name:"Peter"}
Node[2]{name:"Peter", age:34}
1 row
Properties set: 3
Query.
MATCH (n { name: 'Andres' })
SET n.surname = { surname }
RETURN n
1 row
Properties set: 1
172
Writing Clauses
Query.
MATCH (n { name: 'Andres' })
SET n = { props }
RETURN n
The Andres node has had all its properties replaced by the properties in the props parameter.
Result
n
Node[3]{position:"Developer", name:"Andres"}
1 row
Properties set: 4
1 row
Labels added: 1
Writing Clauses
Result
n
Node[0]{name:"Emil"}
1 row
Labels added: 2
174
Writing Clauses
11.5.Delete
Deleting graph elements nodes and relationships, is done with DELETE.
For removing properties and labels, see Section11.6, Remove.
The examples start out with the following database:
nam e = 'Andres'
age = 36
KNOWS
nam e = 'Tobias'
age = 25
KNOWS
Nothing is returned from this query, except the count of affected nodes.
Result
(empty result)
Nodes deleted: 1
Nothing is returned from this query, except the count of affected nodes.
Result
(empty result)
Nodes deleted: 1
Relationships deleted: 2
175
Writing Clauses
OPTIONAL MATCH (n)-[r]-()
DELETE n,r
Nothing is returned from this query, except the count of affected nodes.
Result
(empty result)
Nodes deleted: 3
Relationships deleted: 2
176
Writing Clauses
11.6.Remove
Removing properties and labels from graph elements is done using REMOVE.
For deleting nodes and relationships, see Section11.5, Delete.
Note
Removing labels from a node is an idempotent operation: If you try to remove a label from
a node that does not have that label on it, nothing happens. The query statistics will tell you
if something needed to be done or not.
The examples start out with the following database:
Swedish
nam e = 'Andres'
age = 36
KNOWS
Swedish
KNOWS
Swedish, Germ an
nam e = 'Tobias'
age = 25
11.6.1.Remove a property
Neo4j doesnt allow storing null in properties. Instead, if no value exists, the property is just not there.
So, to remove a property value on a node or a relationship, is also done with REMOVE.
Query.
MATCH (andres { name: 'Andres' })
REMOVE andres.age
RETURN andres
1 row
Properties set: 1
177
Writing Clauses
Result
n
Node[1]{name:"Peter", age:34}
1 row
Labels removed: 1
Result
n
Node[1]{name:"Peter", age:34}
1 row
Labels removed: 2
178
Writing Clauses
11.7.Foreach
Collections and paths are key concepts in Cypher. To use them for updating data, you can use the
FOREACH construct. It allows you to do updating commands on elements in a collection a path, or a
collection created by aggregation.
The identifier context inside of the foreach parenthesis is separate from the one outside it. This means
that if you CREATE a node identifier inside of a FOREACH, you will not be able to use it outside of the
foreach statement, unless you match to find it.
Inside of the FOREACH parentheses, you can do any of the updating commands CREATE, CREATE UNIQUE,
DELETE, and FOREACH.
Figure11.2.Data for the examples
nam e = 'A'
KNOWS
nam e = 'B'
KNOWS
nam e = 'C'
KNOWS
nam e = 'D'
Nothing is returned from this query, but four properties are set.
Result
(empty result)
Properties set: 4
179
Chapter12.Functions
This chapter contains information on all functions in Cypher. Note that related information exists in
Section8.1, Operators.
Note
180
Functions
12.1.Predicates
Predicates are boolean functions that return true or false for a given set of input. They are most
commonly used to filter out subgraphs in the WHERE part of a query.
See also Section8.1.2, Comparison operators.
Figure12.1.Graph
foo, bar
nam e = 'Alice'
age = 38
eyes = 'brown'
KNOWS
nam e = 'Bob'
age = 25
eyes = 'blue'
MARRIED
KNOWS
nam e = 'Charlie'
age = 53
eyes = 'green'
KNOWS
KNOWS
Spouse
nam e = 'Eskil'
age = 41
eyes = 'blue'
array = [ 'one', 't wo', 't hree']
nam e = 'Daniel'
age = 54
eyes = 'brown'
12.1.1.ALL
Tests whether a predicate holds for all element of this collection collection.
Syntax: ALL(identifier in collection WHERE predicate)
Arguments:
collection: An expression that returns a collection
identifier: This is the identifier that can be used from the predicate.
predicate: A predicate that is tested against all items in the collection.
Query.
MATCH p=(a)-[*1..3]->(b)
WHERE a.name='Alice' AND b.name='Daniel' AND ALL (x IN nodes(p) WHERE x.age > 30)
RETURN p
All nodes in the returned paths will have an age property of at least 30.
Result
p
[Node[2]{name:"Alice", age:38, eyes:"brown"}, :KNOWS[1]{}, Node[4]{name:"Charlie", age:53,
eyes:"green"}, :KNOWS[3]{}, Node[0]{name:"Daniel", age:54, eyes:"brown"}]
1 row
181
Functions
12.1.2.ANY
Tests whether a predicate holds for at least one element in the collection.
Syntax: ANY(identifier in collection WHERE predicate)
Arguments:
collection: An expression that returns a collection
identifier: This is the identifier that can be used from the predicate.
predicate: A predicate that is tested against all items in the collection.
Query.
MATCH (a)
WHERE a.name='Eskil' AND ANY (x IN a.array WHERE x = "one")
RETURN a
All nodes in the returned paths has at least one one value set in the array property named array.
Result
a
Node[1]{name:"Eskil", age:41, eyes:"blue", array:["one", "two", "three"]}
1 row
12.1.3.NONE
Returns true if the predicate holds for no element in the collection.
Syntax: NONE(identifier in collection WHERE predicate)
Arguments:
collection: An expression that returns a collection
identifier: This is the identifier that can be used from the predicate.
predicate: A predicate that is tested against all items in the collection.
Query.
MATCH p=(n)-[*1..3]->(b)
WHERE n.name='Alice' AND NONE (x IN nodes(p) WHERE x.age = 25)
RETURN p
2 rows
12.1.4.SINGLE
Returns true if the predicate holds for exactly one of the elements in the collection.
182
Functions
Syntax: SINGLE(identifier in collection WHERE predicate)
Arguments:
collection: An expression that returns a collection
identifier: This is the identifier that can be used from the predicate.
predicate: A predicate that is tested against all items in the collection.
Query.
MATCH p=(n)-->(b)
WHERE n.name='Alice' AND SINGLE (var IN nodes(p) WHERE var.eyes = "blue")
RETURN p
Exactly one node in every returned path will have the eyes property set to "blue".
Result
p
[Node[2]{name:"Alice", age:38, eyes:"brown"}, :KNOWS[0]{}, Node[3]{name:"Bob", age:25, eyes:"blue"}]
1 row
183
Functions
12.2.Scalar functions
Scalar functions return a single value.
Figure12.2.Graph
foo, bar
nam e = 'Alice'
age = 38
eyes = 'brown'
KNOWS
nam e = 'Bob'
age = 25
eyes = 'blue'
MARRIED
KNOWS
nam e = 'Charlie'
age = 53
eyes = 'green'
KNOWS
KNOWS
Spouse
nam e = 'Eskil'
age = 41
eyes = 'blue'
array = [ 'one', 't wo', 't hree']
nam e = 'Daniel'
age = 54
eyes = 'brown'
12.2.1.LENGTH
To return or filter on the length of a collection, use the LENGTH() function.
Syntax: LENGTH( collection )
Arguments:
collection: An expression that returns a collection
Query.
MATCH p=(a)-->(b)-->(c)
WHERE a.name='Alice'
RETURN length(p)
3 rows
12.2.2.TYPE
Returns a string representation of the relationship type.
184
Functions
Syntax: TYPE( relationship )
Arguments:
relationship: A relationship.
Query.
MATCH (n)-[r]->()
WHERE n.name='Alice'
RETURN type(r)
2 rows
12.2.3.ID
Returns the id of the relationship or node.
Syntax: ID( property-container )
Arguments:
property-container: A node or a relationship.
Query.
MATCH (a)
RETURN id(a)
5 rows
12.2.4.COALESCE
Returns the first non-NULL value in the list of expressions passed to it. In case all arguments are NULL,
NULL will be returned.
Syntax: COALESCE( expression [, expression]* )
185
Functions
Arguments:
expression: The expression that might return NULL.
Query.
MATCH (a)
WHERE a.name='Alice'
RETURN coalesce(a.hairColor, a.eyes)
Result
coalesce(a.hairColor, a.eyes)
"brown"
1 row
12.2.5.HEAD
HEAD
head(a.array)
"one"
1 row
12.2.6.LAST
LAST
Functions
Result
a.array
last(a.array)
"three"
1 row
12.2.7.TIMESTAMP
returns the difference, measured in milliseconds, between the current time and midnight,
January 1, 1970 UTC. It will return the same value during the whole one query, even if the query is a
long running one.
TIMESTAMP
Syntax: TIMESTAMP()
Arguments:
Query.
RETURN timestamp()
1 row
12.2.8.STARTNODE
STARTNODE
Result
startNode(r)
Node[2]{name:"Alice", age:38, eyes:"brown"}
Node[2]{name:"Alice", age:38, eyes:"brown"}
2 rows
12.2.9.ENDNODE
ENDNODE
Functions
relationship: An expression that returns a relationship
Query.
MATCH (x:foo)-[r]-()
RETURN endNode(r)
Result
endNode(r)
Node[3]{name:"Bob", age:25, eyes:"blue"}
Node[4]{name:"Charlie", age:53, eyes:"green"}
2 rows
188
Functions
12.3.Collection functions
Collection functions return collections of things nodes in a path, and so on.
See also Section8.1.5, Collection operators.
Figure12.3.Graph
foo, bar
nam e = 'Alice'
age = 38
eyes = 'brown'
KNOWS
nam e = 'Bob'
age = 25
eyes = 'blue'
MARRIED
KNOWS
nam e = 'Charlie'
age = 53
eyes = 'green'
KNOWS
KNOWS
Spouse
nam e = 'Eskil'
age = 41
eyes = 'blue'
array = [ 'one', 't wo', 't hree']
nam e = 'Daniel'
age = 54
eyes = 'brown'
12.3.1.NODES
Returns all nodes in a path.
Syntax: NODES( path )
Arguments:
path: A path.
Query.
MATCH p=(a)-->(b)-->(c)
WHERE a.name='Alice' AND c.name='Eskil'
RETURN nodes(p)
All the nodes in the path p are returned by the example query.
Result
nodes(p)
[Node[2]{name:"Alice", age:38, eyes:"brown"}, Node[3]{name:"Bob", age:25, eyes:"blue"}, Node[1]
{name:"Eskil", age:41, eyes:"blue", array:["one", "two", "three"]}]
1 row
12.3.2.RELATIONSHIPS
Returns all relationships in a path.
189
Functions
Syntax: RELATIONSHIPS( path )
Arguments:
path: A path.
Query.
MATCH p=(a)-->(b)-->(c)
WHERE a.name='Alice' AND c.name='Eskil'
RETURN relationships(p)
1 row
12.3.3.LABELS
Returns a collection of string representations for the labels attached to a node.
Syntax: LABELS( node )
Arguments:
node: Any expression that returns a single node
Query.
MATCH (a)
WHERE a.name='Alice'
RETURN labels(a)
1 row
12.3.4.EXTRACT
To return a single property, or the value of a function from a collection of nodes or relationships, you
can use EXTRACT. It will go through a collection, run an expression on every element, and return the
results in an collection with these values. It works like the map method in functional languages such as
Lisp and Scala.
Syntax: EXTRACT( identifier in collection | expression )
Arguments:
collection: An expression that returns a collection
identifier: The closure will have an identifier introduced in its context. Here you decide which
identifier to use.
190
Functions
expression: This expression will run once per value in the collection, and produces the result
collection.
Query.
MATCH p=(a)-->(b)-->(c)
WHERE a.name='Alice' AND b.name='Bob' AND c.name='Daniel'
RETURN extract(n IN nodes(p)| n.age) AS extracted
1 row
12.3.5.FILTER
FILTER
This returns the property named array and a list of values in it, which have the length 3.
Result
a.array
["one", "two"]
1 row
12.3.6.TAIL
TAIL
191
Functions
This returns the property named array and all elements of that property except the first one.
Result
a.array
tail(a.array)
["two", "three"]
1 row
12.3.7.RANGE
Returns numerical values in a range with a non-zero step value step. Range is inclusive in both ends.
Syntax: RANGE( start, end [, step] )
Arguments:
start: A numerical expression.
end: A numerical expression.
step: A numerical expression.
Query.
RETURN range(0,10), range(2,18,3)
range(2,18,3)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1 row
12.3.8.REDUCE
To run an expression against individual elements of a collection, and store the result of the expression
in an accumulator, you can use REDUCE. It will go through a collection, run an expression on every
element, storing the partial result in the accumulator. It works like the fold or reduce method in
functional languages such as Lisp and Scala.
Syntax: REDUCE( accumulator = initial, identifier in collection | expression )
Arguments:
accumulator: An identifier that will hold the result and the partial results as the collection is iterated
initial: An expression that runs once to give a starting value to the accumulator
collection: An expression that returns a collection
identifier: The closure will have an identifier introduced in its context. Here you decide which
identifier to use.
expression: This expression will run once per value in the collection, and produces the result value.
Query.
MATCH p=(a)-->(b)-->(c)
WHERE a.name='Alice' AND b.name='Bob' AND c.name='Daniel'
RETURN reduce(totalAge = 0, n IN nodes(p)| totalAge + n.age) AS reduction
192
Functions
The age property of all nodes in the path are summed and returned as a single value.
Result
reduction
117
1 row
193
Functions
12.4.Mathematical functions
These functions all operate on numerical expressions only, and will return an error if used on any
other values.
See also Section8.1.1, Mathematical operators.
Figure12.4.Graph
foo, bar
nam e = 'Alice'
age = 38
eyes = 'brown'
KNOWS
nam e = 'Bob'
age = 25
eyes = 'blue'
MARRIED
KNOWS
nam e = 'Charlie'
age = 53
eyes = 'green'
KNOWS
KNOWS
Spouse
nam e = 'Eskil'
age = 41
eyes = 'blue'
array = [ 'one', 't wo', 't hree']
nam e = 'Daniel'
age = 54
eyes = 'brown'
12.4.1.ABS
ABS
e.age
abs(a.age - e.age)
38
41
3. 0
1 row
12.4.2.ACOS
ACOS
Functions
Arguments:
expression: A numeric expression.
Query.
RETURN acos(0.5)
1 row
12.4.3.ASIN
ASIN
1 row
12.4.4.ATAN
ATAN
1 row
195
Functions
12.4.5.ATAN2
ATAN2
1 row
12.4.6.COS
COS
1 row
12.4.7.COT
COT
Functions
Result
cot(0.5)
1. 830487721712452
1 row
12.4.8.DEGREES
DEGREES
1 row
12.4.9.E
E
Syntax: E( expression )
Arguments:
Query.
RETURN e()
1 row
12.4.10.EXP
EXP
Functions
RETURN exp(2)
1 row
12.4.11.FLOOR
FLOOR
1 row
12.4.12.HAVERSIN
HAVERSIN
1 row
Spherical distance using the haversin function
The haversin function may be used to compute the distance on the surface of a sphere between two
points (each given by their latitude and longitude). In this example the spherical distance (in km)
198
Functions
between Berlin in Germany (at lat 52.5, lon 13.4) and San Mateo in California (at lat 37.5, lon -122.3)
is calculated using an average earth radius of 6371 km.
Query.
CREATE (ber:City { lat: 52.5, lon: 13.4 }),(sm:City { lat: 37.5, lon: -122.3 })
RETURN 2 * 6371 * asin(sqrt(haversin(radians(sm.lat - ber.lat))+ cos(radians(sm.lat))*
cos(radians(ber.lat))* haversin(radians(sm.lon - ber.lon)))) AS dist
The distance between Berlin and San Mateo is returned (about 9129 km).
Result
dist
9129. 969740051658
1 row
Nodes created: 2
Properties set: 4
Labels added: 2
12.4.13.LOG
LOG
1 row
12.4.14.LOG10
LOG10
Functions
Result
log10(27)
1. 4313637641589874
1 row
12.4.15.PI
PI
Syntax: PI()
Arguments:
Query.
RETURN pi()
1 row
12.4.16.RADIANS
RADIANS
1 row
12.4.17.RAND
RAND
Functions
RETURN rand() AS x1
1 row
12.4.18.ROUND
ROUND
Result
round(3.141592)
3
1 row
12.4.19.SIGN
SIGN returns the signum of a
1 for any positive number.
number zero if the expression is zero, -1 for any negative number, and
Result
sign(-17)
sign(0.1)
-1. 0
1. 0
1 row
12.4.20.SIN
SIN
Functions
expression: A numeric expression.
Query.
RETURN sin(0.5)
1 row
12.4.21.SQRT
SQRT
Result
sqrt(256)
16. 0
1 row
12.4.22.TAN
TAN
1 row
202
Functions
12.5.String functions
These functions all operate on string expressions only, and will return an error if used on any other
values. Except STR(), which converts to strings.
See also Section8.1.4, String operators.
Figure12.5.Graph
foo, bar
nam e = 'Alice'
age = 38
eyes = 'brown'
KNOWS
nam e = 'Bob'
age = 25
eyes = 'blue'
MARRIED
KNOWS
nam e = 'Charlie'
age = 53
eyes = 'green'
KNOWS
KNOWS
Spouse
nam e = 'Eskil'
age = 41
eyes = 'blue'
array = [ 'one', 't wo', 't hree']
nam e = 'Daniel'
age = 54
eyes = 'brown'
12.5.1.STR
returns a string representation of the expression.
STR
Result
str(1)
"1"
1 row
12.5.2.REPLACE
returns a string with the search string replaced by the replace string. It replaces all
occurrences.
REPLACE
Functions
original: An expression that returns a string
search: An expression that returns a string to search for
replace: An expression that returns the string to replace the search string with
Query.
RETURN replace("hello", "l", "w")
Result
replace("hello", "l", "w")
"hewwo"
1 row
12.5.3.SUBSTRING
returns a substring of the original, with a 0-based index start and length. If length is omitted,
it returns a substring from start until the end of the string.
SUBSTRING
Result
substring("hello", 1, 3)
substring("hello", 2)
"ell"
"llo"
1 row
12.5.4.LEFT
LEFT
Result
left("hello", 3)
"hel"
1 row
204
Functions
12.5.5.RIGHT
returns a string containing the right n characters of the original string.
RIGHT
Result
right("hello", 3)
"llo"
1 row
12.5.6.LTRIM
LTRIM
returns the original string with whitespace removed from the left side.
hello")
Result
ltrim(" hello")
"hello"
1 row
12.5.7.RTRIM
RTRIM
returns the original string with whitespace removed from the right side.
")
Result
rtrim("hello ")
"hello"
1 row
205
Functions
12.5.8.TRIM
TRIM
returns the original string with whitespace removed from both sides.
hello
")
Result
trim(" hello ")
"hello"
1 row
12.5.9.LOWER
LOWER
Result
lower("HELLO")
"hello"
1 row
12.5.10.UPPER
UPPER
Result
upper("hello")
"HELLO"
1 row
206
Chapter13.Schema
Neo4j 2.0 introduced an optional schema for the graph, based around the concept of labels. Labels are
used in the specification of indexes, and for defining constraints on the graph. Together, indexes and
constraints are the schema of the graph. Cypher includes data definition language (DDL) statements
for manipulating the schema.
207
Schema
13.1.Indexes
Cypher allows the creation of indexes over a property for all nodes that have a given label. These
indexes are automatically managed and kept up to date by the database whenever the graph is
changed.
Result
(empty result)
Indexes added: 1
Result
(empty result)
Indexes removed: 1
13.1.3.Use index
There is usually no need to specify which indexes to use in a query, Cypher will figure that out by
itself. For example the query below will use the Person(name) index, if it exists. If you for some reason
want to hint to specific indexes, see Section9.7, Using.
Query.
MATCH (n:Person { name: 'Andres' })
RETURN n
Result
n
(empty result)
0 row
208
Schema
13.2.Constraints
Neo4j helps enforce data integrity with the use of constraints.
You can use unique constraints to ensure that property values are unique for all nodes with a
specific label. Unique constraints do not mean that all nodes have to have a unique value for the
properties nodes without the property are not subject to this rule.
Remember that adding constraints is an atomic operation that can take a while all existing data has
to be scanned before Neo4j can turn the constraint on.
You can have multiple unique constraints for a given label.
Note that adding a uniqueness constraint on a property will also add an index on that property, so you
cannot add such an index separately. Cypher will use that index for lookups just like other indexes. If
you drop a constraint and still want an index on the property, you will have to create the index.
Result
(empty result)
Constraints added: 1
Result
(empty result)
Constraints removed: 1
Result
(empty result)
Nodes created: 1
Properties set: 2
Labels added: 1
209
Schema
Error message.
Node 0 already exists with label Book and property "isbn"=[1449356265]
210
211
14.1.Start
SQL starts with the result you want we SELECT what we want and then declare how to source it. In
Cypher, the START clause is quite a different concept which specifies starting points in the graph from
which the query will execute.
From a SQL point of view, the identifiers in START are like table names that point to a set of nodes
or relationships. The set can be listed literally, come via parameters, or as I show in the following
example, be defined by an index look-up.
So in fact rather than being SELECT-like, the START clause is somewhere between the FROM and the WHERE
clause in SQL.
SQL Query.
SELECT *
FROM "Person"
WHERE name = 'Anakin'
NAME
ID
AGE
HAIR
Anakin
20
blonde
1 rows
Cypher Query.
START person=node:Person(name = 'Anakin')
RETURN person
person
Node[0]{name:"Anakin", id:1, age:20, hair:"blonde"}
1 row
Cypher allows multiple starting points. This should not be strange from a SQL perspective every
table in the FROM clause is another starting point.
212
14.2.Match
Unlike SQL which operates on sets, Cypher predominantly works on sub-graphs. The relational
equivalent is the current set of tuples being evaluated during a SELECT query.
The shape of the sub-graph is specified in the MATCH clause. The MATCH clause is analogous to the JOIN in
SQL. A normal ab relationship is an inner join between nodes a and b both sides have to have at
least one match, or nothing is returned.
Well start with a simple example, where we find all email addresses that are connected to the person
Anakin. This is an ordinary one-to-many relationship.
SQL Query.
SELECT "Email".*
FROM "Person"
JOIN "Email" ON "Person".id = "Email".person_id
WHERE "Person".name = 'Anakin'
ADDRESS
COMMENT
PERSON_ID
anakin@example. com
home
anakin@example. org
work
2 rows
Cypher Query.
START person=node:Person(name = 'Anakin')
MATCH person-[:email]->email
RETURN email
email
Node[6]{address:"anakin@example. com", comment:"home"}
Node[7]{address:"anakin@example. org", comment:"work"}
2 rows
There is no join table here, but if one is necessary the next example will show how to do that, writing
the pattern relationship like so: -[r:belongs_to]-> will introduce (the equivalent of) join table available
as the variable r. In reality this is a named relationship in Cypher, so were saying join Person to Group
via belongs_to. To illustrate this, consider this image, comparing the SQL model and Neo4j/Cypher.
213
NAME
ID
BELONGS_TO_GROUP_ID
PERSON_ID
GROUP_ID
Admin
1 rows
Cypher Query.
START person=node:Person(name = 'Bridget')
MATCH person-[r:belongs_to]->group
RETURN group, r
group
Node[5]{name:"Admin", id:4}
:belongs_to[0]{}
1 row
An outer join <http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html>
is just as easy. Add OPTIONAL before the match and its an optional relationship between nodes the
outer join of Cypher.
Whether its a left outer join, or a right outer join is defined by which side of the pattern has a starting
point. This example is a left outer join, because the bound node is on the left side:
SQL Query.
SELECT "Person".name, "Email".address
FROM "Person" LEFT
JOIN "Email" ON "Person".id = "Email".person_id
NAME
ADDRESS
Anakin
anakin@example. com
Anakin
anakin@example. org
Bridget
<null>
3 rows
Cypher Query.
START person=node:Person('name: *')
OPTIONAL MATCH person-[:email]->email
RETURN person.name, email.address
person.name
email.address
"Anakin"
"anakin@example. com"
3 rows
214
email.address
"Anakin"
"anakin@example. org"
"Bridget"
<null>
3 rows
Relationships in Neo4j are first class citizens its like the SQL tables are pre-joined with each other.
So, naturally, Cypher is designed to be able to handle highly connected data easily.
One such domain is tree structures anyone that has tried storing tree structures in SQL knows that
you have to work hard to get around the limitations of the relational model. There are even books on
the subject.
To find all the groups and sub-groups that Bridget belongs to, this query is enough in Cypher:
Cypher Query.
START person=node:Person('name: Bridget')
MATCH person-[:belongs_to*]->group
RETURN person.name, group.name
person.name
group.name
"Bridget"
"Admin"
"Bridget"
"Technichian"
"Bridget"
"User"
3 rows
The * after the relationship type means that there can be multiple hops across belongs_to relationships
between group and user. Some SQL dialects have recursive abilities, that allow the expression
of queries like this, but you may have a hard time wrapping your head around those. Expressing
something like this in SQL is hugely impractical if not practically impossible.
215
14.3.Where
This is the easiest thing to understand its the same animal in both languages. It filters out result
sets/subgraphs. Not all predicates have an equivalent in the other language, but the concept is the
same.
SQL Query.
SELECT *
FROM "Person"
WHERE "Person".age > 35 AND "Person".hair = 'blonde'
NAME
ID
AGE
HAIR
Bridget
40
blonde
1 rows
Cypher Query.
START person=node:Person('name: *')
WHERE person.age > 35 AND person.hair = 'blonde'
RETURN person
person
Node[1]{name:"Bridget", id:2, age:40, hair:"blonde"}
1 row
216
14.4.Return
This is SQLs SELECT. We just put it in the end because it felt better to have it there you do a lot of
matching and filtering, and finally, you return something.
Aggregate queries work just like they do in SQL, apart from the fact that there is no explicit GROUP BY
clause. Everything in the return clause that is not an aggregate function will be used as the grouping
columns.
SQL Query.
SELECT "Person".name, count(*)
FROM "Person"
GROUP BY "Person".name
ORDER BY "Person".name
NAME
C2
Anakin
Bridget
2 rows
Cypher Query.
START person=node:Person('name: *')
RETURN person.name, count(*)
ORDER BY person.name
person.name
count(*)
"Anakin"
"Bridget"
2 rows
Order by is the same in both languages ORDER BY expression ASC/DESC. Nothing weird here.
217
PartIV.Reference
The reference part is the authoritative source for details on Neo4j usage. It covers details on capabilities,
transactions, indexing and queries among other topics.
Chapter15.Capabilities
219
Capabilities
15.1.Data Security
Some data may need to be protected from unauthorized access (e.g., theft, modification). Neo4j does
not deal with data encryption explicitly, but supports all means built into the Java programming
language and the JVM to protect data by encrypting it before storing.
Furthermore, data can be easily secured by running on an encrypted datastore at the file system level.
Finally, data protection should be considered in the upper layers of the surrounding system in order to
prevent problems with scraping, malicious data insertion, and other threats.
220
Capabilities
15.2.Data Integrity
In order to keep data consistent, there needs to be mechanisms and structures that guarantee the
integrity of all stored data. In Neo4j, data integrity is maintained for the core graph engine together
with other data sources - see below.
221
Capabilities
15.3.Data Integration
Most enterprises rely primarily on relational databases to store their data, but this may cause
performance limitations. In some of these cases, Neo4j can be used as an extension to supplement
search/lookup for faster decision making. However, in any situation where multiple data repositories
contain the same data, synchronization can be an issue.
In some applications, it is acceptable for the search platform to be slightly out of sync with the
relational database. In others, tight data integrity (eg., between Neo4j and RDBMS) is necessary.
Typically, this has to be addressed for data changing in real-time and for bulk data changes happening
in the RDBMS.
A few strategies for synchronizing integrated data follows.
15.3.1.Event-based Synchronization
In this scenario, all data stores, both RDBMS and Neo4j, are fed with domain-specific events via
an event bus. Thus, the data held in the different backends is not actually synchronized but rather
replicated.
15.3.2.Periodic Synchronization
Another viable scenario is the periodic export of the latest changes in the RDBMS to Neo4j via
some form of SQL query. This allows a small amount of latency in the synchronization, but has the
advantage of using the RDBMS as the master for all data purposes. The same process can be applied
with Neo4j as the master data source.
222
Capabilities
15.4.1.Operational Availability
In order not to create a single point of failure, Neo4j supports different approaches which provide
transparent fallback and/or recovery from failures.
Online backup (Cold spare)
In this approach, a single instance of the master database is used, with Online Backup enabled. In
case of a failure, the backup files can be mounted onto a new Neo4j instance and reintegrated into the
application.
Online Backup High Availability (Hot spare)
Here, a Neo4j "backup" instance listens to online transfers of changes from the master. In the event of
a failure of the master, the backup is already running and can directly take over the load.
High Availability cluster
This approach uses a cluster of database instances, with one (read/write) master and a number of
(read-only) slaves. Failing slaves can simply be restarted and brought back online. Alternatively, a
new slave may be added by cloning an existing one. Should the master instance fail, a new master will
be elected by the remaining cluster nodes.
Capabilities
15.5.Capacity
15.5.1.File Sizes
Neo4j relies on Javas Non-blocking I/O subsystem for all file handling. Furthermore, while the
storage file layout is optimized for interconnected data, Neo4j does not require raw devices. Thus,
filesizes are only limited by the underlying operating systems capacity to handle large files.
Physically, there is no built-in limit of the file handling capacity in Neo4j.
Neo4j tries to memory-map as much of the underlying store files as possible. If the available RAM is
not sufficient to keep all data in RAM, Neo4j will use buffers in some cases, reallocating the memorymapped high-performance I/O windows to the regions with the most I/O activity dynamically. Thus,
ACID speed degrades gracefully as RAM becomes the limiting factor.
15.5.2.Read speed
Enterprises want to optimize the use of hardware to deliver the maximum business value from
available resources. Neo4js approach to reading data provides the best possible usage of all available
hardware resources. Neo4j does not block or lock any read operations; thus, there is no danger for
deadlocks in read operations and no need for read transactions. With a threaded read access to the
database, queries can be run simultaneously on as many processors as may be available. This provides
very good scale-up scenarios with bigger servers.
15.5.3.Write speed
Write speed is a consideration for many enterprise applications. However, there are two different
scenarios:
1. sustained continuous operation and
2. bulk access (e.g., backup, initial or batch loading).
To support the disparate requirements of these scenarios, Neo4j supports two modes of writing to the
storage layer.
In transactional, ACID-compliant normal operation, isolation level is maintained and read operations
can occur at the same time as the writing process. At every commit, the data is persisted to disk and
can be recovered to a consistent state upon system failures. This requires disk write access and a real
flushing of data. Thus, the write speed of Neo4j on a single server in continuous mode is limited
by the I/O capacity of the hardware. Consequently, the use of fast SSDs is highly recommended for
production scenarios.
Neo4j has a Batch Inserter that operates directly on the store files. This mode does not provide
transactional security, so it can only be used when there is a single write thread. Because data is
written sequentially, and never flushed to the logical logs, huge performance boosts are achieved. The
Batch Inserter is optimized for non-transactional bulk import of large amounts of data.
15.5.4.Data size
In Neo4j, data size is mainly limited by the address space of the primary keys for Nodes,
Relationships, Properties and RelationshipTypes. Currently, the address space is as follows:
nodes
relationships
235 ( 34 billion)
235 ( 34 billion)
224
Capabilities
properties
relationship types
225
Chapter16.Transaction Management
In order to fully maintain data integrity and ensure good transactional behavior, Neo4j supports the
ACID properties:
atomicity: If any part of a transaction fails, the database state is left unchanged.
consistency: Any transaction will leave the database in a consistent state.
isolation: During a transaction, modified data cannot be accessed by other operations.
durability: The DBMS can always recover the results of a committed transaction.
Specifically:
All database operations that access the graph, indexes, or the schema must be performed in a
transaction.
The default isolation level is READ_COMMITTED.
Data retrieved by traversals is not protected from modification by other transactions.
Non-repeatable reads may occur (i.e., only write locks are acquired and held until the end of the
transaction).
One can manually acquire write locks on nodes and relationships to achieve higher level of
isolation (SERIALIZABLE).
Locks are acquired at the Node and Relationship level.
Deadlock detection is built into the core transaction management.
226
Transaction Management
16.1.Interaction cycle
All database operations that access the graph, indexes, or the schema must be performed in a
transaction. Transactions are thread confined and can be nested as flat nested transactions.
Flat nested transactions means that all nested transactions are added to the scope of the top level
transaction. A nested transaction can mark the top level transaction for rollback, meaning the entire
transaction will be rolled back. To only rollback changes made in a nested transaction is not possible.
The interaction cycle of working with transactions looks like this:
1.
2.
3.
4.
Begin a transaction.
Perform database operations.
Mark the transaction as successful or not.
Finish the transaction.
It is very important to finish each transaction. The transaction will not release the locks or memory
it has acquired until it has been finished. The idiomatic use of transactions in Neo4j is to use a tryfinally block, starting the transaction and then try to perform the write operations. The last operation
in the try block should mark the transaction as successful while the finally block should finish the
transaction. Finishing the transaction will perform commit or rollback depending on the success
status.
Caution
All modifications performed in a transaction are kept in memory. This means that very
large updates have to be split into several top level transactions to avoid running out of
memory. It must be a top level transaction since splitting up the work in many nested
transactions will just add all the work to the top level transaction.
In an environment that makes use of thread pooling other errors may occur when failing to finish a
transaction properly. Consider a leaked transaction that did not get finished properly. It will be tied
to a thread and when that thread gets scheduled to perform work starting a new (what looks to be a)
top level transaction it will actually be a nested transaction. If the leaked transaction state is marked
for rollback (which will happen if a deadlock was detected) no more work can be performed on that
transaction. Trying to do so will result in error on each call to a write operation.
227
Transaction Management
16.2.Isolation levels
By default a read operation will read the last committed value unless a local modification within the
current transaction exist. The default isolation level is very similar to READ_COMMITTED: reads do not
block or take any locks so non-repeatable reads can occur. It is possible to achieve a stronger isolation
level (such as REPETABLE_READ and SERIALIZABLE) by manually acquiring read and write locks.
228
Transaction Management
229
Transaction Management
16.4.Deadlocks
Since locks are used it is possible for deadlocks to happen. Neo4j will however detect any deadlock
(caused by acquiring a lock) before they happen and throw an exception. Before the exception is
thrown the transaction is marked for rollback. All locks acquired by the transaction are still being held
but will be released when the transaction is finished (in the finally block as pointed out earlier). Once
the locks are released other transactions that were waiting for locks held by the transaction causing the
deadlock can proceed. The work performed by the transaction causing the deadlock can then be retried
by the user if needed.
Experiencing frequent deadlocks is an indication of concurrent write requests happening in such a
way that it is not possible to execute them while at the same time live up to the intended isolation
and consistency. The solution is to make sure concurrent updates happen in a reasonable way. For
example given two specific nodes (A and B), adding or deleting relationships to both these nodes in
random order for each transaction will result in deadlocks when there are two or more transactions
doing that concurrently. One solution is to make sure that updates always happens in the same order
(first A then B). Another solution is to make sure that each thread/transaction does not have any
conflicting writes to a node or relationship as some other concurrent transaction. This can for example
be achieved by letting a single thread do all updates of a specific type.
Important
Deadlocks caused by the use of other synchronization than the locks managed by
Neo4j can still happen. Since all operations in the Neo4j API are thread safe unless
specified otherwise, there is no need for external synchronization. Other code that requires
synchronization should be synchronized in such a way that it never performs any Neo4j
operation in the synchronized block.
230
Transaction Management
16.5.Delete semantics
When deleting a node or a relationship all properties for that entity will be automatically removed but
the relationships of a node will not be removed.
Caution
Neo4j enforces a constraint (upon commit) that all relationships must have a valid
start node and end node. In effect this means that trying to delete a node that still has
relationships attached to it will throw an exception upon commit. It is however possible
to choose in which order to delete the node and the attached relationships as long as no
relationships exist when the transaction is committed.
The delete semantics can be summarized in the following bullets:
All properties of a node or relationship will be removed when it is deleted.
A deleted node can not have any attached relationships when the transaction commits.
It is possible to acquire a reference to a deleted relationship or node that has not yet been
committed.
Any write operation on a node or relationship after it has been deleted (but not yet committed) will
throw an exception
After commit trying to acquire a new or work with an old reference to a deleted node or relationship
will throw an exception.
231
Transaction Management
16.6.1.Single thread
By using a single thread, no two threads will even try to create a particular entity simultaneously. On
High Availability, an external single-threaded client can perform the operations on the cluster.
16.6.2.Get or create
The preferred way to get or create a unique node is to use unique constraints and Cypher. See
Section32.11.1, Get or create unique node using Cypher and unique constraints for more
information.
By using put-if-absent <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/
Index.html#putIfAbsent%28T,%20java.lang.String,%20java.lang.Object%29> functionality, entity
uniqueness can be guaranteed using a legacy index. Here the legacy index acts as the lock and will
only lock the smallest part needed to guaranteed uniqueness across threads and transactions.
See Section32.11.2, Get or create unique node using a legacy index for how to do this using the
core Java API. When using the REST API, see Section19.19, Unique Indexing.
16.6.3.Pessimistic locking
Important
While this is a working solution, please consider using the preferred Section16.6.2, Get
or create instead.
By using explicit, pessimistic locking, unique creation of entities can be achieved in a multi-threaded
environment. It is most commonly done by locking on a single or a set of common nodes.
See Section32.11.3, Pessimistic locking for node creation for how to do this using the core Java
API.
232
Transaction Management
16.7.Transaction events
Transaction event handlers can be registered to receive Neo4j Transaction events. Once it has been
registered at a GraphDatabaseService instance it will receive events about what has happened in
each transaction which is about to be committed. Handlers wont get notified about transactions
which havent performed any write operation or wont be committed (either if Transactionsuccess()
hasnt been called or the transaction has been marked as failed Transactionfailure(). Right before
a transaction is about to be committed the beforeCommit method is called with the entire diff of
modifications made in the transaction. At this point the transaction is still running so changes can still
be made. However theres no guarantee that other handlers will see such changes since the order in
which handlers are executed is undefined. This method can also throw an exception and will, in such
a case, prevent the transaction from being committed (where a call to afterRollback will follow). If
beforeCommit is successfully executed the transaction will be committed and the afterCommit method
will be called with the same transaction data as well as the object returned from beforeCommit. This
assumes that all other handlers (if more were registered) also executed beforeCommit successfully.
233
Chapter17.Data Import
For importing data into Neo4j, see http://www.neo4j.org/develop/import.
For high-performance data import, see Chapter35, Batch Insertion.
234
Chapter18.Graph Algorithms
Neo4j graph algorithms is a component that contains Neo4j implementations of some common
algorithms for graphs. It includes algorithms like:
Shortest paths,
all paths,
all simple paths,
Dijkstra and
A*.
235
Graph Algorithms
18.1.Introduction
The graph algorithms are found in the neo4j-graph-algo component, which is included in the standard
Neo4j download.
Javadocs <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphalgo/packagesummary.html>
Download <http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.neo4j%22%20AND
%20a%3A%22neo4j-graph-algo%22>
Source code <https://github.com/neo4j/neo4j/tree/2.0.0/community/graph-algo>
For examples, see Section19.16, Graph Algorithms (REST API) and Section32.9, Graph
Algorithm examples (embedded database).
For information on how to use neo4j-graph-algo as a dependency with Maven and other dependency
management tools, see org.neo4j:neo4j-graph-algo <http://search.maven.org/search%7Cgav%7C1%7Cg
%3A%22org.neo4j%22%20AND%20a%3A%22neo4j-graph-algo%22> Note that it should be used with the same
version of org.neo4j:neo4j-kernel <http://search.maven.org/search%7Cgav%7C1%7Cg%3A%22org.neo4j
%22%20AND%20a%3A%22neo4j-kernel%22>. Different versions of the graph-algo and kernel components are
not compatible in the general case. Both components are included transitively by the org.neo4j:neo4j
<http://search.maven.org/search%7Cgav%7C1%7Cg%3A%22org.neo4j%22%20AND%20a%3A%22neo4j%22> artifact
which makes it simple to keep the versions in sync.
The starting point to find and use graph algorithms is GraphAlgoFactory <http://components.neo4j.org/
neo4j/2.0.0/apidocs/org/neo4j/graphalgo/GraphAlgoFactory.html> when using the Java Core API.
236
Chapter19.REST API
The Neo4j REST API is designed with discoverability in mind, so that you can start with a GET on the
Section19.3, Service root and from there discover URIs to perform other requests. The examples
below uses URIs in the examples; they are subject to change in the future, so for future-proofness
discover URIs where possible, instead of relying on the current layout. The default representation is
json <http://www.json.org/>, both for responses and for data sent with POST/PUT requests.
Below follows a listing of ways to interact with the REST API. For language bindings to the REST
API, see Chapter6, Languages.
To interact with the JSON interface you must explicitly set the request header Accept:application/json
for those requests that responds with data. You should also set the header Content-Type:application/
json if your request sends data, for example when youre creating a relationship. The examples include
the relevant request and response headers.
The server supports streaming results, with better performance and lower memory overhead. See
Section19.4, Streaming for more information.
237
REST API
The Neo4j transactional HTTP endpoint allows you to execute a series of Cypher statements within
the scope of a transaction. The transaction may be kept open across multiple HTTP requests, until
the client chooses to commit or roll back. Each HTTP request can include a list of statements, and for
convenience you can include statements along with a request to begin or commit a transaction.
The server guards against orphaned transactions by using a timeout. If there are no requests for a
given transaction within the timeout period, the server will roll it back. You can configure the timeout
in the server configuration, by setting org.neo4j.server.transaction.timeout to the number of seconds
before timeout. The default timeout is 60 seconds.
The key difference between the transactional HTTP endpoint and the Cypher endpoint (see
Section19.5, Cypher queries via REST) is the ability to use the same transaction across multiple
HTTP requests. The cypher endpoint always attempts to commit a transaction at the end of each
HTTP request.
Note
The serialization format for cypher results is mostly the same as the cypher endpoint.
However, the format for raw entities is slightly less verbose and does not include
hypermedia links.
Note
Open transactions are not shared among members of an HA cluster. Therefore, if you use
this endpoint in an HA cluster, you must ensure that all requests for a given transaction are
sent to the same Neo4j instance.
Tip
In order to speed up queries in repeated scenarios, try not to use literals but replace them
with parameters wherever possible in order to let the server cache query plans.
19.1.1.Begin a transaction
You begin a new transaction by posting zero or more Cypher statements to the transaction endpoint.
The server will respond with the result of your statements, as well as the location of your open
transaction.
Example request
POST http://localhost:7474/db/data/transaction
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"statements" : [ {
"statement" : "CREATE (n {props}) RETURN n",
"parameters" : {
"props" : {
"name" : "My Node"
}
}
} ]
}
Example response
201: Created
238
REST API
Content-Type: application/json
Location: http://localhost:7474/db/data/transaction/7
{
"commit" : "http://localhost:7474/db/data/transaction/7/commit",
"results" : [ {
"columns" : [ "n" ],
"data" : [ {
"row" : [ {
"name" : "My Node"
} ]
} ]
} ],
"transaction" : {
"expires" : "Sun, 08 Dec 2013 15:05:52 +0000"
},
"errors" : [ ]
}
Example response
200: OK
Content-Type: application/json
{
"commit" : "http://localhost:7474/db/data/transaction/9/commit",
"results" : [ {
"columns" : [ "n" ],
"data" : [ {
"row" : [ {
} ]
} ]
} ],
"transaction" : {
"expires" : "Sun, 08 Dec 2013 15:05:52 +0000"
},
"errors" : [ ]
}
REST API
the REST format will give back full Neo4j Rest API representations of the Neo4j Nodes, Relationships
and Paths, if returned.
Example request
POST http://localhost:7474/db/data/transaction/1
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"statements" : [ {
"statement" : "CREATE n RETURN n",
"resultDataContents" : [ "REST" ]
} ]
}
Example response
200: OK
Content-Type: application/json
{
"commit" : "http://localhost:7474/db/data/transaction/1/commit",
"results" : [ {
"columns" : [ "n" ],
"data" : [ {
"rest" : [ {
"paged_traverse" : "http://localhost:7474/db/data/node/12/paged/traverse/{returnType}{?pageSize,leaseTime}",
"labels" : "http://localhost:7474/db/data/node/12/labels",
"outgoing_relationships" : "http://localhost:7474/db/data/node/12/relationships/out",
"traverse" : "http://localhost:7474/db/data/node/12/traverse/{returnType}",
"all_typed_relationships" : "http://localhost:7474/db/data/node/12/relationships/all/{-list|&|types}",
"property" : "http://localhost:7474/db/data/node/12/properties/{key}",
"all_relationships" : "http://localhost:7474/db/data/node/12/relationships/all",
"self" : "http://localhost:7474/db/data/node/12",
"properties" : "http://localhost:7474/db/data/node/12/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/12/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/12/relationships/in",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/12/relationships/in/{-list|&|types}",
"create_relationship" : "http://localhost:7474/db/data/node/12/relationships",
"data" : {
}
} ]
} ]
} ],
"transaction" : {
"expires" : "Sun, 08 Dec 2013 15:05:48 +0000"
},
"errors" : [ ]
}
REST API
Example request
POST http://localhost:7474/db/data/transaction/2
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"statements" : [ ]
}
Example response
200: OK
Content-Type: application/json
{
"commit" : "http://localhost:7474/db/data/transaction/2/commit",
"results" : [ ],
"transaction" : {
"expires" : "Sun, 08 Dec 2013 15:05:51 +0000"
},
"errors" : [ ]
}
Example response
200: OK
Content-Type: application/json
{
"results" : [ {
"columns" : [ "id(n)" ],
"data" : [ {
"row" : [ 14 ]
} ]
} ],
"errors" : [ ]
}
REST API
Example request
DELETE http://localhost:7474/db/data/transaction/3
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"results" : [ ],
"errors" : [ ]
}
Example response
200: OK
Content-Type: application/json
{
"results" : [ {
"columns" : [ "id(n)" ],
"data" : [ {
"row" : [ 15 ]
} ]
} ],
"errors" : [ ]
}
REST API
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"statements" : [ {
"statement" : "CREATE ( bike:Bike { weight: 10 } )CREATE ( frontWheel:Wheel { spokes: 3 } )CREATE ( backWheel:Wheel { spokes: 32
"resultDataContents" : [ "row", "graph" ]
} ]
}
Example response
200: OK
Content-Type: application/json
{
"results" : [ {
"columns" : [ "bike", "p1", "p2" ],
"data" : [ {
"row" : [ {
"weight" : 10
}, [ {
"weight" : 10
}, {
"position" : 1
}, {
"spokes" : 3
} ], [ {
"weight" : 10
}, {
"position" : 2
}, {
"spokes" : 32
} ] ],
"graph" : {
"nodes" : [ {
"id" : "17",
"labels" : [ "Wheel" ],
"properties" : {
"spokes" : 3
}
}, {
"id" : "16",
"labels" : [ "Bike" ],
"properties" : {
"weight" : 10
}
}, {
"id" : "18",
"labels" : [ "Wheel" ],
"properties" : {
"spokes" : 32
}
} ],
"relationships" : [ {
"id" : "9",
"type" : "HAS",
"startNode" : "16",
"endNode" : "17",
"properties" : {
"position" : 1
}
}, {
243
REST API
"id" : "10",
"type" : "HAS",
"startNode" : "16",
"endNode" : "18",
"properties" : {
"position" : 2
}
} ]
}
} ]
} ],
"errors" : [ ]
}
19.1.9.Handling errors
The result of any request against the transaction endpoint is streamed back to the client. Therefore
the server does not know whether the request will be successful or not when it sends the HTTP status
code.
Because of this, all requests against the transactional endpoint will return 200 or 201 status code,
regardless of whether statements were successfully executed. At the end of the response payload,
the server includes a list of errors that occurred while executing statements. If this list is empty, the
request completed successfully.
If any errors occur while executing statements, the server will roll back the transaction.
In this example, we send the server an invalid statement to demonstrate error handling.
For more information on the status codes, see Section19.2, Neo4j Status Codes.
Example request
POST http://localhost:7474/db/data/transaction/8/commit
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"statements" : [ {
"statement" : "This is not a valid Cypher Statement."
} ]
}
Example response
200: OK
Content-Type: application/json
{
"results" : [ ],
"errors" : [ {
"code" : "Neo.ClientError.Statement.InvalidSyntax",
"message" : "Invalid input 'T': expected SingleStatement (line 1, column 1)\n\"This is not a valid Cypher Statement.\"\n ^"
} ]
}
244
REST API
The transactional endpoint may in any response include zero or more status codes, indicating issues or
information for the client. Each status code follows the same format: "Neo.[Classification].[Category].
[Title]". The fact that a status code is returned by the server does always mean there is a fatal error.
Status codes can also indicate transient problems that may go away if you retry the request.
What the effect of the status code is can be determined by its classification.
Note
This is not the same thing as HTTP status codes. Neo4j Status Codes are returned in the
response body, at the very end of the response.
19.2.1.Classifications
Classification
Description
Effect on
transaction
ClientError
None
DatabaseError
Rollback
TransientError
None
19.2.2.Status codes
This is a complete list of all status codes Neo4j may return, and what they mean.
Status Code
Description
REST API
Status Code
Description
246
REST API
19.3.Service root
19.3.1.Get service root
The service root is your starting point to discover the REST API. It contains the basic starting points
for the database, and some version and extension information.
Figure19.1.Final Graph
Example request
GET http://localhost:7474/db/data/
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"node" : "http://localhost:7474/db/data/node",
"node_index" : "http://localhost:7474/db/data/index/node",
"relationship_index" : "http://localhost:7474/db/data/index/relationship",
"extensions_info" : "http://localhost:7474/db/data/ext",
"relationship_types" : "http://localhost:7474/db/data/relationship/types",
"batch" : "http://localhost:7474/db/data/batch",
"cypher" : "http://localhost:7474/db/data/cypher",
"transaction" : "http://localhost:7474/db/data/transaction",
"neo4j_version" : "2.0.0"
}
247
REST API
19.4.Streaming
All responses from the REST API can be transmitted as JSON streams, resulting in better performance
and lower memory overhead on the server side. To use streaming, supply the header X-Stream: true
with each request.
Figure19.2.Final Graph
Example request
GET http://localhost:7474/db/data/
Accept: application/json
X-Stream: true
Example response
200: OK
Content-Type: application/json; charset=UTF-8; stream=true
{
"extensions" : {
},
"node" : "http://localhost:7474/db/data/node",
"node_index" : "http://localhost:7474/db/data/index/node",
"relationship_index" : "http://localhost:7474/db/data/index/relationship",
"extensions_info" : "http://localhost:7474/db/data/ext",
"relationship_types" : "http://localhost:7474/db/data/relationship/types",
"batch" : "http://localhost:7474/db/data/batch",
"cypher" : "http://localhost:7474/db/data/cypher",
"transaction" : "http://localhost:7474/db/data/transaction",
"neo4j_version" : "2.0.0"
}
248
REST API
Tip
In order to speed up queries in repeated scenarios, try not to use literals but replace
them with parameters wherever possible in order to let the server cache query plans, see
Section19.5.1, Use parameters for details. Also see Section7.5, Parameters for where
parameters can be used.
19.5.1.Use parameters
Cypher supports queries with parameters which are submitted as JSON.
MATCH (x { name: { startName }})-[r]-(friend)
WHERE friend.name = { name }
RETURN TYPE(r)
Figure19.3.Final Graph
Node[ 61] nam e = 'I'
know
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "MATCH (x {name: {startName}})-[r]-(friend) WHERE friend.name = {name} RETURN TYPE(r)",
"params" : {
"startName" : "I",
"name" : "you"
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "TYPE(r)" ],
"data" : [ [ "know" ] ]
}
249
REST API
19.5.2.Create a node
Create a node with a label and a property using Cypher. See the request for the parameter sent with the
query.
CREATE (n:Person { name : { name }})
RETURN n
Figure19.4.Final Graph
Node[ 47] : Person
nam e = 'Andres'
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "CREATE (n:Person { name : {name} }) RETURN n",
"params" : {
"name" : "Andres"
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "n" ],
"data" : [ [ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/47/relationships/out",
"labels" : "http://localhost:7474/db/data/node/47/labels",
"data" : {
"name" : "Andres"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/47/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/47/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/47",
"property" : "http://localhost:7474/db/data/node/47/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/47/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/47/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/47/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/47/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/47/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/47/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/47/relationships/in/{-list|&|types}"
} ] ]
}
REST API
CREATE (n:Person { props })
RETURN n
Figure19.5.Final Graph
Node[ 44] : Person
nam e = 'Michael'
posit ion = 'Developer'
awesom e = t rue
children = 3
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "CREATE (n:Person { props } ) RETURN n",
"params" : {
"props" : {
"position" : "Developer",
"name" : "Michael",
"awesome" : true,
"children" : 3
}
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "n" ],
"data" : [ [ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/44/relationships/out",
"labels" : "http://localhost:7474/db/data/node/44/labels",
"data" : {
"position" : "Developer",
"awesome" : true,
"name" : "Michael",
"children" : 3
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/44/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/44/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/44",
"property" : "http://localhost:7474/db/data/node/44/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/44/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/44/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/44/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/44/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/44/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/44/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/44/relationships/in/{-list|&|types}"
} ] ]
}
251
REST API
Figure19.6.Final Graph
Node[ 48] : Person
nam e = 'Andres'
posit ion = 'Developer'
nam e = 'Michael'
posit ion = 'Developer'
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "CREATE (n:Person { props } ) RETURN n",
"params" : {
"props" : [ {
"name" : "Andres",
"position" : "Developer"
}, {
"name" : "Michael",
"position" : "Developer"
} ]
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "n" ],
"data" : [ [ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/48/relationships/out",
"labels" : "http://localhost:7474/db/data/node/48/labels",
"data" : {
"position" : "Developer",
"name" : "Andres"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/48/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/48/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/48",
"property" : "http://localhost:7474/db/data/node/48/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/48/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/48/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/48/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/48/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/48/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/48/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/48/relationships/in/{-list|&|types}"
252
REST API
} ], [ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/49/relationships/out",
"labels" : "http://localhost:7474/db/data/node/49/labels",
"data" : {
"position" : "Developer",
"name" : "Michael"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/49/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/49/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/49",
"property" : "http://localhost:7474/db/data/node/49/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/49/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/49/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/49/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/49/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/49/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/49/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/49/relationships/in/{-list|&|types}"
} ] ]
}
Figure19.7.Final Graph
Node[ 75] : Person
posit ion = 'Developer'
awesom e = t rue
children = 3
first Nam e = 'Michael'
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "CREATE (n:Person { name: 'this property is to be deleted' } ) SET n = { props } RETURN n",
"params" : {
"props" : {
"position" : "Developer",
"firstName" : "Michael",
"awesome" : true,
"children" : 3
}
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
253
REST API
{
"columns" : [ "n" ],
"data" : [ [ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/75/relationships/out",
"labels" : "http://localhost:7474/db/data/node/75/labels",
"data" : {
"position" : "Developer",
"awesome" : true,
"children" : 3,
"firstName" : "Michael"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/75/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/75/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/75",
"property" : "http://localhost:7474/db/data/node/75/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/75/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/75/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/75/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/75/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/75/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/75/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/75/relationships/in/{-list|&|types}"
} ] ]
}
19.5.6.Send a query
A simple query returning all nodes connected to some node, returning the node and the name property,
if it exists, otherwise NULL:
MATCH (x { name: 'I' })-[r]->(n)
RETURN type(r), n.name, n.age
Figure19.8.Final Graph
Node[ 69] nam e = 'I'
know
know
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "MATCH (x {name: 'I'})-[r]->(n) RETURN type(r), n.name, n.age",
"params" : {
}
}
Example response
254
REST API
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "type(r)", "n.name", "n.age" ],
"data" : [ [ "know", "him", 25 ], [ "know", "you", null ] ]
}
19.5.7.Return paths
Paths can be returned just like other return types.
MATCH path =(x { name: 'I' })--(friend)
RETURN path, friend.name
Figure19.9.Final Graph
Node[ 74] nam e = 'I'
know
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "MATCH path = (x {name: 'I'})--(friend) RETURN path, friend.name",
"params" : {
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "path", "friend.name" ],
"data" : [ [ {
"start" : "http://localhost:7474/db/data/node/74",
"nodes" : [ "http://localhost:7474/db/data/node/74", "http://localhost:7474/db/data/node/73" ],
"length" : 1,
"relationships" : [ "http://localhost:7474/db/data/relationship/20" ],
"end" : "http://localhost:7474/db/data/node/73"
}, "you" ] ]
}
19.5.8.Nested results
When sending queries that return nested results like list and maps, these will get serialized into nested
JSON representations according to their types.
255
REST API
MATCH (n)
WHERE n.name IN ['I', 'you']
RETURN collect(n.name)
Figure19.10.Final Graph
Node[ 71] nam e = 'I'
know
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "MATCH (n) WHERE n.name in ['I', 'you'] RETURN collect(n.name)",
"params" : {
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "collect(n.name)" ],
"data" : [ [ [ "you", "I" ] ] ]
}
Figure19.11.Final Graph
Node[ 72] : Act or
nam e = 'I'
Example request
POST http://localhost:7474/db/data/cypher?includeStats=true
Accept: application/json; charset=UTF-8
256
REST API
Content-Type: application/json
{
"query" : "MATCH (n {name: 'I'}) SET n:Actor REMOVE n:Director RETURN labels(n)",
"params" : {
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "labels(n)" ],
"data" : [ [ [ "Actor" ] ] ],
"stats" : {
"relationships_created" : 0,
"nodes_deleted" : 0,
"relationship_deleted" : 0,
"indexes_added" : 0,
"properties_set" : 0,
"constraints_removed" : 0,
"indexes_removed" : 0,
"labels_removed" : 1,
"constraints_added" : 0,
"labels_added" : 1,
"nodes_created" : 0,
"contains_updates" : true
}
}
19.5.10.Errors
Errors on the server will be reported as a JSON-formatted message, exception name and stacktrace.
MATCH (x { name: 'I' })
RETURN x.dummy/0
Figure19.12.Final Graph
Node[ 54] nam e = 'I'
know
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "MATCH (x {name: 'I'}) RETURN x.dummy/0",
257
REST API
"params" : {
}
}
Example response
400: Bad Request
Content-Type: application/json; charset=UTF-8
{
258
REST API
19.6.Property values
The REST API allows setting properties on nodes and relationships through direct RESTful
operations. However, there are restrictions as to what types of values can be used as property values.
Allowed value types are as follows:
Numbers: Both integer values, with capacity as Javas Long type, and floating points, with capacity
as Javas Double.
Booleans.
Strings.
Arrays of the basic types above.
19.6.1.Arrays
There are two important points to be made about array values. First, all values in the array must be of
the same type. That means either all integers, all floats, all booleans or all strings. Mixing types is not
currently supported.
Second, storing empty arrays is only possible given certain preconditions. Because the JSON transfer
format does not contain type information for arrays, type is inferred from the values in the array. If
the array is empty, the Neo4j Server cannot determine the type. In these cases, it will check if an array
is already stored for the given property, and will use the stored arrays type when storing the empty
array. If no array exists already, the server will reject the request.
19.6.2.Property keys
You can list all property keys ever used in the database. This includes and property keys you have
used, but deleted.
There is currently no way to tell which ones are in use and which ones are not, short of walking the
entire set of properties in the database.
[ "firstName", "some-key", "sequence", "awesome", "prop", "cost", "non-existent", "", "value", "key", "array1", "ID", "array
259
REST API
19.7.Nodes
19.7.1.Create node
Figure19.13.Final Graph
Node[ 8]
Example request
POST http://localhost:7474/db/data/node
Accept: application/json; charset=UTF-8
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/node/8
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/8/relationships/out",
"labels" : "http://localhost:7474/db/data/node/8/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/8/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/8/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/8",
"property" : "http://localhost:7474/db/data/node/8/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/8/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/8/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/8/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/8/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/8/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/8/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/8/relationships/in/{-list|&|types}",
"data" : {
}
}
Example request
POST http://localhost:7474/db/data/node
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"foo" : "bar"
260
REST API
}
Example response
201: Created
Content-Length: 1156
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/node/4
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/4/relationships/out",
"labels" : "http://localhost:7474/db/data/node/4/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/4/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/4/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/4",
"property" : "http://localhost:7474/db/data/node/4/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/4/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/4/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/4/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/4/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/4/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/4/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/4/relationships/in/{-list|&|types}",
"data" : {
"foo" : "bar"
}
}
19.7.3.Get node
Note that the response contains URI/templates for the available operations for getting properties and
relationships.
Figure19.15.Final Graph
Node[ 30]
Example request
GET http://localhost:7474/db/data/node/30
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/30/relationships/out",
"labels" : "http://localhost:7474/db/data/node/30/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/30/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/30/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/30",
261
REST API
"property" : "http://localhost:7474/db/data/node/30/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/30/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/30/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/30/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/30/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/30/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/30/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/30/relationships/in/{-list|&|types}",
"data" : {
}
}
Example request
GET http://localhost:7474/db/data/node/3400000
Accept: application/json; charset=UTF-8
Example response
404: Not Found
Content-Type: application/json; charset=UTF-8
{
"message": "Cannot find node with id [3400000] in database.",
"exception": "NodeNotFoundException",
"fullname": "org.neo4j.server.rest.web.NodeNotFoundException",
"stacktrace": [
"org.neo4j.server.rest.web.DatabaseActions.node(DatabaseActions.java:183)",
"org.neo4j.server.rest.web.DatabaseActions.getNode(DatabaseActions.java:228)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.getNode(RestfulGraphDatabase.java:266)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
19.7.5.Delete node
Figure19.17.Final Graph
Example request
DELETE http://localhost:7474/db/data/node/5
Accept: application/json; charset=UTF-8
Example response
204: No Content
262
REST API
LOVES
Node[ 13]
Example request
DELETE http://localhost:7474/db/data/node/12
Accept: application/json; charset=UTF-8
Example response
409: Conflict
Content-Type: application/json; charset=UTF-8
{
"message": "The node with id 12 cannot be deleted. Check that the node is orphaned before deletion.",
"exception": "OperationFailureException",
"fullname": "org.neo4j.server.rest.web.OperationFailureException",
"stacktrace": [
"org.neo4j.server.rest.web.DatabaseActions.deleteNode(DatabaseActions.java:237)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.deleteNode(RestfulGraphDatabase.java:280)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
263
REST API
19.8.Relationships
Relationships are a first class citizen in the Neo4j REST API. They can be accessed either stand-alone
or through the nodes they are attached to.
The general pattern to get relationships from a node is:
GET http://localhost:7474/db/data/node/123/relationships/{dir}/{-list|&|types}
Where dir is one of all, in, out and types is an ampersand-separated list of types. See the examples
below for more information.
19.8.1.Get Relationship by ID
Figure19.19.Final Graph
Node[ 139] nam e = 'I'
know
Example request
GET http://localhost:7474/db/data/relationship/30
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/139",
"property" : "http://localhost:7474/db/data/relationship/30/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/30",
"properties" : "http://localhost:7474/db/data/relationship/30/properties",
"type" : "know",
"end" : "http://localhost:7474/db/data/node/138",
"data" : {
}
}
19.8.2.Create relationship
Upon successful creation of a relationship, the new relationship is returned.
264
REST API
Figure19.20.Final Graph
Node[ 1] nam e = 'Joe'
knows LOVES
Example request
POST http://localhost:7474/db/data/node/1/relationships
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/0",
"type" : "LOVES"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/relationship/1
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/1",
"property" : "http://localhost:7474/db/data/relationship/1/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/1",
"properties" : "http://localhost:7474/db/data/relationship/1/properties",
"type" : "LOVES",
"end" : "http://localhost:7474/db/data/node/0",
"data" : {
}
}
knows
265
REST API
Figure19.22.Final Graph
Node[ 11] nam e = 'Joe'
knows
LOVES
foo = 'bar'
Example request
POST http://localhost:7474/db/data/node/11/relationships
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/10",
"type" : "LOVES",
"data" : {
"foo" : "bar"
}
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/relationship/8
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/11",
"property" : "http://localhost:7474/db/data/relationship/8/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/8",
"properties" : "http://localhost:7474/db/data/relationship/8/properties",
"type" : "LOVES",
"end" : "http://localhost:7474/db/data/node/10",
"data" : {
"foo" : "bar"
}
}
266
REST API
19.8.4.Delete relationship
Figure19.23.Starting Graph
Node[ 125] nam e = 'Rom eo'
LOVES
cost = 'high'
Figure19.24.Final Graph
Node[ 124] nam e = 'Juliet '
Example request
DELETE http://localhost:7474/db/data/relationship/23
Accept: application/json; charset=UTF-8
Example response
204: No Content
LOVES
since = '1day'
cost = 'high'
Example request
GET http://localhost:7474/db/data/relationship/26/properties
Accept: application/json; charset=UTF-8
Example response
267
REST API
200: OK
Content-Type: application/json; charset=UTF-8
{
"cost" : "high",
"since" : "1day"
}
LOVES
cost = 'high'
Figure19.27.Final Graph
Node[ 141] nam e = 'Rom eo'
LOVES
happy = false
Example request
PUT http://localhost:7474/db/data/relationship/31/properties
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"happy" : false
}
Example response
204: No Content
268
REST API
LOVES
cost = 'high'
Example request
GET http://localhost:7474/db/data/relationship/27/properties/cost
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
"high"
LOVES
cost = 'high'
Figure19.30.Final Graph
Node[ 129] nam e = 'Rom eo'
LOVES
cost = 'deadly'
269
REST API
Example request
PUT http://localhost:7474/db/data/relationship/25/properties/cost
Accept: application/json; charset=UTF-8
Content-Type: application/json
"deadly"
Example response
204: No Content
Node[ 303]
LIKES
Node[ 299]
LIKES
HATES
Node[ 300]
Node[ 302]
Example request
GET http://localhost:7474/db/data/node/299/relationships/all
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/299",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/205",
"property" : "http://localhost:7474/db/data/relationship/205/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/205/properties",
"type" : "LIKES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/300"
}, {
270
REST API
"start" : "http://localhost:7474/db/data/node/301",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/206",
"property" : "http://localhost:7474/db/data/relationship/206/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/206/properties",
"type" : "LIKES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/299"
}, {
"start" : "http://localhost:7474/db/data/node/299",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/207",
"property" : "http://localhost:7474/db/data/relationship/207/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/207/properties",
"type" : "HATES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/302"
} ]
Node[ 322]
LIKES
Node[ 318]
LIKES
HATES
Node[ 319]
Node[ 321]
Example request
GET http://localhost:7474/db/data/node/318/relationships/in
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/320",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/217",
271
REST API
"property" : "http://localhost:7474/db/data/relationship/217/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/217/properties",
"type" : "LIKES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/318"
} ]
Node[ 347]
LIKES
Node[ 343]
LIKES
HATES
Node[ 344]
Node[ 346]
Example request
GET http://localhost:7474/db/data/node/343/relationships/out
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/343",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/231",
"property" : "http://localhost:7474/db/data/relationship/231/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/231/properties",
"type" : "LIKES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/344"
}, {
"start" : "http://localhost:7474/db/data/node/343",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/233",
"property" : "http://localhost:7474/db/data/relationship/233/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/233/properties",
"type" : "HATES",
"extensions" : {
272
REST API
},
"end" : "http://localhost:7474/db/data/node/346"
} ]
Node[ 278]
LIKES
Node[ 274]
LIKES
HATES
Node[ 275]
Node[ 277]
Example request
GET http://localhost:7474/db/data/node/274/relationships/all/LIKES&HATES
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/274",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/190",
"property" : "http://localhost:7474/db/data/relationship/190/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/190/properties",
"type" : "LIKES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/275"
}, {
"start" : "http://localhost:7474/db/data/node/276",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/191",
"property" : "http://localhost:7474/db/data/relationship/191/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/191/properties",
"type" : "LIKES",
"extensions" : {
},
273
REST API
"end" : "http://localhost:7474/db/data/node/274"
}, {
"start" : "http://localhost:7474/db/data/node/274",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/192",
"property" : "http://localhost:7474/db/data/relationship/192/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/192/properties",
"type" : "HATES",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/277"
} ]
Node[ 332]
LIKES
Node[ 328]
LIKES
HATES
Node[ 329]
Node[ 331]
Example request
GET http://localhost:7474/db/data/node/332/relationships/all
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ ]
274
REST API
19.9.Relationship types
19.9.1.Get relationship types
Example request
GET http://localhost:7474/db/data/relationship/types
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json
[ "know", "KNOWS", "LOVES" ]
275
REST API
19.10.Node properties
19.10.1.Set property on node
Setting different properties will retain the existing ones for this node. Note that a single value are
submitted not as a map but just as a value (which is valid JSON) like in the example below.
Figure19.36.Final Graph
Node[ 11] foo = 'bar'
foo2 = 'bar2'
Example request
PUT http://localhost:7474/db/data/node/11/properties/foo
Accept: application/json; charset=UTF-8
Content-Type: application/json
"bar"
Example response
204: No Content
knows
Example request
PUT http://localhost:7474/db/data/node/9/properties
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"age" : "18"
}
Example response
204: No Content
276
REST API
Example request
GET http://localhost:7474/db/data/node/36/properties
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"foo" : "bar"
}
Example response
400: Bad Request
Content-Type: application/json; charset=UTF-8
{
"message": "Could not set property \"foo\", unsupported type: null",
"exception": "PropertyValueException",
"fullname": "org.neo4j.server.rest.web.PropertyValueException",
"stacktrace": [
"org.neo4j.server.rest.domain.PropertySettingStrategy.setProperty(PropertySettingStrategy.java:141)",
"org.neo4j.server.rest.domain.PropertySettingStrategy.setProperties(PropertySettingStrategy.java:88)",
"org.neo4j.server.rest.web.DatabaseActions.createNode(DatabaseActions.java:214)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.createNode(RestfulGraphDatabase.java:239)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
277
REST API
Example response
400: Bad Request
Content-Type: application/json; charset=UTF-8
{
"message": "Could not set property \"foo\", unsupported type: {bar\u003dbaz}",
"exception": "PropertyValueException",
"fullname": "org.neo4j.server.rest.web.PropertyValueException",
"stacktrace": [
"org.neo4j.server.rest.domain.PropertySettingStrategy.setProperty(PropertySettingStrategy.java:141)",
"org.neo4j.server.rest.domain.PropertySettingStrategy.setProperties(PropertySettingStrategy.java:88)",
"org.neo4j.server.rest.web.DatabaseActions.createNode(DatabaseActions.java:214)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.createNode(RestfulGraphDatabase.java:239)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
Example request
DELETE http://localhost:7474/db/data/node/251/properties
Accept: application/json; charset=UTF-8
Example response
204: No Content
REST API
Figure19.40.Starting Graph
Node[ 250] nam e = 't obias'
Figure19.41.Final Graph
Node[ 250]
Example request
DELETE http://localhost:7474/db/data/node/250/properties/name
Accept: application/json; charset=UTF-8
Example response
204: No Content
279
REST API
19.11.Relationship properties
19.11.1.Update relationship properties
Figure19.42.Final Graph
Node[ 20]
KNOWS
jim = 't obias'
Node[ 21]
Example request
PUT http://localhost:7474/db/data/relationship/9/properties
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"jim" : "tobias"
}
Example response
204: No Content
LOVES
Example request
DELETE http://localhost:7474/db/data/relationship/21/properties
Accept: application/json; charset=UTF-8
Example response
204: No Content
280
REST API
LOVES
cost = 'high'
Figure19.45.Final Graph
Node[ 127] nam e = 'Rom eo'
LOVES
Example request
DELETE http://localhost:7474/db/data/relationship/24/properties/cost
Accept: application/json; charset=UTF-8
Example response
204: No Content
LOVES
cost = 'high'
281
REST API
Example request
DELETE http://localhost:7474/db/data/relationship/22/properties/non-existent
Accept: application/json; charset=UTF-8
Example response
404: Not Found
Content-Type: application/json; charset=UTF-8
{
"message": "Relationship[22] does not have a property \"non-existent\"",
"exception": "NoSuchPropertyException",
"fullname": "org.neo4j.server.rest.web.NoSuchPropertyException",
"stacktrace": [
"org.neo4j.server.rest.web.DatabaseActions.removeRelationshipProperty(DatabaseActions.java:657)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.deleteRelationshipProperty(RestfulGraphDatabase.java:790)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
know
Example request
DELETE http://localhost:7474/db/data/relationship/1234/properties
Accept: application/json; charset=UTF-8
Example response
404: Not Found
Content-Type: application/json; charset=UTF-8
{
"exception": "RelationshipNotFoundException",
"fullname": "org.neo4j.server.rest.web.RelationshipNotFoundException",
"stacktrace": [
"org.neo4j.server.rest.web.DatabaseActions.relationship(DatabaseActions.java:197)",
"org.neo4j.server.rest.web.DatabaseActions.removeAllRelationshipProperties(DatabaseActions.java:647)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.deleteAllRelationshipProperties(RestfulGraphDatabase.java:769)",
282
REST API
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
know
Example request
DELETE http://localhost:7474/db/data/relationship/1234/properties/cost
Accept: application/json; charset=UTF-8
Example response
404: Not Found
Content-Type: application/json; charset=UTF-8
{
"exception": "RelationshipNotFoundException",
"fullname": "org.neo4j.server.rest.web.RelationshipNotFoundException",
"stacktrace": [
"org.neo4j.server.rest.web.DatabaseActions.relationship(DatabaseActions.java:197)",
"org.neo4j.server.rest.web.DatabaseActions.removeRelationshipProperty(DatabaseActions.java:653)",
"org.neo4j.server.rest.web.RestfulGraphDatabase.deleteRelationshipProperty(RestfulGraphDatabase.java:790)",
"java.lang.reflect.Method.invoke(Method.java:606)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:132)",
"org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
"java.lang.Thread.run(Thread.java:724)"
]
}
283
REST API
19.12.Node labels
19.12.1.Adding a label to a node
Figure19.49.Final Graph
Node[ 101] : Person
nam e = 'Clint East wood'
Example request
POST http://localhost:7474/db/data/node/101/labels
Accept: application/json; charset=UTF-8
Content-Type: application/json
"Person"
Example response
204: No Content
Example request
POST http://localhost:7474/db/data/node/112/labels
Accept: application/json; charset=UTF-8
Content-Type: application/json
[ "Person", "Actor" ]
Example response
204: No Content
284
REST API
Example request
POST http://localhost:7474/db/data/node/119/labels
Accept: application/json; charset=UTF-8
Content-Type: application/json
""
Example response
400: Bad Request
Content-Type: application/json; charset=UTF-8
{
REST API
Figure19.52.Final Graph
Node[ 102] : Direct or, Act or
nam e = 'Clint East wood'
Example request
PUT http://localhost:7474/db/data/node/102/labels
Accept: application/json; charset=UTF-8
Content-Type: application/json
[ "Actor", "Director" ]
Example response
204: No Content
Example request
DELETE http://localhost:7474/db/data/node/103/labels/Person
Accept: application/json; charset=UTF-8
Example response
204: No Content
Example request
GET http://localhost:7474/db/data/node/108/labels
Accept: application/json; charset=UTF-8
Example response
200: OK
286
REST API
Content-Type: application/json; charset=UTF-8
[ "Director", "Actor" ]
Example request
GET http://localhost:7474/db/data/label/Actor/nodes
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/114/relationships/out",
"labels" : "http://localhost:7474/db/data/node/114/labels",
"data" : {
"name" : "Clint Eastwood"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/114/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/114/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/114",
"property" : "http://localhost:7474/db/data/node/114/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/114/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/114/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/114/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/114/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/114/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/114/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/114/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/115/relationships/out",
"labels" : "http://localhost:7474/db/data/node/115/labels",
"data" : {
"name" : "Donald Sutherland"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/115/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/115/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/115",
"property" : "http://localhost:7474/db/data/node/115/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/115/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/115/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/115/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/115/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/115/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/115/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/115/relationships/in/{-list|&|types}"
} ]
287
REST API
Example request
GET http://localhost:7474/db/data/label/Person/nodes?name=%22Clint+Eastwood%22
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/117/relationships/out",
"labels" : "http://localhost:7474/db/data/node/117/labels",
"data" : {
"name" : "Clint Eastwood"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/117/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/117/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/117",
"property" : "http://localhost:7474/db/data/node/117/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/117/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/117/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/117/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/117/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/117/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/117/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/117/relationships/in/{-list|&|types}"
} ]
Example request
GET http://localhost:7474/db/data/labels
Accept: application/json; charset=UTF-8
288
REST API
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ "Actor", "mylabel", "novice", "expert", "person", "SomeLabel", "coder", "Person", "user", "chef", "Director" ]
289
REST API
19.13.Indexing
Note
This documents schema based indexes, a feature that was introduced in Neo4j 2.0, see
Section19.18, Legacy indexing for legacy indexing.
For more details about indexes and the optional schema in Neo4j, see Section3.7, Schema.
19.13.1.Create index
This will start a background job in the database that will create and populate the index. You can check
the status of your index by listing all the indexes for the relevant label. The created index will show
up, but have a state of POPULATING until the index is ready, where it is marked as ONLINE.
Example request
POST http://localhost:7474/db/data/schema/index/person
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"property_keys" : [ "name" ]
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"label" : "person",
"property_keys" : [ "name" ]
}
19.13.3.Drop index
Drop index
Example request
290
REST API
DELETE http://localhost:7474/db/data/schema/index/SomeLabel/name
Accept: application/json; charset=UTF-8
Example response
204: No Content
291
REST API
19.14.Constraints
19.14.1.Create uniqueness constraint
Create a uniqueness constraint on a property.
Example request
POST http://localhost:7474/db/data/schema/constraint/person/uniqueness/
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"property_keys" : [ "name" ]
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"label" : "person",
"type" : "UNIQUENESS",
"property_keys" : [ "name" ]
}
REST API
Content-Type: application/json; charset=UTF-8
[ {
"property_keys" : [ "name2" ],
"label" : "user",
"type" : "UNIQUENESS"
}, {
"property_keys" : [ "name1" ],
"label" : "user",
"type" : "UNIQUENESS"
} ]
19.14.6.Drop constraint
Drop uniqueness constraint for a label and a property.
293
REST API
Example request
DELETE http://localhost:7474/db/data/schema/constraint/SomeLabel/uniqueness/name
Accept: application/json; charset=UTF-8
Example response
204: No Content
294
REST API
19.15.Traversals
Warning
The Traversal REST Endpoint executes arbitrary Javascript code under the hood as part of
the evaluators definitions. In hosted and open environments, this can constitute a security
risk. In these case, consider using declarative approaches like PartIII, Cypher Query
Language or write your own server side plugin executing the interesting traversals with
the Java API ( see Section31.1, Server Plugins ) or secure your server, see Chapter25,
Security.
Traversals are performed from a start node. The traversal is controlled by the URI and the body sent
with the request.
returnType
The kind of objects in the response is determined by traverse/{returnType} in the URL. returnType
can have one of these values:
node
relationship
path: contains full representations of start and end node, the rest are URIs.
fullpath: contains full representations of all nodes and relationships.
To decide how the graph should be traversed you can use these parameters in the request body:
order
Decides in which order to visit nodes. Possible values:
breadth_first: see Breadth-first search <http://en.wikipedia.org/wiki/Breadth-first_search>.
depth_first: see Depth-first search <http://en.wikipedia.org/wiki/Depth-first_search>
relationships
Decides which relationship types and directions should be followed. The direction can be one of:
all
in
out
uniqueness
Decides how uniqueness should be calculated. For details on different uniqueness values see the
Java API on Uniqueness <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/kernel/
Uniqueness.html>. Possible values:
node_global
none
relationship_global
node_path
relationship_path
prune_evaluator
Decides whether the traverser should continue down that path or if it should be pruned so that
the traverser wont continue down that path. You can write your own prune evaluator as (see
Section19.15.1, Traversal using a return filter or use the built-in none prune evaluator.
295
REST API
return_filter
Decides whether the current position should be included in the result. You can provide your own
code for this (see Section19.15.1, Traversal using a return filter), or use one of the built-in
filters:
all
all_but_start_node
max_depth
Is a short-hand way of specifying a prune evaluator which prunes after a certain depth. If not
specified a max depth of 1 is used and if a prune_evaluator is specified instead of a max_depth, no
max depth limit is set.
The position object in the body of the return_filter and prune_evaluator is a Path <http://
components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/Path.html> object representing the path
from the start node to the current traversal position.
Out of the box, the REST API supports JavaScript code in filters and evaluators. The script
body will be executed in a Java context which has access to the full Neo4j Java API <http://
components.neo4j.org/neo4j/2.0.0/apidocs/>. See the examples for the exact syntax of the request.
296
REST API
Figure19.58.Final Graph
Node[ 375] nam e = 'Root '
knows
knows
knows
knows
knows
loves
Example request
POST http://localhost:7474/db/data/node/375/traverse/node
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"order" : "breadth_first",
"return_filter" : {
"body" : "position.endNode().getProperty('name').toLowerCase().contains('t')",
"language" : "javascript"
},
"prune_evaluator" : {
"body" : "position.length() > 10",
"language" : "javascript"
},
"uniqueness" : "node_global",
"relationships" : [ {
"direction" : "all",
"type" : "knows"
}, {
"direction" : "all",
"type" : "loves"
} ],
"max_depth" : 3
}
297
REST API
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/375/relationships/out",
"labels" : "http://localhost:7474/db/data/node/375/labels",
"data" : {
"name" : "Root"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/375/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/375/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/375",
"property" : "http://localhost:7474/db/data/node/375/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/375/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/375/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/375/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/375/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/375/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/375/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/375/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/378/relationships/out",
"labels" : "http://localhost:7474/db/data/node/378/labels",
"data" : {
"name" : "Mattias"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/378/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/378/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/378",
"property" : "http://localhost:7474/db/data/node/378/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/378/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/378/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/378/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/378/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/378/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/378/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/378/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/377/relationships/out",
"labels" : "http://localhost:7474/db/data/node/377/labels",
"data" : {
"name" : "Peter"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/377/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/377/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/377",
"property" : "http://localhost:7474/db/data/node/377/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/377/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/377/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/377/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/377/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/377/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/377/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/377/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/376/relationships/out",
298
REST API
"labels" : "http://localhost:7474/db/data/node/376/labels",
"data" : {
"name" : "Tobias"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/376/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/376/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/376",
"property" : "http://localhost:7474/db/data/node/376/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/376/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/376/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/376/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/376/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/376/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/376/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/376/relationships/in/{-list|&|types}"
} ]
own
know
Example request
POST http://localhost:7474/db/data/node/369/traverse/relationship
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"order" : "breadth_first",
"uniqueness" : "none",
"return_filter" : {
"language" : "builtin",
"name" : "all"
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/369",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/250",
"property" : "http://localhost:7474/db/data/relationship/250/properties/{key}",
299
REST API
"properties" : "http://localhost:7474/db/data/relationship/250/properties",
"type" : "know",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/368"
}, {
"start" : "http://localhost:7474/db/data/node/369",
"data" : {
},
"self" : "http://localhost:7474/db/data/relationship/251",
"property" : "http://localhost:7474/db/data/relationship/251/properties/{key}",
"properties" : "http://localhost:7474/db/data/relationship/251/properties",
"type" : "own",
"extensions" : {
},
"end" : "http://localhost:7474/db/data/node/367"
} ]
own
know
Example request
POST http://localhost:7474/db/data/node/359/traverse/path
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"order" : "breadth_first",
"uniqueness" : "none",
"return_filter" : {
"language" : "builtin",
"name" : "all"
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/359",
"nodes" : [ "http://localhost:7474/db/data/node/359" ],
"length" : 0,
"relationships" : [ ],
"end" : "http://localhost:7474/db/data/node/359"
300
REST API
}, {
"start" : "http://localhost:7474/db/data/node/359",
"nodes" : [ "http://localhost:7474/db/data/node/359", "http://localhost:7474/db/data/node/358" ],
"length" : 1,
"relationships" : [ "http://localhost:7474/db/data/relationship/242" ],
"end" : "http://localhost:7474/db/data/node/358"
}, {
"start" : "http://localhost:7474/db/data/node/359",
"nodes" : [ "http://localhost:7474/db/data/node/359", "http://localhost:7474/db/data/node/357" ],
"length" : 1,
"relationships" : [ "http://localhost:7474/db/data/relationship/243" ],
"end" : "http://localhost:7474/db/data/node/357"
} ]
knows
knows
knows
knows
knows
loves
Example request
POST http://localhost:7474/db/data/node/363/traverse/node
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
301
REST API
"return_filter" : {
"body" : "position.length()<3;",
"language" : "javascript"
},
"prune_evaluator" : {
"name" : "none",
"language" : "builtin"
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/363/relationships/out",
"labels" : "http://localhost:7474/db/data/node/363/labels",
"data" : {
"name" : "Root"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/363/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/363/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/363",
"property" : "http://localhost:7474/db/data/node/363/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/363/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/363/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/363/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/363/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/363/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/363/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/363/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/366/relationships/out",
"labels" : "http://localhost:7474/db/data/node/366/labels",
"data" : {
"name" : "Mattias"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/366/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/366/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/366",
"property" : "http://localhost:7474/db/data/node/366/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/366/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/366/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/366/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/366/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/366/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/366/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/366/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/361/relationships/out",
"labels" : "http://localhost:7474/db/data/node/361/labels",
"data" : {
"name" : "Johan"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/361/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/361/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/361",
"property" : "http://localhost:7474/db/data/node/361/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/361/properties",
302
REST API
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/361/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/361/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/361/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/361/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/361/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/361/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/362/relationships/out",
"labels" : "http://localhost:7474/db/data/node/362/labels",
"data" : {
"name" : "Emil"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/362/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/362/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/362",
"property" : "http://localhost:7474/db/data/node/362/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/362/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/362/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/362/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/362/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/362/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/362/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/362/relationships/in/{-list|&|types}"
} ]
Example response
201: Created
Content-Type: application/json; charset=UTF-8
303
REST API
Location: http://localhost:7474/db/data/node/297/paged/traverse/node/
ab3224c783f24e1fb77789e0c89ba641
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/298/relationships/out",
"labels" : "http://localhost:7474/db/data/node/298/labels",
"data" : {
"name" : "1"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/298/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/298/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/298",
"property" : "http://localhost:7474/db/data/node/298/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/298/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/298/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/298/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/298/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/298/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/298/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/298/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/307/relationships/out",
"labels" : "http://localhost:7474/db/data/node/307/labels",
"data" : {
"name" : "10"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/307/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/307/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/307",
"property" : "http://localhost:7474/db/data/node/307/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/307/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/307/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/307/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/307/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/307/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/307/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/307/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/308/relationships/out",
"labels" : "http://localhost:7474/db/data/node/308/labels",
"data" : {
"name" : "11"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/308/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/308/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/308",
"property" : "http://localhost:7474/db/data/node/308/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/308/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/308/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/308/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/308/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/308/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/308/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/308/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/309/relationships/out",
"labels" : "http://localhost:7474/db/data/node/309/labels",
"data" : {
304
REST API
"name" : "12"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/309/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/309/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/309",
"property" : "http://localhost:7474/db/data/node/309/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/309/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/309/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/309/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/309/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/309/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/309/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/309/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/310/relationships/out",
"labels" : "http://localhost:7474/db/data/node/310/labels",
"data" : {
"name" : "13"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/310/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/310/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/310",
"property" : "http://localhost:7474/db/data/node/310/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/310/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/310/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/310/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/310/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/310/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/310/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/310/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/311/relationships/out",
"labels" : "http://localhost:7474/db/data/node/311/labels",
"data" : {
"name" : "14"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/311/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/311/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/311",
"property" : "http://localhost:7474/db/data/node/311/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/311/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/311/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/311/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/311/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/311/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/311/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/311/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/312/relationships/out",
"labels" : "http://localhost:7474/db/data/node/312/labels",
"data" : {
"name" : "15"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/312/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/312/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/312",
"property" : "http://localhost:7474/db/data/node/312/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/312/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/312/relationships/out/{-list|&|types}",
305
REST API
"incoming_relationships" : "http://localhost:7474/db/data/node/312/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/312/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/312/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/312/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/312/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/313/relationships/out",
"labels" : "http://localhost:7474/db/data/node/313/labels",
"data" : {
"name" : "16"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/313/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/313/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/313",
"property" : "http://localhost:7474/db/data/node/313/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/313/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/313/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/313/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/313/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/313/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/313/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/313/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/314/relationships/out",
"labels" : "http://localhost:7474/db/data/node/314/labels",
"data" : {
"name" : "17"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/314/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/314/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/314",
"property" : "http://localhost:7474/db/data/node/314/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/314/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/314/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/314/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/314/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/314/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/314/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/314/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/315/relationships/out",
"labels" : "http://localhost:7474/db/data/node/315/labels",
"data" : {
"name" : "18"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/315/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/315/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/315",
"property" : "http://localhost:7474/db/data/node/315/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/315/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/315/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/315/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/315/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/315/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/315/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/315/relationships/in/{-list|&|types}"
}, {
306
REST API
"outgoing_relationships" : "http://localhost:7474/db/data/node/316/relationships/out",
"labels" : "http://localhost:7474/db/data/node/316/labels",
"data" : {
"name" : "19"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/316/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/316/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/316",
"property" : "http://localhost:7474/db/data/node/316/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/316/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/316/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/316/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/316/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/316/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/316/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/316/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/318/relationships/out",
"labels" : "http://localhost:7474/db/data/node/318/labels",
"data" : {
"name" : "21"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/318/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/318/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/318",
"property" : "http://localhost:7474/db/data/node/318/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/318/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/318/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/318/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/318/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/318/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/318/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/318/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/328/relationships/out",
"labels" : "http://localhost:7474/db/data/node/328/labels",
"data" : {
"name" : "31"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/328/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/328/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/328",
"property" : "http://localhost:7474/db/data/node/328/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/328/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/328/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/328/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/328/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/328/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/328/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/328/relationships/in/{-list|&|types}"
} ]
REST API
Note that if a traverser expires through inactivity it will cause a 404 response on the next GET request.
Traversers' leases are renewed on every successful access for the same amount of time as originally
specified.
When the paged traverser reaches the end of its results, the client can expect a 404 response as the
traverser is disposed by the server.
Example request
GET http://localhost:7474/db/data/node/330/paged/traverse/node/7ab37c0064654259ab2a1212ddb41c7f
Accept: application/json
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/661/relationships/out",
"labels" : "http://localhost:7474/db/data/node/661/labels",
"data" : {
"name" : "331"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/661/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/661/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/661",
"property" : "http://localhost:7474/db/data/node/661/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/661/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/661/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/661/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/661/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/661/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/661/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/661/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/671/relationships/out",
"labels" : "http://localhost:7474/db/data/node/671/labels",
"data" : {
"name" : "341"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/671/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/671/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/671",
"property" : "http://localhost:7474/db/data/node/671/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/671/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/671/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/671/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/671/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/671/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/671/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/671/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/681/relationships/out",
"labels" : "http://localhost:7474/db/data/node/681/labels",
"data" : {
"name" : "351"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/681/relationships/all/{-list|&|types}",
308
REST API
"traverse" : "http://localhost:7474/db/data/node/681/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/681",
"property" : "http://localhost:7474/db/data/node/681/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/681/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/681/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/681/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/681/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/681/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/681/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/681/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/691/relationships/out",
"labels" : "http://localhost:7474/db/data/node/691/labels",
"data" : {
"name" : "361"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/691/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/691/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/691",
"property" : "http://localhost:7474/db/data/node/691/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/691/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/691/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/691/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/691/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/691/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/691/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/691/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/701/relationships/out",
"labels" : "http://localhost:7474/db/data/node/701/labels",
"data" : {
"name" : "371"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/701/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/701/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/701",
"property" : "http://localhost:7474/db/data/node/701/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/701/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/701/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/701/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/701/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/701/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/701/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/701/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/711/relationships/out",
"labels" : "http://localhost:7474/db/data/node/711/labels",
"data" : {
"name" : "381"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/711/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/711/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/711",
"property" : "http://localhost:7474/db/data/node/711/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/711/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/711/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/711/relationships/in",
"extensions" : {
},
309
REST API
"create_relationship" : "http://localhost:7474/db/data/node/711/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/711/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/711/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/711/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/721/relationships/out",
"labels" : "http://localhost:7474/db/data/node/721/labels",
"data" : {
"name" : "391"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/721/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/721/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/721",
"property" : "http://localhost:7474/db/data/node/721/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/721/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/721/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/721/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/721/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/721/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/721/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/721/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/731/relationships/out",
"labels" : "http://localhost:7474/db/data/node/731/labels",
"data" : {
"name" : "401"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/731/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/731/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/731",
"property" : "http://localhost:7474/db/data/node/731/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/731/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/731/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/731/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/731/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/731/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/731/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/731/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/740/relationships/out",
"labels" : "http://localhost:7474/db/data/node/740/labels",
"data" : {
"name" : "410"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/740/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/740/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/740",
"property" : "http://localhost:7474/db/data/node/740/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/740/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/740/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/740/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/740/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/740/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/740/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/740/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/741/relationships/out",
"labels" : "http://localhost:7474/db/data/node/741/labels",
"data" : {
310
REST API
"name" : "411"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/741/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/741/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/741",
"property" : "http://localhost:7474/db/data/node/741/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/741/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/741/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/741/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/741/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/741/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/741/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/741/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/742/relationships/out",
"labels" : "http://localhost:7474/db/data/node/742/labels",
"data" : {
"name" : "412"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/742/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/742/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/742",
"property" : "http://localhost:7474/db/data/node/742/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/742/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/742/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/742/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/742/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/742/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/742/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/742/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/743/relationships/out",
"labels" : "http://localhost:7474/db/data/node/743/labels",
"data" : {
"name" : "413"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/743/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/743/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/743",
"property" : "http://localhost:7474/db/data/node/743/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/743/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/743/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/743/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/743/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/743/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/743/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/743/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/744/relationships/out",
"labels" : "http://localhost:7474/db/data/node/744/labels",
"data" : {
"name" : "414"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/744/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/744/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/744",
"property" : "http://localhost:7474/db/data/node/744/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/744/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/744/relationships/out/{-list|&|types}",
311
REST API
"incoming_relationships" : "http://localhost:7474/db/data/node/744/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/744/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/744/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/744/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/744/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/745/relationships/out",
"labels" : "http://localhost:7474/db/data/node/745/labels",
"data" : {
"name" : "415"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/745/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/745/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/745",
"property" : "http://localhost:7474/db/data/node/745/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/745/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/745/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/745/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/745/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/745/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/745/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/745/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/746/relationships/out",
"labels" : "http://localhost:7474/db/data/node/746/labels",
"data" : {
"name" : "416"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/746/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/746/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/746",
"property" : "http://localhost:7474/db/data/node/746/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/746/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/746/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/746/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/746/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/746/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/746/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/746/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/747/relationships/out",
"labels" : "http://localhost:7474/db/data/node/747/labels",
"data" : {
"name" : "417"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/747/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/747/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/747",
"property" : "http://localhost:7474/db/data/node/747/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/747/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/747/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/747/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/747/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/747/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/747/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/747/relationships/in/{-list|&|types}"
}, {
312
REST API
"outgoing_relationships" : "http://localhost:7474/db/data/node/748/relationships/out",
"labels" : "http://localhost:7474/db/data/node/748/labels",
"data" : {
"name" : "418"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/748/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/748/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/748",
"property" : "http://localhost:7474/db/data/node/748/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/748/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/748/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/748/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/748/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/748/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/748/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/748/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/749/relationships/out",
"labels" : "http://localhost:7474/db/data/node/749/labels",
"data" : {
"name" : "419"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/749/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/749/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/749",
"property" : "http://localhost:7474/db/data/node/749/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/749/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/749/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/749/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/749/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/749/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/749/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/749/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/751/relationships/out",
"labels" : "http://localhost:7474/db/data/node/751/labels",
"data" : {
"name" : "421"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/751/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/751/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/751",
"property" : "http://localhost:7474/db/data/node/751/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/751/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/751/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/751/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/751/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/751/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/751/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/751/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/761/relationships/out",
"labels" : "http://localhost:7474/db/data/node/761/labels",
"data" : {
"name" : "431"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/761/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/761/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/761",
313
REST API
"property" : "http://localhost:7474/db/data/node/761/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/761/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/761/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/761/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/761/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/761/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/761/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/761/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/771/relationships/out",
"labels" : "http://localhost:7474/db/data/node/771/labels",
"data" : {
"name" : "441"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/771/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/771/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/771",
"property" : "http://localhost:7474/db/data/node/771/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/771/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/771/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/771/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/771/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/771/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/771/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/771/relationships/in/{-list|&|types}"
} ]
Example response
201: Created
Content-Type: application/json; charset=UTF-8
314
REST API
Location: http://localhost:7474/db/data/node/33/paged/traverse/node/
cccb89c7d1f04ca5a021b5ecbaf2075b
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/34/relationships/out",
"labels" : "http://localhost:7474/db/data/node/34/labels",
"data" : {
"name" : "1"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/34/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/34/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/34",
"property" : "http://localhost:7474/db/data/node/34/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/34/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/34/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/34/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/34/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/34/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/34/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/34/relationships/in/{-list|&|types}"
} ]
The default timeout for a paged traverser is 60 seconds, but depending on the application larger or
smaller timeouts might be appropriate. This can be set by adding a leaseTime query parameter with the
number of seconds the paged traverser should last.
Example request
POST http://localhost:7474/db/data/node/807/paged/traverse/node?leaseTime=10
Accept: application/json
Content-Type: application/json
{
"prune_evaluator" : {
"language" : "builtin",
"name" : "none"
},
"return_filter" : {
"language" : "javascript",
"body" : "position.endNode().getProperty('name').contains('1');"
},
"order" : "depth_first",
"relationships" : {
"type" : "NEXT",
"direction" : "out"
}
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/node/807/paged/traverse/node/
e05ab52b5e1b4e7ca3d794cf06fadce2
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/808/relationships/out",
"labels" : "http://localhost:7474/db/data/node/808/labels",
315
REST API
"data" : {
"name" : "1"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/808/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/808/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/808",
"property" : "http://localhost:7474/db/data/node/808/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/808/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/808/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/808/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/808/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/808/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/808/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/808/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/817/relationships/out",
"labels" : "http://localhost:7474/db/data/node/817/labels",
"data" : {
"name" : "10"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/817/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/817/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/817",
"property" : "http://localhost:7474/db/data/node/817/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/817/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/817/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/817/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/817/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/817/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/817/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/817/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/818/relationships/out",
"labels" : "http://localhost:7474/db/data/node/818/labels",
"data" : {
"name" : "11"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/818/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/818/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/818",
"property" : "http://localhost:7474/db/data/node/818/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/818/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/818/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/818/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/818/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/818/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/818/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/818/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/819/relationships/out",
"labels" : "http://localhost:7474/db/data/node/819/labels",
"data" : {
"name" : "12"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/819/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/819/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/819",
"property" : "http://localhost:7474/db/data/node/819/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/819/properties",
316
REST API
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/819/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/819/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/819/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/819/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/819/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/819/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/820/relationships/out",
"labels" : "http://localhost:7474/db/data/node/820/labels",
"data" : {
"name" : "13"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/820/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/820/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/820",
"property" : "http://localhost:7474/db/data/node/820/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/820/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/820/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/820/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/820/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/820/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/820/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/820/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/821/relationships/out",
"labels" : "http://localhost:7474/db/data/node/821/labels",
"data" : {
"name" : "14"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/821/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/821/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/821",
"property" : "http://localhost:7474/db/data/node/821/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/821/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/821/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/821/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/821/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/821/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/821/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/821/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/822/relationships/out",
"labels" : "http://localhost:7474/db/data/node/822/labels",
"data" : {
"name" : "15"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/822/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/822/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/822",
"property" : "http://localhost:7474/db/data/node/822/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/822/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/822/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/822/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/822/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/822/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/822/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/822/relationships/in/{-list|&|types}"
317
REST API
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/823/relationships/out",
"labels" : "http://localhost:7474/db/data/node/823/labels",
"data" : {
"name" : "16"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/823/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/823/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/823",
"property" : "http://localhost:7474/db/data/node/823/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/823/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/823/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/823/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/823/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/823/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/823/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/823/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/824/relationships/out",
"labels" : "http://localhost:7474/db/data/node/824/labels",
"data" : {
"name" : "17"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/824/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/824/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/824",
"property" : "http://localhost:7474/db/data/node/824/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/824/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/824/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/824/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/824/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/824/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/824/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/824/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/825/relationships/out",
"labels" : "http://localhost:7474/db/data/node/825/labels",
"data" : {
"name" : "18"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/825/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/825/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/825",
"property" : "http://localhost:7474/db/data/node/825/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/825/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/825/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/825/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/825/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/825/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/825/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/825/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/826/relationships/out",
"labels" : "http://localhost:7474/db/data/node/826/labels",
"data" : {
"name" : "19"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/826/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/826/traverse/{returnType}",
318
REST API
"self" : "http://localhost:7474/db/data/node/826",
"property" : "http://localhost:7474/db/data/node/826/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/826/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/826/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/826/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/826/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/826/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/826/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/826/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/828/relationships/out",
"labels" : "http://localhost:7474/db/data/node/828/labels",
"data" : {
"name" : "21"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/828/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/828/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/828",
"property" : "http://localhost:7474/db/data/node/828/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/828/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/828/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/828/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/828/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/828/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/828/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/828/relationships/in/{-list|&|types}"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/838/relationships/out",
"labels" : "http://localhost:7474/db/data/node/838/labels",
"data" : {
"name" : "31"
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/838/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/838/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/838",
"property" : "http://localhost:7474/db/data/node/838/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/838/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/838/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/838/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/838/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/838/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/838/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/838/relationships/in/{-list|&|types}"
} ]
319
REST API
19.16.Graph Algorithms
Neo4j comes with a number of built-in graph algorithms. They are performed from a start node. The
traversal is controlled by the URI and the body sent with the request. These are the parameters that
can be used:
algorithm
The algorithm to choose. If not set, default is shortestPath. algorithm can have one of these values:
shortestPath
allSimplePaths
allPaths
dijkstra (optionally with cost_property and default_cost parameters)
max_depth
The maximum depth as an integer for the algorithms like shortestPath, where applicable. Default
is 1.
to
to
to
to
to
to
to
to
to
Example request
320
to
REST API
POST http://localhost:7474/db/data/node/202/paths
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/197",
"max_depth" : 3,
"relationships" : {
"type" : "to",
"direction" : "out"
},
"algorithm" : "shortestPath"
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"start" : "http://localhost:7474/db/data/node/202",
"nodes" : [ "http://localhost:7474/db/data/node/202", "http://localhost:7474/db/data/node/201", "http://localhost:7474/db/data/node
"length" : 2,
"relationships" : [ "http://localhost:7474/db/data/relationship/138", "http://localhost:7474/db/data/relationship/147" ],
"end" : "http://localhost:7474/db/data/node/197"
}, {
"start" : "http://localhost:7474/db/data/node/202",
"nodes" : [ "http://localhost:7474/db/data/node/202", "http://localhost:7474/db/data/node/198", "http://localhost:7474/db/data/node
"length" : 2,
"relationships" : [ "http://localhost:7474/db/data/relationship/139", "http://localhost:7474/db/data/relationship/145" ],
"end" : "http://localhost:7474/db/data/node/197"
} ]
321
REST API
Figure19.63.Final Graph
Node[ 195] nam e = 'a'
to
to
to
to
to
to
to
to
to
to
Example request
POST http://localhost:7474/db/data/node/195/path
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/190",
"max_depth" : 3,
"relationships" : {
"type" : "to",
"direction" : "out"
},
"algorithm" : "shortestPath"
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"start" : "http://localhost:7474/db/data/node/195",
"nodes" : [ "http://localhost:7474/db/data/node/195", "http://localhost:7474/db/data/node/191", "http://localhost:7474/db/data/node
"length" : 2,
322
REST API
"relationships" : [ "http://localhost:7474/db/data/relationship/129", "http://localhost:7474/db/data/relationship/135" ],
"end" : "http://localhost:7474/db/data/node/190"
}
to
cost = 0.5
to
cost = 1.5
to
cost = 0.5
to
cost = 0.5
to
cost = 1.2
to
cost = 0.5
to
cost = 0.5
Example request
POST http://localhost:7474/db/data/node/208/path
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/205",
"cost_property" : "cost",
"relationships" : {
"type" : "to",
"direction" : "out"
},
"algorithm" : "dijkstra"
}
Example response
200: OK
323
REST API
Content-Type: application/json; charset=UTF-8
{
"weight" : 1.5,
"start" : "http://localhost:7474/db/data/node/208",
"nodes" : [ "http://localhost:7474/db/data/node/208", "http://localhost:7474/db/data/node/207", "http://localhost:7474/db/data/node
"length" : 3,
"relationships" : [ "http://localhost:7474/db/data/relationship/149", "http://localhost:7474/db/data/relationship/151", "http://loc
"end" : "http://localhost:7474/db/data/node/205"
}
to
cost = 1
to
cost = 1
to
cost = 1
to
cost = 1
to
cost = 1
to
cost = 1
to
cost = 1
Example request
POST http://localhost:7474/db/data/node/214/path
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/211",
"cost_property" : "cost",
"relationships" : {
"type" : "to",
324
REST API
"direction" : "out"
},
"algorithm" : "dijkstra"
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"weight" : 2.0,
"start" : "http://localhost:7474/db/data/node/214",
"nodes" : [ "http://localhost:7474/db/data/node/214", "http://localhost:7474/db/data/node/212", "http://localhost:7474/db/data/node
"length" : 2,
"relationships" : [ "http://localhost:7474/db/data/relationship/155", "http://localhost:7474/db/data/relationship/160" ],
"end" : "http://localhost:7474/db/data/node/211"
}
to
cost = 0.5
to
cost = 1.5
to
cost = 0.5
to
cost = 0.5
to
cost = 1.0
to
cost = 0.5
to
cost = 0.5
Example request
POST http://localhost:7474/db/data/node/188/paths
325
REST API
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"to" : "http://localhost:7474/db/data/node/185",
"cost_property" : "cost",
"relationships" : {
"type" : "to",
"direction" : "out"
},
"algorithm" : "dijkstra"
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"weight" : 1.5,
"start" : "http://localhost:7474/db/data/node/188",
"nodes" : [ "http://localhost:7474/db/data/node/188", "http://localhost:7474/db/data/node/187", "http://localhost:7474/db/data/node
"length" : 3,
"relationships" : [ "http://localhost:7474/db/data/relationship/122", "http://localhost:7474/db/data/relationship/124", "http://loc
"end" : "http://localhost:7474/db/data/node/185"
}, {
"weight" : 1.5,
"start" : "http://localhost:7474/db/data/node/188",
"nodes" : [ "http://localhost:7474/db/data/node/188", "http://localhost:7474/db/data/node/183", "http://localhost:7474/db/data/node
"length" : 2,
"relationships" : [ "http://localhost:7474/db/data/relationship/123", "http://localhost:7474/db/data/relationship/127" ],
"end" : "http://localhost:7474/db/data/node/185"
} ]
326
REST API
19.17.Batch operations
19.17.1.Execute multiple operations in batch
This lets you execute multiple API calls through a single HTTP call, significantly improving
performance for large insert and update operations.
The batch service expects an array of job descriptions as input, each job description describing an
action to be performed via the normal server API.
This service is transactional. If any of the operations performed fails (returns a non-2xx HTTP status
code), the transaction will be rolled back and all changes will be undone.
Each job description should contain a to attribute, with a value relative to the data API root (so http://
localhost:7474/db/data/node becomes just /node), and a method attribute containing HTTP verb to use.
Optionally you may provide a body attribute, and an id attribute to help you keep track of responses,
although responses are guaranteed to be returned in the same order the job descriptions are received.
The following figure outlines the different parts of the job descriptions:
Figure19.67.Final Graph
Node[ 31] age = 1
knows
Example request
POST http://localhost:7474/db/data/batch
Accept: application/json; charset=UTF-8
Content-Type: application/json
[ {
"method" : "PUT",
"to" : "/node/31/properties",
"body" : {
"age" : 1
},
"id" : 0
}, {
"method" : "GET",
327
REST API
"to" : "/node/31",
"id" : 1
}, {
"method" : "POST",
"to" : "/node",
"body" : {
"age" : 1
},
"id" : 2
}, {
"method" : "POST",
"to" : "/node",
"body" : {
"age" : 1
},
"id" : 3
} ]
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"id" : 0,
"from" : "/node/31/properties"
}, {
"id" : 1,
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/31/relationships/out",
"labels" : "http://localhost:7474/db/data/node/31/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/31/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/31/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/31",
"property" : "http://localhost:7474/db/data/node/31/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/31/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/31/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/31/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/31/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/31/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/31/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/31/relationships/in/{-list|&|types}",
"data" : {
"age" : 1
}
},
"from" : "/node/31"
}, {
"id" : 2,
"location" : "http://localhost:7474/db/data/node/32",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/32/relationships/out",
"labels" : "http://localhost:7474/db/data/node/32/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/32/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/32/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/32",
"property" : "http://localhost:7474/db/data/node/32/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/32/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/32/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/32/relationships/in",
328
REST API
"create_relationship" : "http://localhost:7474/db/data/node/32/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/32/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/32/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/32/relationships/in/{-list|&|types}",
"data" : {
"age" : 1
}
},
"from" : "/node"
}, {
"id" : 3,
"location" : "http://localhost:7474/db/data/node/33",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/33/relationships/out",
"labels" : "http://localhost:7474/db/data/node/33/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/33/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/33/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/33",
"property" : "http://localhost:7474/db/data/node/33/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/33/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/33/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/33/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/33/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/33/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/33/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/33/relationships/in/{-list|&|types}",
"data" : {
"age" : 1
}
},
"from" : "/node"
} ]
KNOWS
since = '2010'
Example request
POST http://localhost:7474/db/data/batch
Accept: application/json; charset=UTF-8
Content-Type: application/json
329
REST API
[ {
"method" : "POST",
"to" : "/node",
"id" : 0,
"body" : {
"name" : "bob"
}
}, {
"method" : "POST",
"to" : "/node",
"id" : 1,
"body" : {
"age" : 12
}
}, {
"method" : "POST",
"to" : "{0}/relationships",
"id" : 3,
"body" : {
"to" : "{1}",
"data" : {
"since" : "2010"
},
"type" : "KNOWS"
}
}, {
"method" : "POST",
"to" : "/index/relationship/my_rels",
"id" : 4,
"body" : {
"key" : "since",
"value" : "2010",
"uri" : "{3}"
}
} ]
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"id" : 0,
"location" : "http://localhost:7474/db/data/node/23",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/23/relationships/out",
"labels" : "http://localhost:7474/db/data/node/23/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/23/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/23/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/23",
"property" : "http://localhost:7474/db/data/node/23/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/23/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/23/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/23/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/23/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/23/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/23/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/23/relationships/in/{-list|&|types}",
"data" : {
"name" : "bob"
}
},
330
REST API
"from" : "/node"
}, {
"id" : 1,
"location" : "http://localhost:7474/db/data/node/24",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/24/relationships/out",
"labels" : "http://localhost:7474/db/data/node/24/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/24/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/24/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/24",
"property" : "http://localhost:7474/db/data/node/24/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/24/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/24/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/24/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/24/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/24/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/24/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/24/relationships/in/{-list|&|types}",
"data" : {
"age" : 12
}
},
"from" : "/node"
}, {
"id" : 3,
"location" : "http://localhost:7474/db/data/relationship/3",
"body" : {
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/23",
"property" : "http://localhost:7474/db/data/relationship/3/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/3",
"properties" : "http://localhost:7474/db/data/relationship/3/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/24",
"data" : {
"since" : "2010"
}
},
"from" : "http://localhost:7474/db/data/node/23/relationships"
}, {
"id" : 4,
"location" : "http://localhost:7474/db/data/index/relationship/my_rels/since/2010/3",
"body" : {
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/23",
"property" : "http://localhost:7474/db/data/relationship/3/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/3",
"properties" : "http://localhost:7474/db/data/relationship/3/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/24",
"data" : {
"since" : "2010"
},
"indexed" : "http://localhost:7474/db/data/index/relationship/my_rels/since/2010/3"
},
"from" : "/index/relationship/my_rels"
} ]
331
REST API
knows
Example request
POST http://localhost:7474/db/data/batch
Accept: application/json
Content-Type: application/json
X-Stream: true
[ {
"method" : "PUT",
"to" : "/node/101/properties",
"body" : {
"age" : 1
},
"id" : 0
}, {
"method" : "GET",
"to" : "/node/101",
"id" : 1
}, {
"method" : "POST",
"to" : "/node",
"body" : {
"age" : 1
},
"id" : 2
}, {
"method" : "POST",
"to" : "/node",
"body" : {
"age" : 1
},
"id" : 3
} ]
Example response
200: OK
Content-Type: application/json; charset=UTF-8
[ {
"id" : 0,
"from" : "/node/101/properties",
"body" : null,
"status" : 204
}, {
"id" : 1,
332
REST API
"from" : "/node/101",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/101/relationships/out",
"labels" : "http://localhost:7474/db/data/node/101/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/101/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/101/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/101",
"property" : "http://localhost:7474/db/data/node/101/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/101/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/101/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/101/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/101/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/101/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/101/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/101/relationships/in/{-list|&|types}",
"data" : {
"age" : 1
}
},
"status" : 200
}, {
"id" : 2,
"from" : "/node",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/102/relationships/out",
"labels" : "http://localhost:7474/db/data/node/102/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/102/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/102/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/102",
"property" : "http://localhost:7474/db/data/node/102/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/102/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/102/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/102/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/102/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/102/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/102/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/102/relationships/in/{-list|&|types}",
"data" : {
"age" : 1
}
},
"location" : "http://localhost:7474/db/data/node/102",
"status" : 201
}, {
"id" : 3,
"from" : "/node",
"body" : {
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/103/relationships/out",
"labels" : "http://localhost:7474/db/data/node/103/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/103/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/103/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/103",
"property" : "http://localhost:7474/db/data/node/103/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/103/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/103/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/103/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/103/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/103/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/103/relationships/all",
333
REST API
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/103/relationships/in/{-list|&|types}",
"data" : {
"age" : 1
}
},
"location" : "http://localhost:7474/db/data/node/103",
"status" : 201
} ]
334
REST API
19.18.Legacy indexing
Note
This documents the legacy indexing in Neo4j, which is no longer the preferred way to
handle indexes. Consider looking at Section19.13, Indexing.
An index can contain either nodes or relationships.
Note
To create an index with default configuration, simply start using it by adding nodes/
relationships to it. It will then be automatically created for you.
What default configuration means depends on how you have configured your database. If you havent
changed any indexing configuration, it means the indexes will be using a Lucene-based backend.
All the examples below show you how to do operations on node indexes, but all of them are just as
applicable to relationship indexes. Simple change the "node" part of the URL to "relationship".
If you want to customize the index settings, see Section19.18.2, Create node index with
configuration.
Instead of creating the index this way, you can simply start to use it, and it will be created
automatically with default configuration.
Example request
POST http://localhost:7474/db/data/index/node/
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"name" : "favorites"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/favorites/
{
"template" : "http://localhost:7474/db/data/index/node/favorites/{key}/{value}"
}
REST API
POST http://localhost:7474/db/data/index/node/
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"name" : "fulltext",
"config" : {
"type" : "fulltext",
"provider" : "lucene"
}
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/fulltext/
{
"template" : "http://localhost:7474/db/data/index/node/fulltext/{key}/{value}",
"type" : "fulltext",
"provider" : "lucene"
}
336
REST API
"type" : "exact"
}
}
Note
Caution
This does not overwrite previous entries. If you index the same key/value/item
combination twice, two index entries are created. To do update-type operations, you need
to delete the old entry before adding a new one.
Example request
POST http://localhost:7474/db/data/index/node/favorites
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"value" : "some value",
"uri" : "http://localhost:7474/db/data/node/83",
"key" : "some-key"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/favorites/some-key/some%20value/83
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/83/relationships/out",
"labels" : "http://localhost:7474/db/data/node/83/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/83/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/83/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/83",
"property" : "http://localhost:7474/db/data/node/83/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/83/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/83/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/83/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/83/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/83/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/83/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/83/relationships/in/{-list|&|types}",
"data" : {
},
"indexed" : "http://localhost:7474/db/data/index/node/favorites/some-key/some%20value/83"
}
REST API
DELETE http://localhost:7474/db/data/index/node/kvnode/89
Accept: application/json; charset=UTF-8
Example response
204: No Content
19.18.7.Remove all entries with a given node and key from an index
Example request
DELETE http://localhost:7474/db/data/index/node/kvnode/kvkey2/91
Accept: application/json; charset=UTF-8
Example response
204: No Content
19.18.8.Remove all entries with a given node, key and value from an index
Example request
DELETE http://localhost:7474/db/data/index/node/kvnode/kvkey1/value1/84
Accept: application/json; charset=UTF-8
Example response
204: No Content
338
REST API
"traverse" : "http://localhost:7474/db/data/node/100/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/100",
"property" : "http://localhost:7474/db/data/node/100/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/100/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/100/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/100/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/100/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/100/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/100/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/100/relationships/in/{-list|&|types}"
} ]
where ordering is one of index, relevance or score. In this case an additional field will be added to
each result, named score, that holds the float value that is the score reported by the query result.
Example request
GET http://localhost:7474/db/data/index/node/bobTheIndex?query=Name:Build~0.1%20AND
%20Gender:Male
339
REST API
19.19.Unique Indexing
Note
As of Neo4j 2.0, unique constraints have been added. These make Neo4j enforce the
uniqueness, guaranteeing that uniqueness is maintained. See Section3.7.2, Constraints
for details about this. For most cases, the unique constraints should be used rather than the
features described below.
For uniqueness enforcements, there are two modes:
URL Parameter uniqueness=get_or_create: Create a new node/relationship and index it if no existing
one can be found. If an existing node/relationship is found, discard the sent data and return the
existing node/relationship.
URL Parameter uniqueness=create_or_fail: Create a new node/relationship if no existing one can be
found in the index. If an existing node/relationship is found, return a conflict error.
For more information, see Section16.6, Creating unique nodes.
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/people/name/Tobias/98
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/98/relationships/out",
"labels" : "http://localhost:7474/db/data/node/98/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/98/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/98/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/98",
"property" : "http://localhost:7474/db/data/node/98/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/98/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/98/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/98/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/98/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/98/paged/traverse/{returnType}{?pageSize,leaseTime}",
340
REST API
"all_relationships" : "http://localhost:7474/db/data/node/98/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/98/relationships/in/{-list|&|types}",
"data" : {
"sequence" : 1,
"name" : "Tobias"
},
"indexed" : "http://localhost:7474/db/data/index/node/people/name/Tobias/98"
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/people/name/Peter/88
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/88/relationships/out",
"labels" : "http://localhost:7474/db/data/node/88/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/88/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/88/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/88",
"property" : "http://localhost:7474/db/data/node/88/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/88/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/88/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/88/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/88/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/88/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/88/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/88/relationships/in/{-list|&|types}",
"data" : {
"sequence" : 1,
"name" : "Peter"
},
"indexed" : "http://localhost:7474/db/data/index/node/people/name/Peter/88"
}
REST API
Example request
POST http://localhost:7474/db/data/index/node/people?uniqueness=create_or_fail
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"key" : "name",
"value" : "Tobias",
"properties" : {
"name" : "Tobias",
"sequence" : 1
}
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/people/name/Tobias/97
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/97/relationships/out",
"labels" : "http://localhost:7474/db/data/node/97/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/97/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/97/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/97",
"property" : "http://localhost:7474/db/data/node/97/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/97/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/97/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/97/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/97/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/97/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/97/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/97/relationships/in/{-list|&|types}",
"data" : {
"sequence" : 1,
"name" : "Tobias"
},
"indexed" : "http://localhost:7474/db/data/index/node/people/name/Tobias/97"
}
342
REST API
"name" : "Peter",
"sequence" : 2
}
}
Example response
409: Conflict
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/81/relationships/out",
"labels" : "http://localhost:7474/db/data/node/81/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/81/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/81/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/81",
"property" : "http://localhost:7474/db/data/node/81/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/81/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/81/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/81/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/81/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/81/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/81/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/81/relationships/in/{-list|&|types}",
"data" : {
"sequence" : 1,
"name" : "Peter"
},
"indexed" : "http://localhost:7474/db/data/index/node/people/name/Peter/81"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/favorites/some-key/some%20value/93
{
"extensions" : {
},
343
REST API
"outgoing_relationships" : "http://localhost:7474/db/data/node/93/relationships/out",
"labels" : "http://localhost:7474/db/data/node/93/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/93/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/93/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/93",
"property" : "http://localhost:7474/db/data/node/93/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/93/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/93/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/93/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/93/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/93/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/93/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/93/relationships/in/{-list|&|types}",
"data" : {
"some-key" : "some value"
},
"indexed" : "http://localhost:7474/db/data/index/node/favorites/some-key/some%20value/93"
}
Example response
409: Conflict
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"outgoing_relationships" : "http://localhost:7474/db/data/node/96/relationships/out",
"labels" : "http://localhost:7474/db/data/node/96/labels",
"all_typed_relationships" : "http://localhost:7474/db/data/node/96/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/96/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/96",
"property" : "http://localhost:7474/db/data/node/96/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/96/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/96/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/96/relationships/in",
"create_relationship" : "http://localhost:7474/db/data/node/96/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/96/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/96/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/96/relationships/in/{-list|&|types}",
"data" : {
"some-key" : "some value"
},
"indexed" : "http://localhost:7474/db/data/index/node/favorites/some-key/some%20value/96"
}
344
REST API
Note
The type and direction of the relationship is not regarded when determining uniqueness.
Example request
POST http://localhost:7474/db/data/index/relationship/MyIndex/?uniqueness=get_or_create
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"key" : "name",
"value" : "Tobias",
"start" : "http://localhost:7474/db/data/node/231",
"end" : "http://localhost:7474/db/data/node/232",
"type" : "knowledge"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/relationship/MyIndex/name/Tobias/170
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/231",
"property" : "http://localhost:7474/db/data/relationship/170/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/170",
"properties" : "http://localhost:7474/db/data/relationship/170/properties",
"type" : "knowledge",
"end" : "http://localhost:7474/db/data/node/232",
"data" : {
"name" : "Tobias"
},
"indexed" : "http://localhost:7474/db/data/index/relationship/MyIndex/name/Tobias/170"
}
345
REST API
"start" : "http://localhost:7474/db/data/node/235",
"end" : "http://localhost:7474/db/data/node/236",
"type" : "KNOWS"
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/233",
"property" : "http://localhost:7474/db/data/relationship/171/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/171",
"properties" : "http://localhost:7474/db/data/relationship/171/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/234",
"data" : {
},
"indexed" : "http://localhost:7474/db/data/index/relationship/rels/name/Peter/171"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/relationship/rels/name/Tobias/174
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/243",
"property" : "http://localhost:7474/db/data/relationship/174/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/174",
"properties" : "http://localhost:7474/db/data/relationship/174/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/244",
"data" : {
"name" : "Tobias"
},
346
REST API
"indexed" : "http://localhost:7474/db/data/index/relationship/rels/name/Tobias/174"
}
Example response
409: Conflict
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/221",
"property" : "http://localhost:7474/db/data/relationship/166/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/166",
"properties" : "http://localhost:7474/db/data/relationship/166/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/222",
"data" : {
},
"indexed" : "http://localhost:7474/db/data/index/relationship/rels/name/Peter/166"
}
Note
The type and direction of the relationship is not regarded when determining uniqueness.
Example request
POST http://localhost:7474/db/data/index/relationship/rels?uniqueness=create_or_fail
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
347
REST API
"key" : "name",
"value" : "Peter",
"uri" : "http://localhost:7474/db/data/relationship/164"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/relationship/rels/name/Peter/165
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/219",
"property" : "http://localhost:7474/db/data/relationship/165/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/165",
"properties" : "http://localhost:7474/db/data/relationship/165/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/220",
"data" : {
"name" : "Peter"
},
"indexed" : "http://localhost:7474/db/data/index/relationship/rels/name/Peter/165"
}
Example response
409: Conflict
Content-Type: application/json; charset=UTF-8
{
"extensions" : {
},
"start" : "http://localhost:7474/db/data/node/225",
"property" : "http://localhost:7474/db/data/relationship/167/properties/{key}",
"self" : "http://localhost:7474/db/data/relationship/167",
"properties" : "http://localhost:7474/db/data/relationship/167/properties",
"type" : "KNOWS",
"end" : "http://localhost:7474/db/data/node/226",
"data" : {
},
"indexed" : "http://localhost:7474/db/data/index/relationship/rels/name/Peter/167"
}
348
REST API
349
REST API
},
"all_typed_relationships" : "http://localhost:7474/db/data/node/0/relationships/all/{-list|&|types}",
"traverse" : "http://localhost:7474/db/data/node/0/traverse/{returnType}",
"self" : "http://localhost:7474/db/data/node/0",
"property" : "http://localhost:7474/db/data/node/0/properties/{key}",
"properties" : "http://localhost:7474/db/data/node/0/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/0/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/0/relationships/in",
"extensions" : {
},
"create_relationship" : "http://localhost:7474/db/data/node/0/relationships",
"paged_traverse" : "http://localhost:7474/db/data/node/0/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://localhost:7474/db/data/node/0/relationships/all",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/0/relationships/in/{-list|&|types}"
} ]
350
REST API
Warning
This approach cannot be used on databases that already have auto-indexes established. To
change the auto-index configuration existing indexes would have to be deleted first, so be
careful!
Caution
This technique works, but it is not particularly pleasant. Future versions of Neo4j may
remove this loophole in favour of a better structured feature for managing auto-indexing
configurations.
Auto-indexing must be enabled through configuration before we can create or configure them. Firstly
ensure that youve added some config like this into your servers neo4j.properties file:
node_auto_indexing=true
relationship_auto_indexing=true
node_keys_indexable=name,phone
relationship_keys_indexable=since
The node_auto_indexing and relationship_auto_indexing settings turn auto-indexing on for nodes and
relationships respectively. The node_keys_indexable key allows you to specify a comma-separated list
of node property keys to be indexed. The relationship_keys_indexable does the same for relationship
property keys.
Next start the server as usual by invoking the start script as described in Section21.2, Server
Installation.
Next we have to pre-empt the creation of an auto-index, by telling the server to create an apparently
manual index which has the same name as the node (or relationship) auto-index. For example, in this
case well create a node auto index whose name is node_auto_index, like so:
Example response
201: Created
351
REST API
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/node/node_auto_index/
{
"template" : "http://localhost:7474/db/data/index/node/node_auto_index/{key}/{value}",
"type" : "fulltext",
"provider" : "lucene"
}
Example response
201: Created
Content-Type: application/json; charset=UTF-8
Location: http://localhost:7474/db/data/index/relationship/relationship_auto_index/
{
"template" : "http://localhost:7474/db/data/index/relationship/relationship_auto_index/{key}/{value}",
"type" : "fulltext",
"provider" : "lucene"
}
In case youre curious how this works, on the server side it triggers the creation of an index which
happens to have the same name as the auto index that the database would create for itself. Now when
we interact with the database, the index thinks the index is already created so the state machine skips
over that step and just gets on with normal day-to-day auto-indexing.
Caution
You have to do this early in your server lifecycle, before any normal auto indexes are
created.
There are a few REST calls providing a REST interface to the AutoIndexer <http://
components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/AutoIndexer.html> component.
The following REST calls work both, for node and relationship by simply changing the respective part
of the URL.
REST API
GET http://localhost:7474/db/data/index/auto/node/status
Accept: application/json; charset=UTF-8
Example response
200: OK
Content-Type: application/json; charset=UTF-8
false
Example response
204: No Content
Example response
204: No Content
REST API
DELETE http://localhost:7474/db/data/index/auto/node/properties/myProperty1
Accept: application/json; charset=UTF-8
Example response
204: No Content
354
REST API
19.22.WADL Support
The Neo4j REST API is a truly RESTful interface relying on hypermedia controls (links) to advertise
permissible actions to users. Hypermedia is a dynamic interface style where declarative constructs
(semantic markup) are used to inform clients of their next legal choices just in time.
Caution
RESTful APIs cannot be modeled by static interface description languages like WSDL or
WADL.
However for some use cases, developers may wish to expose WADL descriptions of the Neo4j REST
API, particularly when using tooling that expects such.
In those cases WADL generation may be enabled by adding to your servers neo4j.properties file:
unsupported_wadl_generation_enabled=true
Caution
WADL is not an officially supported part of the Neo4j server API because WADL is
insufficiently expressive to capture the set of potential interactions a client can drive
with Neo4j server. Expect the WADL description to be incomplete, and in some cases
contradictory to the real API. In any cases where the WADL description disagrees with the
REST API, the REST API should be considered authoritative. WADL generation may be
withdrawn at any point in the Neo4j release cycle.
355
Chapter20.Deprecations
This section outlines deprecations in Neo4j 2.0 or earlier in order to help you find a smoother
transition path to future releases.
Graph Matching
The graph-matching component will be removed in future releases.
356
PartV.Operations
This part describes how to install and maintain a Neo4j installation. This includes topics such as backing
up the database and monitoring the health of the database as well as diagnosing issues.
358
21.1.System Requirements
Memory constrains graph size, disk I/O constrains read/write performance, as always.
21.1.1.CPU
Performance is generally memory or I/O bound for large graphs, and compute bound for graphs which
fit in memory.
Minimum
Intel Core i3
Recommended
Intel Core i7
21.1.2.Memory
More memory allows even larger graphs, but runs the risk of inducing larger Garbage Collection
operations.
Minimum
2GB
Recommended
1632GB or more
21.1.3.Disk
Aside from capacity, the performance characteristics of the disk are the most important when selecting
storage.
Minimum
10GB SATA
Recommended
SSD w/ SATA
21.1.4.Filesystem
For proper ACID behavior, the filesystem must support flush (fsync, fdatasync).
Minimum
ext4 (or similar)
Recommended
ext4, ZFS
21.1.5.Software
Neo4j is Java-based.
Java
Oracle Java 7 <http://www.oracle.com/technetwork/java/javase/downloads/index.html>
Operating Systems
Linux, HP UX, Windows 2008 for production; additionally Windows XP, Mac OS X for
development.
359
21.2.Server Installation
21.2.1.Deployment Scenarios
As a developer, you may wish to download Neo4j and run it locally on your desktop computer. We
recommend this as an easy way to discover Neo4j.
For Windows, see Section21.2.3, Windows.
For Unix/Linux, see Section21.2.4, Linux.
For OSX, see Section21.2.5, Mac OSX.
As a systems administrator, you may wish to install Neo4j using a packaging system so you can
ensure that a cluster of machines have identical installs. See the section called Linux Packages for
more information on this.
For information on High Availability, please refer to Chapter23, High Availability.
21.2.2.Prerequisites
With the exception of our Windows Installer, youll need a Java Virtual Machine installed on
your computer. We recommend that you install the Java JDK 7 from the Oracle Website <http://
www.oracle.com/technetwork/java/javase/downloads/index.html>.
21.2.3.Windows
Windows Installer
1. Download the version that you want from http://neo4j.org/download.
Select the appropriate version and architecture for your platform.
2. Double-click the downloaded installer file.
3. Follow the prompts.
Note
Note
Some users have reported problems on Windows when using the ZoneAlarm firewall. If
you are having problems getting large responses from the server, or if the web interface
does not work, try disabling ZoneAlarm. Contact ZoneAlarm support to get information on
how to resolve this.
21.2.4.Linux
Linux Packages
For Debian packages, see the instructions at http://debian.neo4j.org.
Unix Console Application
1. Download the latest release from http://neo4j.org/download.
Select the appropriate tar.gz distribution for your platform.
2. Extract the contents of the archive, using: tar -cf <filename>
Refer to the top-level extracted directory as: NEO4J_HOME
3. Change directory to: $NEO4J_HOME
Run: ./bin/neo4j console
4. Stop the server by typing Ctrl-C in the console.
Linux Service
Neo4j can participate in the normal system startup and shutdown process. The following procedure
should work on most popular Linux distributions:
1. cd $NEO4J_HOME
2. sudo ./bin/neo4j-installer install
If asked, enter your password to gain super-user privileges.
3. sudo service neo4j-service status
This should indicate that the server is not running.
361
Note
This approach to running Neo4j as a server is deprecated. We strongly advise you to run
Neo4j from a package where feasible.
You can alternatively build your own init.d script. See for instance the Linux Standard Base
specification on system initialization <http://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-Coregeneric/LSB-Core-generic/tocsysinit.html>, or one of the many samples <https://gist.github.com/
chrisvest/7673244> and tutorials <http://www.linux.com/learn/tutorials/442412-managing-linuxdaemons-with-init-scripts>.
21.2.5.Mac OSX
OSX via Homebrew
Using Homebrew (see http://brew.sh/), to install the latest stable version of Neo4j Server, issue the
following command:
brew install neo4j && neo4j start
This will get a Neo4j instance running on http://localhost:7474. The installation files will reside in
ls /usr/local/Cellar/neo4j/community-{NEO4J_VERSION}/libexec/ to tweak settings and symlink the
database directory if desired.
After the installation, Neo4j can run either as a service or from a terminal.
Running Neo4j from the Terminal
The server can be started in the background from the terminal with the command neo4j start, and
then stopped again with neo4j stop. The server can also be started in the foreground with neo4j
console then its log output will be printed to the terminal.
362
363
364
21.4.Upgrading
A database can be upgraded from a minor version to the next, e.g. 1.1 1.2, and 1.2 1.3, but you
can not jump directly from 1.1 1.3. For version 1.8 in particular, it is possible to upgrade directly
from version 1.5.3 and later, as an explicit upgrade. The upgrade process is a one way step; databases
cannot be downgraded.
For most upgrades, only small changes are required to the database store, and these changes proceed
automatically when you start up the database using the newer version of Neo4j.
However, some upgrades require more significant changes to the database store. In these cases, Neo4j
will refuse to start without explicit configuration to allow the upgrade.
The table below lists recent Neo4j versions, and the type of upgrade required.
Upgrade process for Neo4j version
From Version To Version
Upgrade Type
1.3
1.4
Automatic
1.4
1.5
Explicit
1.5
1.6
Explicit
1.6
1.7
Automatic
1.7
1.8
Automatic
1.8
1.9
Automatic
1.9
2.0
Explicit
Note
Downgrade is supported only between versions which do not have incompatible store
layouts. That means that if you did an upgrade where you didnt have to explicitly set the
allow_store_upgrade flag to false then you can downgrade without any problems to the
previous version used. Otherwise downgrading is not supported. In any case, downgrading
currently cannot be done in a rolling fashion, even in HA deployments. Instead, the whole
cluster must be shutdown and each machine downgraded individually and then the service
can be resumed.
21.4.1.Automatic Upgrade
To perform a normal upgrade (for minor changes to the database store):
1.
2.
3.
4.
21.4.2.Explicit Upgrade
To perform a special upgrade (for significant changes to the database store):
1. Download the newer version of Neo4j.
365
Cypher compatibility
Unless you set the statements to still use Cypher 1.9, they have to be updated for the following
changes:
Pattern syntax
Parentheses are required around node patterns when labels are used. This means that when adding
labels to a pattern like a-->b you should use something like (a:Person)-->(b:Company). Its good
practice to use parentheses in node patterns even where they are not strictly required, to enhance
readability.
Optional relationships
The syntax (a)-[?]->(x) for optional relationships has been removed. Use OPTIONAL MATCH instead
(see the corresponding chapter in the Neo4j Manual).
The ! and ? property operators
Expressions like node.property = "value" will not fail when a node without the property is
encountered, and will instead return NULL. This is the same behavior as node.property! = "value"
in Cypher 1.9. The ! property operator has been removed in 2.0. Support for expressions using
the ? property operator, such as node.property? = "value", have also been removed. You can use
not(has(node.property)) OR node.property = "value" instead, which is compatible with both 1.9
and 2.0.
syntax
The CREATE a={foo:bar} syntax has been removed. Instead, use CREATE (a {foo:bar}).
CREATE
366
Warning
The upgrade process for this upgrade temporarily requires additional disk space, for the
period while the upgrade is in progress. Before starting the upgrade to Neo4j 1.5, you
should ensure that the machine performing the upgrade has free space equal to the current
size of of the database on disk. You can find the current space occupied by the database by
inspecting the store file directory (data/graph.db is the default location in Neo4j server).
Once the upgrade is complete, this additional space is no longer required.
368
This configuration will start a Neo4j server ready for remote debugging attachement at localhost and
port 5005. Use these parameters to attach to the process from Eclipse, IntelliJ or your remote debugger
of choice after starting the server.
369
21.6.1.Technical Information
To gather good statistics about Neo4j usage, UDC collects this information:
Kernel version: The build number, and if there are any modifications to the kernel.
Store id: A randomized globally unique id created at the same time a database is created.
Ping count: UDC holds an internal counter which is incremented for every ping, and reset for every
restart of the kernel.
Source: This is either "neo4j" or "maven". If you downloaded Neo4j from the Neo4j website, its
"neo4j", if you are using Maven to get Neo4j, it will be "maven".
Java version: The referrer string shows which version of Java is being used.
MAC address to uniquely identify instances behind firewalls.
Registration id: For registered server instances.
Tags about the execution context (e.g. test, language, web-container, app-container, spring, ejb).
Neo4j Edition (community, enterprise).
A hash of the current cluster name (if any).
Distribution information for Linux (rpm, dpkg, unknown).
User-Agent header for tracking usage of REST client drivers
After startup, UDC waits for ten minutes before sending the first ping. It does this for two reasons;
first, we dont want the startup to be slower because of UDC, and secondly, we want to keep pings
from automatic tests to a minimum. The ping to the UDC servers is done with a HTTP GET.
370
371
372
22.1.Introduction
To gain good performance, these are the things to look into first:
Make sure the JVM is not spending too much time performing garbage collection. Monitoring
heap usage on an application that uses Neo4j can be a bit confusing since Neo4j will increase the
size of caches if there is available memory and decrease if the heap is getting full. The goal is to
have a large enough heap to make sure that heavy/peak load will not result in so called GC trashing
(performance can drop as much as two orders of magnitude when GC trashing happens).
Start the JVM with the -server flag and a good sized heap (see Section22.8, JVM Settings).
Having too large heap may also hurt performance so you may have to try some different heap sizes.
Use the parallel/concurrent garbage collector (we found that -XX:+UseConcMarkSweepGC works well in
most use-cases)
Looking at the example above you will also notice one of the most basic command line parameters:
the one for specifying the classpath. The classpath is the path in which the JVM searches for your
classes. It is usually a list of jar-files. Specifying the classpath is done by specifying the flag -cp (or classpath) and then the value of the classpath. For Neo4j applications this should at least include the
path to the Neo4j neo4j-kernel.jar and the Java Transaction API (jta.jar) as well as the path where
the classes for your application are located.
Tip
On Linux, Unix and Mac OS X each element in the path list are separated by a colon
symbol (:), on Windows the path elements are separated by a semicolon (;).
When using the Neo4j REST server, see Section22.2, Server Configuration for how to add
configuration settings for the database to the server.
373
22.2.Server Configuration
Quick info
Note
On Windows systems, absolute locations including drive letters need to read "c:/data/db".
Specify the HTTP server port supporting data, administrative, and UI access:
org.neo4j.server.webserver.port=7474
Specify the client accept pattern for the webserver (default is 127.0.0.1, localhost only):
allow any client to connect
org.neo4j.server.webserver.address=0.0.0.0
Set the URI path for the REST data API through which the database is accessed. This should be a
relative path.
org.neo4j.server.webadmin.data.uri=/db/data/
Setting the management URI for the administration API that the Webadmin tool uses. This should be
a relative path.
org.neo4j.server.webadmin.management.uri=/db/manage
Force the server to use IPv4 network addresses, in conf/neo4j-wrapper.conf under the section Java
Additional Parameters add a new paramter:
wrapper.java.additional.3=-Djava.net.preferIPv4Stack=true
Specify the number of threads used by the Neo4j Web server to control the level of concurrent HTTP
requests that the server will service.
374
Note
The default value is 10 times the number of CPUs reported available by the JVM.
The server guards against orphaned transactions by using a timeout. If there are no requests for a
given transaction within the timeout period, the server will roll it back. You can configure the timeout
period by setting the following property to the number of seconds before timeout. The default timeout
is 60 seconds.
org.neo4j.server.transaction.timeout=60
Low-level performance tuning parameters can be explicitly set by referring to the following property:
org.neo4j.server.db.tuning.properties=neo4j.properties
If this property isnt set, the server will look for a file called neo4j.properties in the same directory as
the neo4j-server.properties file.
If this property isnt set, and there is no neo4j.properties file in the default configuration directory,
then the server will log a warning. Subsequently at runtime the database engine will attempt tune itself
based on the prevailing conditions.
By default log files are rotated at approximately 10Mb and named consecutively neo4j.<id>.<rotation
sequence #>.log To change the naming scheme, rotation frequency and backlog size modify
java.util.logging.FileHandler.pattern
java.util.logging.FileHandler.limit
java.util.logging.FileHandler.count
respectively to your needs. Details are available at the Javadoc for java.util.logging.FileHandler
<http://download.oracle.com/javase/6/docs/api/java/util/logging/FileHandler.html>.
Apart from log statements originating from the Neo4j server, other libraries report their messages
through various frameworks.
375
Warning
org.neo4j.server.http.log.enabled=true tells the server that HTTP logging is enabled. HTTP logging
can be totally disabled by setting this property to false. org.neo4j.server.http.log.config=conf/neo4jhttp-logging.xml specifies the logging format and rollover policy file that governs how HTTP log
output is presented and archived. The defaults provided with Neo4j server uses an hourly log rotation
and Common Log Format <http://en.wikipedia.org/wiki/Common_Log_Format>.
If logging is set up to use log files then the server will check that the log file directory exists and is
writable. If this check fails, then the server will not startup and wil report the failure another available
channel like standard out.
This line is already present and needs uncommenting. Note also that logging is not directed to
console ; You will find the logging statements in data/log/ne4j-gc.log or whatever directory you set at
the option.
376
377
On restarting the server the tuning enhancements specified in the neo4j.properties file will be loaded
and configured into the underlying database engine.
Meaning
For more information on the tuning properties, see Section22.8, JVM Settings.
378
22.4.Performance Guide
This is the Neo4j performance guide. It will attempt to give you guidance on how to use Neo4j to
achieve maximum performance.
The first thing is to make sure the JVM is running well and not spending too much time in garbage
collection. Monitoring heap usage of an application that uses Neo4j can be a bit confusing since Neo4j
will increase the size of caches if there is available memory and decrease if the heap is getting full.
The goal is to have a large enough heap so heavy/peak load will not result in so called GC trashing
(performance can drop as much as two orders of a magnitude when this happens).
Start the JVM with -server flag and -Xmx<good sized heap> (f.ex. -Xmx512M for 512Mb memory or
-Xmx3G for 3Gb memory). Having too large heap may also hurt performance so you may have to
try out some different heap sizes. Make sure a parallel/concurrent garbage collector is running (-XX:
+UseConcMarkSweepGC works well in most use-cases).
Finally make sure that the OS has some memory left to manage proper file system caches. This
means, if your server has 8GB of RAM dont use all of that RAM for heap (unless you have turned
off memory mapped buffers), but leave a good part of it to the OS. For more information on this see
Chapter22, Configuration & Performance.
For Linux specific tweaks, see Section22.12, Linux Performance Guide.
Neo4j manages its primitives (nodes, relationships and properties) different depending on how you
use Neo4j. For example if you never get a property from a certain node or relationship that node or
relationship will not have its properties loaded into memory. The first time, after loading a node or
relationship, that any property is accessed all the properties are loaded for that entity. If any of those
properties contain an array larger than a few elements or a long string such values are loaded on
demand when requesting them individually. Similarly, relationships of a node will only be loaded the
first time they are requested for that node.
Nodes and relationships are cached using LRU caches. If you (for some strange reason) only work
with nodes the relationship cache will become smaller and smaller while the node cache is allowed
to grow (if needed). Working with many relationships and few nodes results in a bigger relationship
cache and smaller node cache.
The Neo4j API specification does not say anything about order regarding relationships so invoking
Node.getRelationships() may return the relationships in a different order than the previous invocation.
This allows us to make even heavier optimizations returning the relationships that are most commonly
traversed.
All in all Neo4j has been designed to be very adaptive depending on how it is used. The
(unachievable) overall goal is to be able to handle any incoming operation without having to go down
and work with the file/disk I/O layer.
22.4.3.Configuring Neo4j
In Chapter22, Configuration & Performance page theres information on how to configure Neo4j and
the JVM. These settings have a lot of impact on performance.
Note
Make sure to read Section22.12, Linux Performance Guide as well for more specific
information.
380
This works fine in most scenarios, but in this particular scenario many instances of that POJO is
being worked with in nested loops adding/removing/getting/finding to collection classes. Profiling the
applications will show that the equals implementation is being called many times and can be viewed
as a hot spot. Adding second-level caching for the equals override will in this particular scenario
increase performance.
private Object cachedProperty = null;
public boolean equals( Object obj )
{
if ( cachedProperty == null )
{
cachedProperty = underlyingNode.getProperty( "some_property" );
}
return cachedProperty.equals( obj );
}
public int hashCode()
{
if ( cachedPropety == null )
{
cachedProperty = underlyingNode.getProperty( "some_property" );
}
return cachedProperty.hashCode();
}
The problem with this is that now we need to invalidate the cached property whenever some_property
is changed (may not be a problem in this scenario since the state picked for equals and hash code
computation often wont change).
Tip
To sum up, avoid second-level caching if possible and only add it when you really need it.
381
22.5.Kernel configuration
These are the configuration options you can pass to the neo4j kernel. They can either be passed as a
map when using the embedded database, or in the neo4j.properties file when using the Neo4j Server.
All stores total mapped memory size
all_stores_total_mapped_memory_size
The size to allocate for a memory mapping pool to be shared between all stores.
Default value: 524288000
Allow store upgrade
allow_store_upgrade
Whether to allow a store upgrade in case the current version of the database starts against an older
store version. Setting this to true does not guarantee successful upgrade, just that it allows an attempt
at it.
Default value: false
Array block size
array_block_size
Specifies the block size for storing arrays. This parameter is only honored when the store is created,
otherwise it is ignored. The default block size is 120 bytes, and the overhead of each block is the
same as for string blocks, i.e., 8 bytes.
Default value: 120
Backup slave
backup_slave
The type of cache to use for nodes and relationships. Note that the Neo4j Enterprise Edition has the
additional 'hpc' cache type (High-Performance Cache). See the chapter on caches in the manual for
more information.
Default value: soft
Cypher parser version
cypher_parser_version
An identifier that uniquely identifies this graph database instance within this JVM. Defaults to an
auto-generated number depending on how many instance are started in this JVM.
Gc monitor threshold
gc_monitor_threshold
The amount of time in ms the monitor thread has to be blocked before logging a message it was
blocked.
Default value: 200
Gc monitor wait time
gc_monitor_wait_time
Amount of time in ms the GC monitor thread will wait before taking another measurement.
Default value: 100
Intercept committing transactions
intercept_committing_transactions
Determines whether any TransactionInterceptors loaded will intercept prepared transactions before
they reach the logical log.
Default value: false
Intercept deserialized transactions
intercept_deserialized_transactions
Make Neo4j keep the logical transaction logs for being able to backup the database.Can be used for
specifying the threshold to prune logical logs after. For example "10 days" will prune logical logs
that only contains transactions older than 10 days from the current time, or "100k txs" will keep the
100k latest transactions and prune any older transactions.
Default value: true
Label block size
label_block_size
Default value: 60
383
The number of records to be loaded between regular logging of memory mapping statistics.
Default value: 1000000
Logging.threshold for rotation
logging. threshold_for_rotation
Threshold in bytes for when database logs (text logs, for debugging, that is) are rotated.
Default value: 104857600
Logical log
logical_log
The base name for the logical log files, either an absolute path or relative to the store_dir setting.
This should generally not be changed.
Default value: nioneo_logical.log
Logical log rotation threshold
logical_log_rotation_threshold
Specifies at which file size the logical log will auto-rotate. 0 means that no rotation will
automatically occur based on file size. Default is 25M
Default value: 26214400
Lucene searcher cache size
lucene_searcher_cache_size
Integer value that sets the maximum number of open lucene index searchers.
Default value: 2147483647
384
The base name for the Neo4j Store files, either an absolute path or relative to the store_dir setting.
This should generally not be changed.
Default value: neostore
Neostore.nodestore.db.mapped memory
neostore. nodestore. db. mapped_memory
The size to allocate for memory mapping the array property store.
Default value: 136314880
Neostore.propertystore.db.index.keys.mapped memory
neostore. propertystore. db. index. keys. mapped_memory
The size to allocate for memory mapping the store for property key strings.
Default value: 1048576
Neostore.propertystore.db.index.mapped memory
neostore. propertystore. db. index. mapped_memory
The size to allocate for memory mapping the store for property key indexes.
Default value: 1048576
Neostore.propertystore.db.mapped memory
neostore. propertystore. db. mapped_memory
The size to allocate for memory mapping the property value store.
Default value: 94371840
Neostore.propertystore.db.strings.mapped memory
neostore. propertystore. db. strings. mapped_memory
The size to allocate for memory mapping the string property store.
Default value: 136314880
385
Controls the auto indexing feature for nodes. Setting to false shuts it down, while true enables it by
default for properties listed in the node_keys_indexable setting.
Default value: false
Node keys indexable
node_keys_indexable
A list of property names (comma separated) that will be indexed by default. This applies to Nodes
only.
Query cache size
query_cache_size
Used to set the number of Cypher query execution plans that are cached.
Default value: 100
Read only database
read_only
Only allow read operations from this Neo4j instance. This mode still requires write access to the
directory for lock purposes
Default value: false
Rebuild idgenerators fast
rebuild_idgenerators_fast
Use a quick approach for rebuilding the ID generators. This give quicker recovery time, but will limit
the ability to reuse the space of deleted entities.
Default value: true
Relationship auto indexing
relationship_auto_indexing
Controls the auto indexing feature for relationships. Setting to false shuts it down, while true enables
it by default for properties listed in the relationship_keys_indexable setting.
Default value: false
Relationship grab size
relationship_grab_size
A list of property names (comma separated) that will be indexed by default. This applies to
Relationships only.
Remote logging enabled
remote_logging_enabled
Specifies the block size for storing strings. This parameter is only honored when the store is created,
otherwise it is ignored. Note that each character in a string occupies two bytes, meaning that a block
size of 120 (the default size) will hold a 60 character long string before overflowing into a second
block. Also note that each block carries an overhead of 8 bytes. This means that if the block size is
120, the size of the stored records will be 128 bytes.
Default value: 120
Tx manager impl
tx_manager_impl
The name of the Transaction Manager service to use as defined in the TM service provider
constructor.
Default value: native
387
Tell Neo4j to use memory mapped buffers for accessing the native storage layer.
Default value: true
388
22.6.Caches in Neo4j
For how to provide custom configuration to Neo4j, see Section22.1, Introduction.
Neo4j utilizes two different types of caches: A file buffer cache and an object cache. The file buffer
cache caches the storage file data in the same format as it is stored on the durable storage media.
The object cache caches the nodes, relationships and properties in a format that is optimized for high
traversal speeds and transactional writes.
The file buffer cache is sometimes called low level cache or file system cache.
It caches the Neo4j data as stored on the durable media.
It uses the operating system memory mapping features when possible.
Neo4j will configure the cache automatically as long as the heap size of the JVM is
configured properly.
The file buffer cache caches the Neo4j data in the same format as it is represented on the durable
storage media. The purpose of this cache layer is to improve both read and write performance. The
file buffer cache improves write performance by writing to the cache and deferring durable write until
the logical log is rotated. This behavior is safe since all transactions are always durably written to the
logical log, which can be used to recover the store files in the event of a crash.
Since the operation of the cache is tightly related to the data it stores, a short description of the Neo4j
durable representation format is necessary background. Neo4j stores data in multiple files and relies
on the underlying file system to handle this efficiently. Each Neo4j storage file contains uniform fixed
size records of a particular type:
Store file
neostore.nodestore.db
14 B
Nodes
neostore.relationshipstore.db
33 B
Relationships
neostore.propertystore.db
41 B
neostore.propertystore.db.strings
128 B
neostore.propertystore.db.arrays
128 B
For strings and arrays, where data can be of variable length, data is stored in one or more 120B
chunks, with 8B record overhead. The sizes of these blocks can actually be configured when the store
is created using the string_block_size and array_block_size parameters. The size of each record type
can also be used to calculate the storage requirements of a Neo4j graph or the appropriate cache size
for each file buffer cache. Note that some strings and arrays can be stored without using the string
store or the array store respectively, see Section22.9, Compressed storage of short strings and
Section22.10, Compressed storage of short arrays.
Neo4j uses multiple file buffer caches, one for each different storage file. Each file buffer cache
divides its storage file into a number of equally sized windows. Each cache window contains an even
number of storage records. The cache holds the most active cache windows in memory and tracks hit
389
Important
Note that the block sizes can only be configured at store creation time.
Configuration
Parameter
Possible values
use_memory_mapped_buffers
true
or false
Effect
If set to true Neo4j will use
the operating systems memory
mapping functionality for the
file buffer cache windows. If set
to false Neo4j will use its own
buffer implementation. In this case
the buffers will reside in the JVM
heap which needs to be increased
accordingly. The default value for
this parameter is true, except on
Windows.
string_block_size
The number of
bytes per block.
390
Possible values
array_block_size
dump_configuration
Effect
each character in a string occupies
two bytes, meaning that a block
size of 120 (the default size) will
hold a 60 character long string
before overflowing into a second
block. Also note that each block
carries an overhead of 8 bytes.
This means that if the block size is
120, the size of the stored records
will be 128 bytes.
true
or false
When memory mapped buffers are used (use_memory_mapped_buffers = true) the heap size of the JVM
must be smaller than the total available memory of the computer, minus the total amount of memory
used for the buffers. When heap buffers are used (use_memory_mapped_buffers = false) the heap size of
the JVM must be large enough to contain all the buffers, plus the runtime heap memory requirements
of the application and the object cache.
When reading the configuration parameters on startup Neo4j will automatically configure the
parameters that are not specified. The cache sizes will be configured based on the available memory
on the computer, how much is used by the JVM heap, and how large the storage files are.
22.6.2.Object cache
Quick info
The object cache is sometimes called high level cache.
It caches the Neo4j data in a form optimized for fast traversal.
The object cache caches individual nodes and relationships and their properties in a form that is
optimized for fast traversal of the graph. There are two different categories of object caches in Neo4j.
There is the reference caches. Here Neo4j will utilize as much as it can out of the allocated heap
memory for the JVM for object caching and relies on garbage collection for eviction from the cache
in an LRU manner. Note however that Neo4j is competing for the heap space with other objects
in the same JVM, such as a your application, if deployed in embedded mode, and Neo4j will let the
application win by using less memory if the application needs more.
391
Note
The High-Performance Cache described below is only available in the Neo4j Enterprise
Edition.
The other is the High-Performance Cache which gets assigned a certain amount of space in the JVM
heap and will purge objects whenever it grows bigger than that. It is assigned a maximum amount
of memory which the sum of all cached objects in it will not exceed. Objects will be evicted from
cache when the maximum size is about to be reached, instead of relying on garbage collection (GC)
to make that decision. Here the competition with other objects in the heap as well as GC-pauses can
be better controlled since the cache gets assigned a maximum heap space usage. The overhead of the
High-Performance Cache is also much smaller as well as insert/lookup times faster than for reference
caches.
Tip
The use of heap memory is subject to the java garbage collector depending on the cache
type some tuning might be needed to play well with the GC at large heap sizes. Therefore,
assigning a large heap for Neo4js sake isnt always the best strategy as it may lead to long
GC-pauses. Instead leave some space for Neo4js filesystem caches. These are outside of
the heap and under under the kernels direct control, thus more efficiently managed.
The content of this cache are objects with a representation geared towards supporting the Neo4j object
API and graph traversals. Reading from this cache is 5 to 10 times faster than reading from the file
buffer cache. This cache is contained in the heap of the JVM and the size is adapted to the current
amount of available heap memory.
Nodes and relationships are added to the object cache as soon as they are accessed. The cached
objects are however populated lazily. The properties for a node or relationship are not loaded until
properties are accessed for that node or relationship. String (and array) properties are not loaded until
that particular property is accessed. The relationships for a particular node is also not loaded until the
relationships are accessed for that node.
Configuration
The main configuration parameter for the object cache is the cache_type parameter. This specifies
which cache implementation to use for the object cache. Note that there will exist two cache instances,
one for nodes and one for relationships. The available cache types are:
cache_type
Description
none
soft
Provides optimal utilization of the available memory. Suitable for high performance
traversal. May run into GC issues under high load if the frequently accessed parts of
the graph does not fit in the cache.
This is the default cache implementation.
weak
Provides short life span for cached objects. Suitable for high throughput applications
where a larger portion of the graph than what can fit into memory is frequently
accessed.
strong
This cache will hold on to all data that gets loaded to never release it again. Provides
good performance if your graph is small enough to fit in memory.
hpc
Description
fast insert/lookup. Should be the best option for most scenarios. See below on how to
configure it. Note that this option is only available in the Neo4j Enterprise Edition.
High-Performance Cache
Since the High-Performance Cache operates with a maximum size in the JVM it may be configured
per use case for optimal performance. There are two aspects of the cache size.
One is the size of the array referencing the objects that are put in the cache. It is specified as a
fraction of the heap, for example specifying 5 will let that array itself take up 5% out of the entire
heap. Increasing this figure (up to a maximum of 10) will reduce the chance of hash collisions at the
expense of more heap used for it. More collisions means more redundant loading of objects from the
low level cache.
configuration option
Example
value
node_cache_array_fraction
relationship_cache_array_fraction
The other aspect is the maximum size of all the objects in the cache. It is specified as size in bytes, for
example 500M for 500 megabytes or 2G for two gigabytes. Right before the maximum size is reached
a purge is performed where (currently) random objects are evicted from the cache until the cache size
gets below 90% of the maximum size. Optimal settings for the maximum size depends on the size
of your graph. The configured maximum size should leave enough room for other objects to coexist
in the same JVM, but at the same time large enough to keep loading from the low level cache at a
minimum. Predicted load on the JVM as well as layout of domain level objects should also be take
into consideration.
configuration option
Example
value
node_cache_size
2G
relationship_cache_size
800M
You can read about references and relevant JVM settings for Sun HotSpot here:
Understanding soft/weak references <http://weblogs.java.net/blog/enicholas/archive/2006/05/
understanding_w.html>
How Hotspot Decides to Clear SoftReferences <http://jeremymanson.blogspot.com/2009/07/howhotspot-decides-to-clear_07.html>
HotSpot FAQ <http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_softrefs>
393
22.7.Logical logs
Logical logs in Neo4j are the journal of which operations happens and are the source of truth in
scenarios where the database needs to be recovered after a crash or similar. Logs are rotated every
now and then (defaults to when they surpass 25 Mb in size) and the amount of legacy logs to keep can
be configured. Purpose of keeping a history of logical logs include being able to serve incremental
backups as well as keeping an HA cluster running.
For any given configuration at least the latest non-empty logical log will be kept, but configuration
can be supplied to control how much more to keep. There are several different means of controlling it
and the format in which configuration is supplied is:
keep_logical_logs=<true/false>
keep_logical_logs=<amount> <type>
For example:
Will keep logical logs indefinitely
keep_logical_logs=true
Will keep only the most recent non-empty log
keep_logical_logs=false
Will keep logical logs which contains any transaction committed within 30 days
keep_logical_logs=30 days
Will keep logical logs which contains any of the most recent 500 000 transactions
keep_logical_logs=500k txs
Full list:
Type
Description
Example
files
"10 files"
size
"300M size" or
"1G size"
txs
"250k txs" or
"5M txs"
hours
days
394
"50 days"
22.8.JVM Settings
22.8.1.Background
There are two main memory parameters for the JVM, one controls the heap space and the other
controls the stack space. The heap space parameter is the most important one for Neo4j, since this
governs how many objects you can allocate. The stack space parameter governs the how deep the call
stack of your application is allowed to get.
When it comes to heap space the general rule is: the larger heap space you have the better, but make
sure the heap fits in the RAM memory of the computer. If the heap is paged out to disk performance
will degrade rapidly. Having a heap that is much larger than what your application needs is not
good either, since this means that the JVM will accumulate a lot of dead objects before the garbage
collector is executed, this leads to long garbage collection pauses and undesired performance
behavior.
Having a larger heap space will mean that Neo4j can handle larger transactions and more concurrent
transactions. A large heap space will also make Neo4j run faster since it means Neo4j can fit a larger
portion of the graph in its caches, meaning that the nodes and relationships your application uses
frequently are always available quickly. The default heap size for a 32bit JVM is 64MB (and 30%
larger for 64bit), which is too small for most real applications.
Neo4j works fine with the default stack space configuration, but if your application implements some
recursive behavior it is a good idea to increment the stack size. Note that the stack size is shared for
all threads, so if you application is running a lot of concurrent threads it is a good idea to increase the
stack size.
The heap size is set by specifying the -Xmx???m parameter to hotspot, where ??? is the heap size in
megabytes. Default heap size is 64MB for 32bit JVMs, 30% larger (appr. 83MB) for 64bit JVMs.
The stack size is set by specifying the -Xss???m parameter to hotspot, where ??? is the stack size in
megabytes. Default stack size is 512kB for 32bit JVMs on Solaris, 320kB for 32bit JVMs on Linux
(and Windows), and 1024kB for 64bit JVMs.
Most modern CPUs implement a Non-Uniform Memory Access (NUMA) architecture <http://
en.wikipedia.org/wiki/Non-Uniform_Memory_Access>, where different parts of the memory
have different access speeds. Suns Hotspot JVM is able to allocate objects with awareness of the
NUMA structure as of version 1.6.0 update 18. When enabled this can give up to 40% performance
improvements. To enabled the NUMA awareness, specify the -XX:+UseNUMA parameter (works only
when using the Parallel Scavenger garbage collector (default or -XX:+UseParallelGC not the concurrent
mark and sweep one).
Properly configuring memory utilization of the JVM is crucial for optimal performance. As an
example, a poorly configured JVM could spend all CPU time performing garbage collection (blocking
all threads from performing any work). Requirements such as latency, total throughput and available
hardware have to be considered to find the right setup. In production, Neo4j should run on a multi
core/CPU platform with the JVM in server mode.
Important
When running Neo4j on Windows, keep in mind that the memory mapped buffers are
allocated on heap by default, so they need to be taken into account when determining heap
size.
Guidelines for heap size
Number of
RAM size
primitives
Heap
configuration
Reserved RAM
for the OS
10M
2GB
512MB
the rest
100M
8GB+
1-4GB
1-2GB
1B+
16GB-32GB+
4GB+
1-2GB
Tip
The recommended garbage collector to use when running Neo4j in production is the
Concurrent Mark and Sweep Compactor turned on by supplying -XX:+UseConcMarkSweepGC
as a JVM parameter.
When having made sure that the heap size is well configured the second thing to tune in order to tune
the garbage collector for your application is to specify the sizes of the different generations of the
heap. The default settings are well tuned for "normal" applications, and work quite well for most
applications, but if you have an application with either really high allocation rate, or a lot of long
lived objects you might want to consider tuning the sizes of the heap generation. The ratio between
the young and tenured generation of the heap is specified by using the -XX:NewRatio= command line
option (where is replaced by a number). The default ratio is 1:12 for client mode JVM, and 1:8 for
server mode JVM. You can also specify the size of the young generation explicitly using the -Xmn
command line option, which works just like the -Xmx option that specifies the total heap space.
GC shortname
Generation
Command line
parameter
Comment
Copy
Young
-XX:+UseSerialGC
MarkSweepCompact
Tenured
-XX:+UseSerialGC
ConcurrentMarkSweep Tenured
-XX:+UseConcMarkSweepGC
ParNew
-XX:+UseParNewGC
The parallel
Young Generation
Young
396
Generation
Command line
parameter
Comment
Collector can only
be used with the
Concurrent mark and
sweep compactor.
PS Scavenge
Young
-XX:+UseParallelGC
PS MarkSweep
Tenured
-XX:+UseParallelGC
These are the default configurations on some platforms according to our non-exhaustive research:
JVM
-d32 -client
-d32 -server
Mac OS X Snow
Leopard, 64-bit,
Hotspot 1.6.0_17
ParNew and
PS Scavenge
ConcurrentMarkSweep MarkSweep
Ubuntu, 32-bit,
Hotspot 1.6.0_16
Copy and
MarkSweepCompact
-d64 -server
Copy and
MarkSweepCompact
397
-d64 -client
PS Scavenge
ConcurrentMarkSweep MarkSweep
N/A
N/A
and PS
Neo4j will try to classify your strings in a short string class and if it manages that it will treat it
accordingly. In that case, it will be stored without indirection in the property store, inlining it instead
in the property record, meaning that the dynamic string store will not be involved in storing that value,
leading to reduced disk footprint. Additionally, when no string record is needed to store the property,
it can be read and written in a single lookup, leading to performance improvements and less disk space
required.
The various classes for short strings are:
Numerical, consisting of digits 0..9 and the punctuation space, period, dash, plus, comma and
apostrophe.
Date, consisting of digits 0..9 and the punctuation space dash, colon, slash, plus and comma.
Hex (lower case), consisting of digits 0..9 and lower case letters a..f
Hex (upper case), consisting of digits 0..9 and upper case letters a..f
Upper case, consisting of upper case letters A..Z, and the punctuation space, underscore, period,
dash, colon and slash.
Lower case, like upper but with lower case letters a..z instead of upper case
E-mail, consisting of lower case letters a..z and the punctuation comma, underscore, period, dash,
plus and the at sign (@).
URI, consisting of lower case letters a..z, digits 0..9 and most punctuation available.
Alpha-numerical, consisting of both upper and lower case letters a..zA..z, digits 0..9 and
punctuation space and underscore.
Alpha-symbolical, consisting of both upper and lower case letters a..zA..Z and the punctuation
space, underscore, period, dash, colon, slash, plus, comma, apostrophe, at sign, pipe and semicolon.
European, consisting of most accented european characters and digits plus punctuation space, dash,
underscore and period like latin1 but with less punctuation.
Latin 1.
UTF-8.
In addition to the strings contents, the number of characters also determines if the string can be
inlined or not. Each class has its own character count limits, which are
Character count limits
String class
Character
count
limit
54
43
36
European
31
Latin1
27
UTF-8
14
That means that the largest inline-able string is 54 characters long and must be of the Numerical class
and also that all Strings of size 14 or less will always be inlined.
398
399
For each member of the array, it determines the position of leftmost set bit.
Determines the largest such position among all members of the array
It reduces all members to that number of bits
Stores those values, prefixed by a small header.
That means that when even a single negative value is included in the array then the natural size of the
primitives will be used.
There is a possibility that the result can be inlined in the property record if:
It is less than 24 bytes after compression
It has less than 64 members
For example, an array long[] {0L, 1L, 2L, 4L} will be inlined, as the largest entry (4) will require
3 bits to store so the whole array will be stored in 4*3=12 bits. The array long[] {-1L, 1L, 2L, 4L}
however will require the whole 64 bits for the -1 entry so it needs 64*4 = 32 bytes and it will end up
in the dynamic store.
400
Important
Neo4j makes heavy use of the java.nio package. Native I/O will result in memory being
allocated outside the normal Java heap so that memory usage needs to be taken into
consideration. Other processes running on the OS will impact the availability of such
memory. Neo4j will require all of the heap memory of the JVM plus the memory to be
used for memory mapping to be available as physical memory. Other processes may thus
not use more than what is available after the configured memory allocation is made for
Neo4j.
A well configured OS with large disk caches will help a lot once we get cache misses in the node and
relationship caches. Therefore it is not a good idea to use all available memory as Java heap.
If you look into the directory of your Neo4j database, you will find its store files, all prefixed by
neostore:
nodestore stores information about nodes
relationshipstore holds all the relationships
propertystore stores information of properties and all simple properties such as primitive types
(both for relationships and nodes)
propertystore strings stores all string properties
propertystore arrays stores all array properties
There are other files there as well, but they are normally not interesting in this context.
This is how the default memory mapping configuration looks:
neostore.nodestore.db.mapped_memory=25M
neostore.relationshipstore.db.mapped_memory=50M
neostore.propertystore.db.mapped_memory=90M
neostore.propertystore.db.strings.mapped_memory=130M
neostore.propertystore.db.arrays.mapped_memory=130M
In this example the application is running on a machine with 4GB of RAM. Weve reserved about
2GB for the OS and other programs. The Java heap is set to 1.5GB, that leaves about 500MB of RAM
that can be used for memory mapping.
401
Tip
If traversal speed is the highest priority it is good to memory map as much as possible of
the node- and relationship stores.
An example configuration on the example machine focusing on traversal speed would then look
something like:
neostore.nodestore.db.mapped_memory=15M
neostore.relationshipstore.db.mapped_memory=285M
neostore.propertystore.db.mapped_memory=100M
neostore.propertystore.db.strings.mapped_memory=100M
neostore.propertystore.db.arrays.mapped_memory=0M
The configuration above will fit the entire graph (with exception to properties) in memory.
A rough formula to calculate the memory needed for the nodes:
number_of_nodes * 9 bytes
Properties will typically only be injected once and never read so a few megabytes for the property
store and string store is usually enough. If you have very large strings or arrays you may want to
increase the amount of memory assigned to the string and array store files.
An important thing to remember is that the above configuration will need a Java heap of 3.3G+ since
in batch inserter mode normal Java buffers that gets allocated on the heap will be used instead of
memory mapped ones.
402
If you have a standard hard drive in the machine you may know that it is not capable of transfer
speeds as high as 1.4GB/s. What is measured is how fast we can read a file that is cached for us by the
operating system.
Next we will use a small utility that simulates the Neo4j kernel behavior to benchmark write speed of
the system.
The utility will be given a store file (large file we just created) and a name of a log file. Then a record
size in bytes, min tx size, max tx size and transaction count must be set. When started the utility will
map the large store file entirely in memory and read (transaction size) records from it randomly and
403
We see that we get about 6185 record updates/s and 20 transactions/s with the current transaction
size. We can change the transaction size to be bigger, for example writing 10 transactions of size
1000-5000 records:
$ ./run store logfile 33 1000 5000 10
tx_count[10] records[24511] fdatasyncs[10] read[0.77139187 MB] wrote[1.5427837 MB]
Time was: 0.792
12.626263 tx/s, 30948.232 records/s, 12.626263 fdatasyncs/s, 997.35516 kB/s on reads, 1994.7103 kB/s on writes
With larger transaction we will do fewer of them per second but record throughput will increase. Lets
see if it scales, 10 transactions in under 1s then 100 of them should execute in about 10s:
$ ./run store logfile 33 1000 5000 100
tx_count[100] records[308814] fdatasyncs[100] read[9.718763 MB] wrote[19.437527 MB]
Time was: 65.115
1.5357445 tx/s, 4742.594 records/s, 1.5357445 fdatasyncs/s, 152.83751 kB/s on reads, 305.67502 kB/s on writes
This is not very linear scaling. We modified a bit more than 10x records in total but the time jumped
up almost 100x. Running the benchmark watching vmstat output will reveal that something is not as it
should be:
$ vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---r b
swpd
free
buff cache
si
so
bi
bo
in
cs us sy id wa
0 1 47660 298884 136036 2650324
0
0
0 10239 1167 2268 5 7 46 42
0 1 47660 302728 136044 2646060
0
0
0 7389 1267 2627 6 7 47 40
0 1 47660 302408 136044 2646024
0
0
0 11707 1861 2016 8 5 48 39
0 2 47660 302472 136060 2646432
0
0
0 10011 1704 1878 4 7 49 40
0 1 47660 303420 136068 2645788
0
0
0 13807 1406 1601 4 5 44 47
There are a lot of blocks going out to IO, way more than expected for the write speed we are seeing in
the benchmark. Another observation that can be made is that the Linux kernel has spawned a process
called "flush-x:x" (run top) that seems to be consuming a lot of resources.
The problem here is that the Linux kernel is trying to be smart and write out dirty pages from the
virtual memory. As the benchmark will memory map a 1GB file and do random writes it is likely that
this will result in 1/4 of the memory pages available on the system to be marked as dirty. The Neo4j
kernel is not sending any system calls to the Linux kernel to write out these pages to disk however the
Linux kernel decided to start doing so and it is a very bad decision. The result is that instead of doing
sequential like writes down to disk (the logical log file) we are now doing random writes writing
regions of the memory mapped file to disk.
It is possible to observe this behavior in more detail by looking at /proc/vmstat "nr_dirty" and
"nr_writeback" values. By default the Linux kernel will start writing out pages at a very low ratio of
dirty pages (10%).
$ sync
$ watch grep -A 1 dirty /proc/vmstat
...
404
The "sync" command will write out all data (that needs writing) from memory to disk. The second
command will watch the "nr_dirty" and "nr_writeback" count from vmstat. Now start the benchmark
again and observe the numbers:
nr_dirty 124947
nr_writeback 232
The "nr_dirty" pages will quickly start to rise and after a while the "nr_writeback" will also increase
meaning the Linux kernel is scheduling a lot of pages to write out to disk.
Fixing the problem
As we have 4GB RAM on the machine and memory map a 1GB file that does not need its content
written to disk (until we tell it to do so because of logical log rotation or Neo4j kernel shutdown) it
should be possible to do endless random writes to that memory with high throughput. All we have to
do is to tell the Linux kernel to stop trying to be smart. Edit the /etc/sysctl.conf (need root access) and
add the following lines:
vm.dirty_background_ratio = 50
vm.dirty_ratio = 80
The "vm.dirty_background_ratio" tells at what ratio should the linux kernel start the background task
of writing out dirty pages. We increased this from the default 10% to 50% and that should cover the
1GB memory mapped file. The "vm.dirty_ratio" tells at what ratio all IO writes become synchronous,
meaning that we can not do IO calls without waiting for the underlying device to complete them
(which is something you never want to happen).
Rerun the benchmark:
$ ./run store logfile 33 1000 5000 100
tx_count[100] records[265624] fdatasyncs[100] read[8.35952 MB] wrote[16.71904 MB]
Time was: 6.781
14.7470875 tx/s, 39171.805 records/s, 14.7470875 fdatasyncs/s, 1262.3726 kB/s on reads, 2524.745 kB/s on writes
Results are now more in line with what can be expected, 10x more records modified results in 10x
longer execution time. The vmstat utility will not report any absurd amount of IO blocks going out (it
reports the ones caused by the fdatasync to the logical log) and Linux kernel will not spawn a "flushx:x" background process writing out dirty pages caused by writes to the memory mapped store file.
The usual default of 1024 is often not enough, especially when many indexes are used or a server
installation sees too many connections (network sockets count against that limit as well). Users are
therefore encouraged to increase that limit to a healthy value of 40000 or more, depending on usage
patterns. Setting this value via the ulimit command is possible only for the root user and that for that
session only. To set the value system wide you have to follow the instructions for your platform.
What follows is the procedure to set the open file descriptor limit to 40k for user neo4j under Ubuntu
10.04 and later. If you opted to run the neo4j service as a different user, change the first field in step 2
accordingly.
1. Become root since all operations that follow require editing protected system files.
user@localhost:~$ sudo su Password:
root@localhost:~$
soft
hard
nofile
nofile
40000
40000
required
pam_limits.so
406
Chapter23.High Availability
Note
The High Availability features are only available in the Neo4j Enterprise Edition.
Neo4j High Availability or Neo4j HA provides the following two main features:
1. It enables a fault-tolerant database architecture, where several Neo4j slave databases can be
configured to be exact replicas of a single Neo4j master database. This allows the end-user system
to be fully functional and both read and write to the database in the event of hardware failure.
2. It enables a horizontally scaling read-mostly architecture that enables the system to handle more
read load than a single Neo4j database instance can handle.
407
High Availability
23.1.Architecture
Neo4j HA has been designed to make the transition from single machine to multi machine operation
simple, by not having to change the already existing application.
Consider an existing application with Neo4j embedded and running on a single machine. To deploy
such an application in a multi machine setup the only required change is to switch the creation of the
GraphDatabaseService from GraphDatabaseFactory to HighlyAvailableGraphDatabaseFactory. Since both
implement the same interface, no additional changes are required.
Figure23.1.Multiple Neo4j instances in HA mode
When running Neo4j in HA mode there is always a single master and zero or more slaves. Compared
to other master-slave replication setups Neo4j HA can handle write requests on all machines so there
is no need to redirect those to the master specifically.
408
High Availability
A slave will handle writes by synchronizing with the master to preserve consistency. Writes to master
can be configured to be optimistically pushed to 0 or more slaves. By optimistically we mean the
master will try to push to slaves before the transaction completes but if it fails the transaction will still
be successful (different from normal replication factor). All updates will however propagate from the
master to other slaves eventually so a write from one slave may not be immediately visible on all other
slaves. This is the only difference between multiple machines running in HA mode compared to single
machine operation. All other ACID characteristics are the same.
409
High Availability
23.2.2.Server configuration
If you are running Neo4j server, specify org.neo4j.server.database.mode=HA in neo4j-server.properties.
HA server configuration parameters
Parameter Name
Description
Example value
Required?
Whether to run as a
single server or in HA
mode.
single or HA
yes
23.2.3.Database configuration
HA configuration parameters should be supplied alongside general Neo4j parameters in
neo4j.properties. There are many configurable parameters, most in most cases it isnt necessary
to modify the default values. The only parameters that need to be specified are ha.server_id and
ha.initial_hosts.
HA database configuration parameters
Parameter Name
Description
Example value
Required?
ha. server_id
Id for a cluster
instance. Must be
unique within the
cluster.
yes
ha. initial_hosts
yes
ha. cluster_server
no
410
High Availability
Parameter Name
Description
Example value
ha. allow_init_cluster
no
ha. default_timeout
no
ha. heartbeat_interval
no
ha. heartbeat_timeout
no
ha. broadcast_timeout
Timeout for
30s
broadcasting values in
cluster. Must consider
end-to-end duration of
Paxos algorithm. This
value is the default
value for settings
ha.join_timeout and
ha.leave_timeout.
no
ha. join_timeout
no
ha. configuration_timeout
no
ha. leave_timeout
no
411
5s
30s
30s
Required?
High Availability
Parameter Name
Description
Example value
ha. paxos_timeout
Default timeout
5s
for all Paxos
timeouts. Defaults to
ha.default_timeout.
This value is the
default value
for settings
ha.phase1_timeout,
ha.phase2_timeout and
ha.election_timeout.
no
ha. phase1_timeout
5s
no
ha. phase2_timeout
5s
no
ha. learn_timeout
5s
no
ha. election_timeout
5s
no
ha. read_timeout
no
ha. state_switch_timeout
no
ha. lock_read_timeout
no
20s
ha.
Maximum number of 20
max_concurrent_channels_per_slave
connections a slave can
Required?
no
ha. server
ha. slave_only
412
my-domain. com:6001
no
no
High Availability
Parameter Name
Description
Example value
set to true, it will never
be elected as master.
Required?
ha. branched_data_policy
no
ha. com_chunk_size
no
ha. pull_interval
Interval of pulling
updates from master.
10s
no
ha. tx_push_factor
no
ha. tx_push_strategy
Push strategy of a
transaction to a slave
during commit.
fixed or round_robin
no
413
keep_last or keep_all
or keep_none
High Availability
High Availability
Reads are highly available and the ability to handle read load scales with more database instances in
the cluster.
415
High Availability
23.4.Arbiter Instances
A typical deployment of Neo4j will use a cluster of 3 machines to provide fault-tolerance and read
scalability. This setup is described in Section23.6, High Availability setup tutorial.
While having at least 3 instances is necessary for failover to happen in case the master becomes
unavailable, it is not required for all instances to run the full Neo4j stack, which includes the database
engine. Instead, what is called arbiter instances can be deployed. They can be regarded as cluster
participants in that their role is to take part in master elections with the single purpose of breaking ties
in the election process. That makes possible a scenario where you have a cluster of 2 Neo4j database
instances and an additional arbiter instance and still enjoy tolerance of a single failure of either of the
3 instances.
Arbiter instances are configured in the same way as Neo4j HA members are through the
neo4j.properties file in the installations conf/ directory. Settings that are not cluster specific are of
course ignored, so you can easily start up an arbiter instance in place of a properly configured Neo4j
instance.
To start an arbiter instance, call
neo4j_home$ ./bin/neo4j-arbiter start
You can also stop, install and remove it as a service and ask for its status in exactly the same way as
for Neo4j instances. See also Section21.2, Server Installation.
416
High Availability
High Availability
to make sure that all but one instance have the ha.allow_init_cluster setting to false - the machine
that has it set to true should be the one that is to become the new master. In addition, it is necessary
that the last machine to be upgraded (the 1.8 master) does not have the ha.coordinators setting present
in its configuration file. Finally, dont forget to copy over any server plugins you may have. First,
shutdown the neo4j instance with
service neo4j-service stop
Next, uninstall it
service neo4j-service remove
Now you can copy over the database. Assuming the old instance is at /opt/old-neo4j and the newly
unpacked under /opt/neo4j-enterprise-1.9 the proper command would be
cp -R /opt/old-neo4j/data/graph.db /opt/neo4j-enterprise-1.9/data/
Done. Now check that the services are running and that webadmin reports the version 1.9.
Transactions should also be applied from the master as usual.
Step 2: Upgrade the master, complete the procedure
Warning
Make sure that the installation that will replace the current master instance does not have
ha.coordinators setting present in the neo4j.properties file.
Go to the current master and execute step 1 The moment it will be stopped another instance will take
over (the one with the allow_init_cluster setting set to true), transitioning the cluster to 1.9. Finish
Step 1 on this machine as well and you will have completed the process.
Step 3: Cleanup, removing the coordinator services
Each 1.8 installation still has a coordinator service installed and running. To have those removed you
need to execute at every upgraded instance
service neo4j-coordinator stop
service neo4j-coordinator remove
After that, the 1.8 instances are no longer active or needed and can be removed or archived.
Due to an bug in the HA code, it may not be possible to do a rolling (i.e. uninterrupted)
upgrade to Neo4j 1.9.2 on 1.9 or 1.9.1 clusters. Attempting to do so may lead to an
unstable cluster and data loss may occur. It is suggested that upgrades to 1.9.2 happen
offline, where all instances are shutdown, upgraded and restarted. Upgrading from 1.9.2 to
any other version works as described elsewhere in this guide.
Upgrading between 1.9.x versions follows the same general pattern as described in the first part of this
guide, but is much simpler because of the compatibility of the configuration options between 1.9.x
releases. We will describe a step by step procedure, aimed at reducing the master switches to a single
change.
418
High Availability
Step 1: On each slave perform the upgrade
Download and unpack the new version. Copy over any configuration settings, ensuring that newly
introduced settings have proper values (see Section23.2, Setup and configuration). Dont forget to
copy over any server plugins you may have.
First, shutdown the neo4j instance with
service neo4j-service stop
Next, uninstall it
service neo4j-service remove
Now you can copy over the database. Assuming the old instance is at /opt/old-neo4j and the newly
unpacked under /opt/neo4j-enterprise-1.9.x the proper command would be
cp -R /opt/old-neo4j/data/graph.db /opt/neo4j-enterprise-1.9.x/data/
Now check that the services are running and that webadmin reports the version 1.9.x. Transactions
should also be applied from the master as usual.
Step 2: Upgrade the master, complete the procedure
Go to the current master and execute step 1 The moment it will be stopped another instance will take
over, transitioning the cluster to the new 1.9.x version. Finish Step 1 on this machine as well and you
will have completed the process.
419
High Availability
This guide will help you understand how to configure and deploy a Neo4j High Availability cluster.
Two scenarios will be considered:
Configuring 3 instances to be deployed on 3 separate machines, in a setting similar to what might
be encountered in a production environment.
Modifying the former to make it possible to run a cluster of 3 instances on the same physical
machine, which is particularly useful during development.
23.6.1.Background
Each instance in a Neo4j HA cluster must be assigned an integer ID, which serves as its unique
identifier. At startup, a Neo4j instance contacts the other instances specified in the ha.initial_hosts
configuration option.
When an instance establishes a connection to any other, it determines the current state of the cluster
and ensures that it is eligible to join. To be eligible the Neo4j instance must host the same database
store as other members of the cluster (although it is allowed to be in an older state), or be a new
deployment without a database store.
Neo4j will attempt to configure IP addresses for itself in the absence of explicit
configuration. However in typical operational environments where machines have multiple
network cards and support IPv4 and IPv6 it is strongly recommended that the operator
explicitly sets the IP address/hostname configuration for each machine in the cluster.
Lets examine the available settings and the values they accept.
ha.server_id
is the cluster identifier for each instance. It must be a positive integer and must be unique
among all Neo4j instances in the cluster.
ha.server_id
Warning
High Availability
For example, ha.initial_hosts=192.168.33.22:5001,192.168.33.21:5001 will attempt to reach
Neo4j instances listening on 192.168.33.22 on port 5001 and 192.168.33.21 on port 5001 on the
192.168.33.0 subnet.
ha.server
is an address/port setting that specifies where the Neo4j instance will listen for transactions
(changes to the graph data) from the cluster master. The default port is 6001. In the absence of a
specified IP address, Neo4j will attempt to find a valid interface for binding. While this behavior
typically results in a well-behaved server, it is strongly recommended that users explicitly choose an
IP address bound to the network interface of their choosing to ensure a coherent cluster topology.
ha.server
ha.server
For example, ha.server=192.168.33.22:6001 will listen for cluster communications on the network
interface bound to the 192.168.33.0 subnet on port 6001.
Address/port format
421
High Availability
conf/neo4j-server.properties
Let the webserver only listen on the specified IP.
org.neo4j.server.webserver.address=0.0.0.0
HA - High Availability
SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA
conf/neo4j-server.properties
Let the webserver only listen on the specified IP.
org.neo4j.server.webserver.address=0.0.0.0
HA - High Availability
SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA
conf/neo4j-server.properties
Let the webserver only listen on the specified IP.
org.neo4j.server.webserver.address=0.0.0.0
HA - High Availability
SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA
Startup Time
When running in HA mode, the startup script returns immediately instead of waiting for
the server to become available. This is because the instance does not accept any requests
422
High Availability
until a cluster has been formed. In the example above this happens when you startup
the second instance. To keep track of the startup state you can follow the messages in
console.log - the path to that is printed before the startup script returns.
Now, you should be able to access the 3 servers and check their HA status:
http://neo4j-01.local:7474/webadmin/#/info/org.neo4j/High%20Availability/
http://neo4j-02.local:7474/webadmin/#/info/org.neo4j/High%20Availability/
http://neo4j-03.local:7474/webadmin/#/info/org.neo4j/High%20Availability/
Tip
You can replace database #3 with an arbiter instance, see Arbiter Instances.
Thats it! You now have a Neo4j HA cluster of 3 instances running. You can start by making a
change on any instance and those changes will be propagated between them. For more HA related
configuration options take a look at HA Configuration.
conf/neo4j-server.properties
database location
org.neo4j.server.database.location=data/graph.db
http port (for all data, administrative, and UI access)
org.neo4j.server.webserver.port=7474
423
High Availability
conf/neo4j-server.properties
database location
org.neo4j.server.database.location=data/graph.db
http port (for all data, administrative, and UI access)
org.neo4j.server.webserver.port=7475
https port (for all data, administrative, and UI access)
org.neo4j.server.webserver.https.port=7485
HA - High Availability
SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA
conf/neo4j-server.properties
database location
424
High Availability
org.neo4j.server.database.location=data/graph.db
http port (for all data, administrative, and UI access)
org.neo4j.server.webserver.port=7476
https port (for all data, administrative, and UI access)
org.neo4j.server.webserver.https.port=7486
HA - High Availability
SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA
Now, you should be able to access the 3 servers and check their HA status:
http://127.0.0.1:7474/webadmin/#/info/org.neo4j/High%20Availability/
http://127.0.0.1:7475/webadmin/#/info/org.neo4j/High%20Availability/
http://127.0.0.1:7476/webadmin/#/info/org.neo4j/High%20Availability/
425
High Availability
23.7.2.The endpoints
Each HA instance comes with 2 endpoints regarding its HA status. They are complimentary but each
may be used depending on your load balancing needs and your production setup. Those are:
/db/manage/server/ha/master
/db/manage/server/ha/slave
To use them, perform an HTTP GET operation on either and the following will be returned:
HA REST endpoint responses
Endpoint
/db/manage/server/ha/master
/db/manage/server/ha/slave
Instance
State
Returned Code
Body text
Master
200 OK
true
Slave
false
Unknown
UNKNOWN
Master
false
Slave
200 OK
true
Unknown
UNKNOWN
23.7.3.Examples
From the command line, a common way to ask those endpoints is to use curl. With no arguments, curl
will do an HTTP GET on the URI provided and will output the body text, if any. If you also want to get
the response code, just add the -v flag for verbose output. Here are some examples:
Requesting master endpoint on a running master with verbose output
> curl -v localhost:7474/db/manage/server/ha/master
* About to connect() to localhost port 7474 (0)
*
Trying ::1...
* connected
* Connected to localhost (::1) port 7474 (0)
> GET /db/manage/server/ha/master HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:7474
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Access-Control-Allow-Origin: *
< Transfer-Encoding: chunked
426
High Availability
< Server: Jetty(6.1.25)
<
* Connection 0 to host localhost left intact
true* Closing connection 0
Unknown status
The UNKNOWN status exists to describe when a Neo4j instance is neither master nor slave. For
example, the instance could be transitioning between states (master to slave in a recovery
scenario or slave being promoted to master in the event of failure). If the UNKNOWN status is
returned, the client should not treat the instance as a master or a slave and should instead
pick another instance in the cluster to use, wait for the instance to transit from the UNKNOWN
state, or undertake restorative action via systems admin.
427
High Availability
23.8.1.Configuring HAProxy
HAProxy can be configured in many ways. The full documentation is available at their website.
For this example, we will configure HAProxy to load balance requests to three HA servers. Simply
write the follow configuration to /etc/haproxy.cfg:
global
daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend neo4j
backend neo4j
server s1 10.0.1.10:7474 maxconn 32
server s2 10.0.1.11:7474 maxconn 32
server s3 10.0.1.12:7474 maxconn 32
listen admin
bind *:8080
stats enable
428
High Availability
daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend neo4j-slaves
backend neo4j-slaves
option httpchk GET /db/manage/server/ha/slave
server s1 10.0.1.10:7474 maxconn 32 check
server s2 10.0.1.11:7474 maxconn 32 check
server s3 10.0.1.12:7474 maxconn 32 check
listen admin
bind *:8080
stats enable
Note
In practice, writing to a slave is uncommon. While writing to slaves has the benefit of
ensuring that data is persisted in two places (the slave and the master), it comes at a
cost. The cost is that the slave must immediately become consistent with the master by
applying any missing transactions and then synchronously apply the new transaction with
the master. This is a more expensive operation than writing to the master and having the
master push changes to one or more slaves.
429
High Availability
stats enable
Naturally the health check and query parameter-based routing can be combined to only route requests
to slaves by user ID. Other load balancing algorithms are also available, such as routing by source IP
(source), the URI (uri) or HTTP headers(hdr()).
430
Chapter24.Backup
Note
The Backup features are only available in the Neo4j Enterprise Edition.
431
Backup
24.1.Introducing Backup
Backups are performed over the network live from a running graph database onto a local copy. There
are two types of backup: full and incremental.
A full backup copies the database files without acquiring any locks, allowing for continued operations
on the target instance. This of course means that while copying, transactions will continue and the
store will change. For this reason, the transaction that was running when the backup operation started
is noted and, when the copy operation completes, all transactions from the latter down to the one
happening at the end of the copy are replayed on the backup files. This ensures that the backed up data
represent a consistent and up-to-date snapshot of the database storage.
In contrast, an incremental backup does not copy store files instead it copies the logs of the
transactions that have taken place since the last full or incremental backup which are then replayed
over an existing backup store. This makes incremental backups far more efficient than doing full
backups every time but they also require that a full backup has taken place before they are executed.
The backup tool will detect whether you are trying to run a full backup or an incremental one by
inspecting the target directory. Regardless of the mode a backup is created with, the resulting files
represent a consistent database snapshot and they can be used to boot up a Neo4j instance.
The database to be backed up is specified using a URI with syntax
<running mode>://<host>[:port]{,<host>[:port]*}
Running mode must be defined and is either single for non-HA or ha for HA clusters. The
<host>[:port] part points to a host running the database, on port port if not the default. The additional
host:port arguments are useful for passing multiple cluster members.
Important
As of version 1.9, backups are enabled by default. That means that the configuration
parameter online_backup_enabled defaults to true and that makes the backup service
available on the default port (6362). To enable the backup service on a different port use
online_backup_server=:9999.
432
Backup
433
Backup
For more information, please see the Javadocs for OnlineBackup <http://components.neo4j.org/neo4jenterprise/2.0.0/apidocs/org/neo4j/backup/OnlineBackup.html>
434
Backup
24.4.High Availability
To perform a backup on an HA cluster you specify one or more members of the target HA cluster.
Note that the addresses you must provide are the cluster server addresses and not the HA server
addresses. That is, use the value of the ha.cluster_server setting in the configuration.
Performing a backup from HA cluster, specifying only one cluster member
./neo4j-backup -from ha://192.168.1.15:5001 -to /mnt/backup/neo4j-backup
Performing a backup from HA cluster, specifying two possible cluster members
./neo4j-backup -from ha://192.168.1.15:5001,192.168.1.16:5002 -to /mnt/backup/neo4j-backup
435
Backup
436
Chapter25.Security
Neo4j in itself does not enforce security on the data level. However, there are different aspects that
should be considered when using Neo4j in different scenarios. See Section25.1, Securing access to
the Neo4j Server for details.
437
Security
If you need to enable access from external hosts, configure the Web server in the conf/neo4jserver.properties by setting the property org.neo4j.server.webserver.address=0.0.0.0 to enable access
from any host.
25.1.3.HTTPS support
The Neo4j server includes built in support for SSL encrypted communication over HTTPS. The
first time the server starts, it automatically generates a self-signed SSL certificate and a private key.
Because the certificate is self signed, it is not safe to rely on for production use, instead, you should
provide your own key and certificate for the server to use.
To provide your own key and certificate, replace the generated key and certificate, or change the
neo4j-server.properties file to set the location of your certificate and key:
Certificate location (auto generated if the file does not exist)
org.neo4j.server.webserver.https.cert.location=ssl/snakeoil.cert
Private key location (auto generated if the file does not exist)
org.neo4j.server.webserver.https.key.location=ssl/snakeoil.key
Note that the key should be unencrypted. Make sure you set correct permissions on the private key, so
that only the Neo4j server user can read/write it.
Neo4j also supports chained SSL certificates. This requires to have all certificates in PEM format
combined in one file and the private key needs to be in DER format.
You can set what port the HTTPS connector should bind to in the same configuration file, as well as
turn HTTPS off:
Turn https-support on/off
org.neo4j.server.webserver.https.enabled=true
https port (for all data, administrative, and UI access)
438
Security
org.neo4j.server.webserver.https.port=443
components.neo4j.org/neo4j-server/2.0.0/apidocs/org/neo4j/server/rest/security/SecurityRule.html>
can be implemented and registered with the server. This makes scenarios like user and role based
security and authentication against external lookup services possible.
Enforcing Server Authorization Rules
In this example, a (dummy) failing security rule is registered to deny access to all URIs to the server
by listing the rules class in neo4j-server.properties:
org.neo4j.server.rest.security_rules=my.rules.PermanentlyFailingSecurityRule
With this rule registered, any access to the server will be denied. In a production-quality
implementation the rule will likely lookup credentials/claims in a 3rd-party directory service (e.g.
LDAP) or in a local database of authorized users.
Example request
POST http://localhost:7474/db/data/node
Accept: application/json; charset=UTF-8
Example response
401: Unauthorized
WWW-Authenticate: Basic realm="WallyWorld"
439
Security
Using Wildcards to Target Security Rules
In this example, a security rule is registered to deny access to all URIs to the server by listing the
rule(s) class(es) in neo4j-server.properties. In this case, the rule is registered using a wildcard URI
path (where * characters can be used to signify any part of the path). For example /users* means the
rule will be bound to any resources under the /users root path. Similarly /users*type* will bind the
rule to resources matching URIs like /users/fred/type/premium.
org.neo4j.server.rest.security_rules=my.rules.PermanentlyFailingSecurityRuleWithWildcardPath
With this rule registered, any access to URIs under /protected/ will be denied by the server. Using
wildcards allows flexible targeting of security rules to arbitrary parts of the servers API, including
any unmanaged extensions or managed plugins that have been registered.
Example request
GET http://localhost:7474/protected/tree/starts/here/dummy/more/stuff
Accept: application/json
Example response
401: Unauthorized
WWW-Authenticate: Basic realm="WallyWorld"
Using Complex Wildcards to Target Security Rules
In this example, a security rule is registered to deny access to all URIs matching a complex pattern.
The config looks like this:
org.neo4j.server.rest.security_rules=my.rules.PermanentlyFailingSecurityRuleWithComplexWildcardPath
440
Security
}
}
Example request
GET http://localhost:7474/protected/wildcard_replacement/x/y/z/something/else/
more_wildcard_replacement/a/b/c/final/bit/more/stuff
Accept: application/json
Example response
401: Unauthorized
WWW-Authenticate: Basic realm="WallyWorld"
25.1.5.Hosted Scripting
Important
The neo4j server exposes remote scripting functionality by default that allow full access to
the underlying system. Exposing your server without implementing a security layer poses a
substantial security vulnerability.
25.1.6.Security in Depth
Although the Neo4j server has a number of security features built-in (see the above chapters), for
sensitive deployments it is often sensible to front against the outside world it with a proxy like Apache
1
mod_proxy .
This provides a number of advantages:
Control access to the Neo4j server to specific IP addresses, URL patterns and IP ranges. This can be
used to make for instance only the /db/data namespace accessible to non-local clients, while the /
db/admin URLs only respond to a specific IP address.
<Proxy *>
Order Deny,Allow
Deny from all
Allow from 192.168.0
</Proxy>
While equivalent functionality can be implemented with Neo4js SecurityRule plugins (see above), for
operations professionals configuring servers like Apache is often preferable to developing plugins.
However it should be noted that where both approaches are used, they will work harmoniously
providing the behavior is consistent across proxy server and SecurityRule plugins.
Run Neo4j Server as a non-root user on a Linux/Unix system on a port < 1000 (e.g. port 80) using
ProxyPass /neo4jdb/data http://localhost:7474/db/data
ProxyPassReverse /neo4jdb/data http://localhost:7474/db/data
Simple load balancing in a clustered environment to load-balance read load using the Apache
2
mod_proxy_balancer plugin
<Proxy balancer://mycluster>
1
http://httpd.apache.org/docs/2.2/mod/mod_proxy.html
http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html
441
Security
BalancerMember http://192.168.1.50:80
BalancerMember http://192.168.1.51:80
</Proxy>
ProxyPass /test balancer://mycluster
442
Chapter26.Monitoring
Note
Most of the monitoring features are only available in the Enterprise edition of Neo4j.
In order to be able to continuously get an overview of the health of a Neo4j database, there are
different levels of monitoring facilities available. Most of these are exposed through JMX <http://
www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html>.
443
Monitoring
Warning
For maximum security, please adjust at least the password settings in conf/jmx.password
for a production installation.
For more details, see: http://docs.oracle.com/javase/7/docs/technotes/guides/management/agent.html.
444
Monitoring
Now, beside the MBeans exposed by the JVM, you will see an org.neo4j section in the MBeans tab.
Under that, you will have access to all the monitoring information exposed by Neo4j.
For opening JMX to remote monitoring access, please see Section26.1, Adjusting remote JMX
access to the Neo4j Server and the JMX documention <http://docs.oracle.com/javase/7/docs/
technotes/guides/management/agent.html>. When using Neo4j in embedded mode, make sure to pass
the com.sun.management.jmxremote.port=portNum or other configuration as JVM parameters to your
running Java process.
445
Monitoring
Figure26.2.Neo4j MBeans View
446
Monitoring
447
Monitoring
Branched Store: Information about the branched stores present in this HA cluster member
Cache/NodeCache: Information about the caching in Neo4j
Cache/RelationshipCache: Information about the caching in Neo4j
Configuration: The configuration parameters used to configure Neo4j
Diagnostics: Diagnostics provided by Neo4j
High Availability: Information about an instance participating in a HA cluster
Kernel: Information about the Neo4j kernel
Locking: Information about the Neo4j lock status
Memory Mapping: The status of Neo4j memory mapping
Primitive count: Estimates of the numbers of different kinds of Neo4j primitives
Store file sizes: Information about the sizes of the different parts of the Neo4j graph store
Transactions: Information about the Neo4j transaction manager
XA Resources: Information about the XA transaction manager
Note
For additional information on the primitive datatypes (int, long etc.) used in the JMX
attributes, please see Property value types in Section3.3, Properties.
MBean Branched Store (org.neo4j.management.BranchedStore) Attributes
Name
Description
Type
Read Write
org. neo4j.
management.
BranchedStoreInfo[]
as CompositeData[]
yes
no
Description
Type
Read Write
long
yes
no
CacheType
String
yes
no
HitCount
long
yes
no
MissCount
long
yes
no
Description
ReturnType
Signature
clear
void
(no parameters)
448
Monitoring
MBean Cache/RelationshipCache (org.neo4j.management.Cache) Attributes
Name
Description
Type
Read Write
long
yes
no
CacheType
String
yes
no
HitCount
long
yes
no
MissCount
long
yes
no
Description
ReturnType
Signature
clear
void
(no parameters)
Description
Type
Read Write
String
yes
no
yes
no
yes
no
backup_ slave
String
yes
no
cache_ type
yes
no
cypher_ parser_
version
yes
no
449
Monitoring
Name
Description
Type
Read Write
dump_ configuration
String
yes
no
ephemeral
Configuration attribute
String
yes
no
forced_ kernel_ id
String
yes
no
yes
no
String
yes
no
yes
no
yes
no
String
yes
no
String
yes
no
String
yes
no
String
yes
no
ha. configuration_
timeout
yes
no
yes
no
450
Monitoring
Name
Description
Type
Read Write
String
yes
no
ha. heartbeat_
interval
yes
no
String
yes
no
String
yes
no
String
yes
no
yes
no
yes
no
yes
no
String
yes
no
String
yes
no
String
yes
no
String
yes
no
yes
no
yes
no
ha. server_ id
yes
no
ha. server
String
yes
no
String
yes
no
String
yes
no
451
Monitoring
Name
Description
Type
yes
no
yes
no
intercept_ committing_
transactions
String
yes
no
intercept_
deserialized_
transactions
String
yes
no
jmx. port
Configuration attribute
String
yes
no
yes
no
String
yes
no
String
yes
no
String
yes
no
String
yes
no
logging. threshold_
for_ rotation
String
yes
no
452
Read Write
Monitoring
Name
Description
Type
Read Write
logical_ log_
rotation_ threshold
String
yes
no
logical_ log
yes
no
String
yes
no
neo_ store
yes
no
neostore. nodestore.
db. mapped_ memory
String
yes
no
neostore.
propertystore. db.
arrays. mapped_ memory
String
yes
no
neostore.
propertystore. db.
index. keys. mapped_
memory
String
yes
no
neostore.
propertystore. db.
index. mapped_ memory
String
yes
no
neostore.
propertystore. db.
mapped_ memory
String
yes
no
neostore.
propertystore. db.
strings. mapped_
memory
String
yes
no
neostore.
relationshipstore.
db. mapped_ memory
String
yes
no
String
yes
no
453
Monitoring
Name
Description
Type
Read Write
String
yes
no
online_ backup_
enabled
String
yes
no
String
yes
no
read_ only
yes
no
rebuild_ idgenerators_
fast
String
yes
no
relationship_ auto_
indexing
String
yes
no
relationship_ grab_
size
String
yes
no
relationship_ keys_
indexable
String
yes
no
remote_ logging_
enabled
String
yes
no
yes
no
String
yes
no
yes
no
store_ dir
String
yes
no
yes
no
454
Monitoring
Name
Description
Type
note that each block carries an overhead
of 8 bytes. This means that if the block
size is 120, the size of the stored records
will be 128 bytes.
Read Write
String
yes
no
String
yes
no
Description
ReturnType
Signature
apply
Apply settings
void
(no parameters)
Description
Type
Read Write
yes
no
Description
ReturnType
Signature
dumpAll
(no parameters)
dumpToLog
void
(no parameters)
dumpToLog
void
extract
String
Description
Type
Read Write
boolean
yes
no
Available
yes
no
InstanceId
yes
no
InstancesInCluster
yes
no
455
org. neo4j.
management.
ClusterMemberInfo[]
as CompositeData[]
Monitoring
Name
Description
Type
Read Write
LastCommittedTxId
long
yes
no
LastUpdateTime
yes
no
Role
yes
no
String
Description
ReturnType
Signature
update
String
(no parameters)
Description
Type
Read Write
yes
no
KernelVersion
String
yes
no
MBeanQuery
yes
no
ReadOnly
boolean
yes
no
StoreCreationDate
yes
no
StoreDirectory
String
yes
no
StoreId
String
yes
no
StoreLogVersion
long
yes
no
Description
Type
Read Write
NumberOf
AvertedDeadlocks
yes
no
yes
no
Monitoring
Name
Description
Type
that Neo4j has detected and averted (by
throwing DeadlockDetectedException).
Read Write
Description
ReturnType
Signature
getContendedLocks
getContendedLocks
java. util.
List<org. neo4j.
kernel. info.
LockInfo> as
CompositeData[]
long
Description
Type
Read Write
MemoryPools
yes
no
CompositeData[]
Description
Type
Read Write
long
yes
no
NumberOf PropertyIds
InUse
long
yes
no
NumberOf
RelationshipIds InUse
yes
no
NumberOf
RelationshipTypeIds
InUse
long
yes
no
Type
Read Write
Description
Information about the sizes of the different parts of the Neo4j graph store
ArrayStoreSize
long
yes
no
LogicalLogSize
long
yes
no
NodeStoreSize
long
yes
no
457
Monitoring
Name
Description
Type
Read Write
PropertyStoreSize
long
yes
no
RelationshipStoreSize
long
yes
no
StringStoreSize
long
yes
no
TotalStoreSize
long
yes
no
Description
Type
Read Write
long
yes
no
NumberOf Committed
Transactions
long
yes
no
NumberOf Opened
Transactions
int
yes
no
NumberOf Open
Transactions
int
yes
no
NumberOf RolledBack
Transactions
long
yes
no
PeakNumberOf
Concurrent
Transactions
yes
no
Description
Type
Read Write
458
org. neo4j.
management.
XaResourceInfo[] as
CompositeData[]
yes
no
PartVI.Tools
The Tools part describes available Neo4j tools and how to use them.
Chapter27.Web Interface
The Neo4j Web Interface is the primary user interface for Neo4j.
The tool is available at http://127.0.0.1:7474/ after you have installed the Neo4j Server.
See the tool itself for more information!
460
Chapter28.Neo4j Shell
Neo4j shell is a command-line shell for running Cypher queries. Theres also commands to get
information about the database. In addition, you can browse the graph, much like how the Unix shell
along with commands like cd, ls and pwd can be used to browse your local file system.
Its a nice tool for development and debugging. This guide will show you how to get it going!
461
Neo4j Shell
When used together with a Neo4j server, simply issue the following at the command line:
./bin/neo4j-shell
For the full list of options, see the reference in the Shell manual page.
To connect to a running Neo4j database, use Section28.1.4, Read-only mode for local databases
and see Section28.1.1, Enabling the shell server for remote databases.
You need to make sure that the shell jar file is on the classpath when you start up your Neo4j instance.
Shell is enabled from the configuration of the Neo4j kernel, see Section22.2, Server Configuration.
Heres some sample configurations:
Enable the remote shell feature
remote_shell_enabled = true
The default port is 1337, but you can specify something else if you like
remote_shell_port = 1337
If you want to be a little protective of your data,
you can also tell the shell to only support read operations
remote_shell_read_only = true
When using the Neo4j server, see Section22.2, Server Configuration for how to add configuration
settings in that case.
There are two ways to start the shell, either by connecting to a remote shell server or by pointing it to
a Neo4j store path.
Alternatively supply -port and -name options depending on how the remote shell server was enabled.
Then youll get the shell prompt like this:
neo4j-sh (0)$
To start the shell by just pointing it to a Neo4j store path you run the shell jar file. Given that the right
neo4j-kernel-<version>.jar and jta jar files are in the same path as your neo4j-shell-<version>.jar file
you run it with:
$ neo4j-shell -path path/to/neo4j-db
28.1.4.Read-only mode
By issuing the -readonly switch when starting the shell with a store path, changes cannot be made to
the database during the session.
$ neo4j-shell -readonly -path path/to/neo4j-db
It is possible to tell the shell to just start, execute a command and then exit. This opens up for uses of
background jobs and also handling of huge output of f.ex. an ls command where you then could pipe
the output to less or another reader of your choice, or even to a file.
462
Neo4j Shell
And even to another neo4j-shell, e.g. for importing a dump of another database or cypher result. When
used with command mode the shell will not output a welcome message. So some examples of usage:
$ neo4j-shell -c "cd -a 24 && set name Mattias"
$ neo4j-shell -c "trav -r KNOWS" | less
463
Neo4j Shell
464
Neo4j Shell
28.3.Enum options
Some options expects a value which is one of the values in an enum, f.ex. direction part of
relationship type filtering where theres INCOMING, OUTGOING and BOTH. All such values can be supplied
in an easier way. Its enough that you write the start of the value and the interpreter will find what you
really meant. F.ex. out, in, i or even INCOMING.
465
Neo4j Shell
28.4.Filters
Some commands makes use of filters for varying purposes. F.ex. -f in ls and in trav. A filter is
supplied as a json <http://www.json.org/> object (w/ or w/o the surrounding {} brackets. Both keys
and values can contain regular expressions for a more flexible matching. An example of a filter could
be .*url.*:http.*neo4j.*,name:Neo4j. The filter option is also accompanied by the options -i and l which stands for ignore case (ignore casing of the characters) and loose matching (its considered
a match even if the filter value just matches a part of the compared value, not necessarily the entire
value). So for a case-insensitive, loose filter you can supply a filter with -f -i -l or -fil for short.
466
Neo4j Shell
28.5.Node titles
To make it easier to navigate your graph the shell can display a title for each node, f.ex. in ls -r. It
will display the relationships as well as the nodes on the other side of the relationships. The title is
displayed together with each node and its best suited property value from a list of property keys.
If youre standing on a node which has two KNOWS relationships to other nodes itd be difficult to know
which friend is which. The title feature addresses this by reading a list of property keys and grabbing
the first existing property value of those keys and displays it as a title for the node. So you may
specify a list (with or without regular expressions), f.ex: name,title.*,caption and the title for each
node will be the property value of the first existing key in that list. The list is defined by the client
(you) using the TITLE_KEYS environment variable (see Section28.6.7, Environment variables) and the
default being .*name.*,.*title.*
467
Neo4j Shell
28.6.1.Comments
Single line comments, which will be ignored, can be made by using the prefix //. Example:
// This is a comment
Neo4j Shell
28.6.7.Environment variables
The shell uses environment variables a-la bash to keep session information, such as the current path
and more. The commands for this mimics the bash commands export and env. For example you can at
anytime issue a export STACKTRACES=true command to set the STACKTRACES environment variable to true.
This will then result in stacktraces being printed if an exception or error should occur. Allowed values
are all parseable JSON strings, so maps {age:10,name:"Mattias"} and arrays [1,2,3] are also supported.
Variables can also be assigned to each other. E.g. a=b will result in a containing the value of b.
This becomes especially interesting as all shell variables are automatically passed to cypher
statements as parameters. That makes it easy to query for certain start nodes or create nodes and
relationships with certain provided properties (as maps).
Values are removed by setting them to null or an empty value. List environment variables using env
Neo4j Shell
contain ["1234", "Mathias", "Mattias"]. Also please write your outputs to the out variable, such as
out.println( "My tracing text" ) so that it will be printed at the shell client instead of the server.
28.6.9.Traverse
You can traverse the graph with the trav command which allows for simple traversing from the
current node. You can supply which relationship types (w/ regex matching) and optionally direction
as well as property filters for matching nodes. In addition to that you can supply a command line
to execute for each match. An example: trav -o depth -r KNOWS:both,HAS_.*:incoming -c "ls $n".
Which means traverse depth first for relationships with type KNOWS disregarding direction and incoming
relationships with type matching HAS_.\* and do a ls <matching node> for each match. The node
filtering is supplied with the -f option, see Section28.4, Filters. See Section28.3, Enum options
for the traversal order option. Even relationship types/directions are supplied using the same format as
filters.
Tip
Note
This command does not list legacy indexes. For working with legacy indexes, please see
Section28.6.12, Legacy Indexing.
List all indexes and constraints:
schema
Neo4j Shell
-p propertyKey only list indexes or constraints for the given property key propertyKey
-v if an index is in the FAILED state, print a verbose error cause if available
Indexes and constraints can be created or removed using Cypher or the Java Core API. They
are updated automatically whenever the graph is changed. See Section3.7, Schema for more
information.
28.6.12.Legacy Indexing
Its possible to query and manipulate legacy indexes via the index command.
Example: index -i persons name (will index the name for the current node or relationship in the
"persons" legacy index).
-g will do exact lookup in the legacy index and display hits. You can supply -c with a command to
be executed for each hit.
-q will ask the legacy index a query and display hits. You can supply -c with a command to be
executed for each hit.
--cd will change current location to the hit from the query. Its just a convenience for using the -c
option.
--ls will do a listing of the contents for each hit. Its just a convenience for using the -c option.
-i will index a key-value pair into a legacy index for the current node/relationship. If no value is
given the property value for that key for the current node is used as value.
-r will remove a key-value pair (if it exists) from a legacy index for the current node/relationship.
Key and value are optional.
-t will set the legacy index type to work with, for example index -t Relationship --delete friends
will delete the friends relationship index.
28.6.13.Transactions
It is useful to be able to test changes, and then being able to commit or rollback said changes.
Transactions can be nested. With a nested transaction, a commit does not write any changes to
disk, except for the top level transaction. A rollback, however works regardless of the level of the
transaction. It will roll back all open transactions.
begin transaction Starts a transaction.
commit Commits a transaction.
rollback Rollbacks all open transactions.
Note
Neo4j Shell
dump START n=node({self}) MATCH p=(n)-[r:KNOWS*](m) RETURN n,r,m; dumps the transitive
friendship graph of the current node.
neo4j-shell -path db1 -c dump START n=node:node_auto_index(name='Mattias\') MATCH p=(n)[r:KNOWS](m) RETURN p; | neo4j-shell -path db2 imports the subgraph of the first database (db1)
into the second (db2)
Example Dump Scripts
create a new node and go to it
neo4j-sh (?)$ mknode --cd --np "{'name':'Neo'}"
create a relationship
neo4j-sh (Neo,0)$ mkrel -c -d i -t LIKES --np "{'app':'foobar'}"
Export the cypher statement results
neo4j-sh (Neo,0)$ dump START n=node({self}) MATCH (n)-[r]-(m) return n,r,m;
begin
create (_0 {`name`:"Neo"})
create (_1 {`app`:"foobar"})
create _1-[:`LIKES`]->_0
;
commit
create an index
neo4j-sh (?)$ create index on :Person(name);
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
1120 ms
472
Neo4j Shell
make an incoming relationship of type LIKES, create the end node with the node properties specified.
neo4j-sh (Jon,0)$ mkrel -c -d i -t LIKES --np "{'app':'foobar'}"
where are we?
neo4j-sh (Jon,0)$ ls
*name =[Jon]
(me)<-[:LIKES]-(1)
473
Neo4j Shell
This example is creating a graph of the characters in the Matrix via the shell and then executing
Cypher queries against it:
Figure28.1.Shell Matrix Example
ROOT
KNOWS
age = 3
KNOWS
nam e = 'Morpheus'
KNOWS
age = 90
KNOWS
nam e = 'Cypher'
KNOWS
CODED_BY
Neo4j is configured for autoindexing, in this case with the following in the Neo4j configuration file:
node_auto_indexing=true
node_keys_indexable=name,age
relationship_auto_indexing=true
relationship_keys_indexable=ROOT,KNOWS,CODED_BY
The following is a sample shell session creating the Matrix graph and querying it.
Create a reference node
neo4j-sh (?)$ mknode --cd
474
Neo4j Shell
create the Thomas Andersson node
neo4j-sh (0)$ mkrel -t ROOT -c -v
Node (1) created
Relationship [:ROOT,0] created
475
Neo4j Shell
new node
neo4j-sh (Morpheus,3)$ mkrel -t KNOWS -c
list relationships on the current node
neo4j-sh (Morpheus,3)$ ls -r
(me)-[:KNOWS]->(Trinity,2)
(me)-[:KNOWS]->(4)
(me)<-[:KNOWS]-(Thomas Andersson,1)
go to Cypher
neo4j-sh (Morpheus,3)$ cd 4
set the name
neo4j-sh (4)$ set name Cypher
create new node from Cypher
neo4j-sh (Cypher,4)$ mkrel -ct KNOWS
list relationships
neo4j-sh (Cypher,4)$ ls -r
(me)-[:KNOWS]->(5)
(me)<-[:KNOWS]-(Morpheus,3)
go there
neo4j-sh (Agent Smith,5)$ cd 6
set the name
neo4j-sh (6)$ set name "The Architect"
go to the first node in the history stack
neo4j-sh (The Architect,6)$ cd
Morpheus' friends, looking up Morpheus by name in the Neo4j autoindex
neo4j-sh (?)$ start morpheus = node:node_auto_index(name='Morpheus') match morpheus-[:KNOWS]-zionist return zionist.name;
+--------------------+
| zionist.name
|
+--------------------+
| "Trinity"
|
| "Cypher"
|
| "Thomas Andersson" |
+--------------------+
3 rows
322 ms
476
Neo4j Shell
| "Trinity"
|
| "Cypher"
|
| "Thomas Andersson" |
+--------------------+
3 rows
289 ms
477
PartVII.Community
The Neo4j project has a strong community around it. Read about how to get help from the community
and how to contribute to it.
Chapter29.Community Support
You can learn a lot about Neo4j on different events. To get information on upcoming Neo4j events,
have a look here:
http://www.neo4j.org/
http://neo4j.meetup.com/
Get help from the Neo4j open source community; here are some starting points.
479
Chapter30.Contributing to Neo4j
The Neo4j project is an Open Source effort to bring fast complex data storage and processing to life.
Every form of help is highly appreciated by the community - and you are not alone, see Section30.6,
Contributors!
One crucial aspect of contributing to the Neo4j project is the Section30.1, Contributor License
Agreement.
In short: make sure to sign the CLA and send in the email, or the Neo4j project wont be able to accept
your contribution.
Note that you can contribute to Neo4j also by contributing documentation or giving feedback on
the current documentation. Basically, at all the places where you can get help, theres also room for
contributions.
If you want to contribute, there are some good areas to start with, especially for getting in contact with
the community, Chapter29, Community Support.
To document your efforts, we highly recommend to read Section30.3, Writing Neo4j
Documentation.
480
Contributing to Neo4j
30.1.2.Common questions
Am I losing the rights to my own code?
No, the Neo4j CLA <http://dist.neo4j.org/neo4j-cla.pdf> only asks you to share your rights, not
relinquish them. Unlike some contribution agreements that require you to transfer copyrights to
another organization, the CLA does not take away your rights to your contributed intellectual
property. When you agree to the CLA, you grant us joint ownership in copyright, and a patent license
for your contributions. You retain all rights, title, and interest in your contributions and may use them
for any purpose you wish. Other than revoking our rights, you can still do whatever you want with
your code.
What can you do with my contribution?
We may exercise all rights that a copyright holder has, as well as the rights you grant in the Neo4j
CLA <http://dist.neo4j.org/neo4j-cla.pdf> to use any patents you have in your contributions. As
the CLA provides for joint copyright ownership, you may exercise the same rights as we in your
contributions.
What are the community benefits of this?
Well, it allows us to sponsor the Neo4j projects and provide an infrastructure for the community,
while making sure that we can include this in software that we ship to our customers without any
nasty surprises. Without this ability, we as a small company would be hard pressed to release all our
code as free software.
Moreover, the CLA lets us protect community members (both developers and users) from hostile
intellectual property litigation should the need arise. This is in line with how other free software
stewards like the Free Software Foundation - FSF <http://www.fsf.org> defend projects (except
with the FSF, theres no shared copyright but instead you completely sign it over to the FSF). The
contributor agreement also includes a free software covenant, or a promise that a contribution will
remain available as free software.
At the end of the day, you still retain all rights to your contribution and we can stand confident that we
can protect the Neo4j community and the Neo Technology customers.
Can we discuss some items in the CLA?
Absolutely! Please give us feedback! But lets keep the legalese off the mailing lists. Please mail your
feedback directly to cla (@t) neotechnology dot cm and well get back to you.
481
Contributing to Neo4j
I still dont like this CLA.
Thats fine. You can still host it anywhere else, of course. Please do! Were only talking here about the
rules for the infrastructure that we provide.
30.1.3.How to sign
When youve read through the CLA, please send a mail to cla (@t) neotechnology dot cm. Include
the following information:
For example:
Hi. My name is John Doe (john@doe.com).
I agree to the terms in the attached Neo4j Contributor License Agreement.
482
Contributing to Neo4j
483
Contributing to Neo4j
Other than writing documentation, you can help out by providing comments - head over to
the online HTML version <http://docs.neo4j.org/chunked/snapshot/> to do that!
For how to build the manual see: readme <https://github.com/neo4j/neo4j/blob/master/manual/
README.asciidoc>
The documents use the asciidoc format, see:
Aciidoc Reference <http://www.methods.co.nz/asciidoc/>
AsciiDoc cheatsheet <http://powerman.name/doc/asciidoc>
The cheatsheet is really useful!
30.3.1.Overall Flow
Each (sub)project has its own documentation, which will produce a docs.jar file. By default this file is
assembled from the contents in src/docs/. Asciidoc documents have the .asciidoc file extension.
The documents can use code snippets which will extract code from the project. The corresponding
code must be deployed to the sources.jar or test-sources.jar file.
By setting up a unit test accordingly, documentation can be written directly in the JavaDoc comment.
The above files are all consumed by the build of the manual (by adding them as dependencies). To get
content included in the manual, it has to be explicitly included by a document in the manual as well.
Note that different ways to add documentation works best for different cases:
For detail level documentation, it works well to write the documentation as part of unit tests (in
the JavaDoc comment). In this case, you typically do not want to link to the source code in the
documentation.
For tutorial level documentation, the result will be best by writing a .asciidoc file containing
the text. Source snippets and output examples can then be included from there. In this case you
typically want to link to the source code, and users should be able to run it without any special
setup.
Contents
dev/
dev/images/
ops/
ops/images/
man/
manpages
Additional subdirectories are used as needed to structure the documents, like dev/tutorial/, ops/
tutorial/ etc.
Each document starts over with headings from level zero (the document title). Each document should
have an id. In some cases sections in the document need to have ids as well, this depends on where
484
Contributing to Neo4j
they fit in the overall structure. To be able to link to content, it has to have an id. Missing ids in
mandatory places will fail the build.
This is how a document should start:
[[unique-id-verbose-is-ok]]
= The Document Title =
To push the headings down to the right level in the output, the leveloffset attribute is used when
including the document inside of another document.
Subsequent headings in a document should use the following syntax:
== Subheading ==
... content here ...
=== Subsubheading ===
content here ...
Asciidoc comes with one more syntax for headings, but in this project its not used.
30.3.4.Writing
Put one sentence on each line. This makes it easy to move content around, and also easy to spot (too)
long sentences.
30.3.5.Gotchas
A chapter cant be empty. (the build will fail on the docbook xml validity check)
Always leave a blank line at the end of documents (or the title of the next document might end up in
the last paragraph of the document)
As {} are used for Asciidoc attributes, everything inside will be treated as an attribute. What you
have to do is to escape the opening brace: \{. If you dont, the braces and the text inside them will
be removed without any warning being issued!
30.3.6.Links
To link to other parts of the manual the id of the target is used. This is how such a reference looks:
<<community-docs-overall-flow>>
Note
Just write "see <<target-id>>" and similar, that should suffice in most cases.
If you need to link to another document with your own link text, this is what to do:
<<target-id, link text that fits in the context>>
Note
Having lots of linked text may work well in a web context but is a pain in print, and we
aim for both!
External links are added like this:
http://neo4j.org/[Link text here]
Contributing to Neo4j
For short links it may be better not to add a link text, just do:
http://neo4j.org/
Note
Its ok to have a dot right after the URL, it wont be part of the link.
30.3.7.Text Formatting
_Italics_ is rendered as Italics and used for emphasis.
*Bold* is rendered as Bold and used sparingly, for strong emphasis only.
+methodName()+ is rendered as methodName() and is used for literals as well (note: the content
between the + signs will be parsed).
`command` is rendered as command (typically used for command-line) (note: the content between the
` signs will not be parsed).
Mono++space++d is rendered as Monospaced and is used for monospaced letters.
'my/path/' is rendered as my/path/ (used for file names and paths).
``Double quoted'' (that is two grave accents to the left and two acute accents to the right) renders as
Double quoted.
`Single quoted' (that is a single grave accent to the left and a single acute accent to the right) renders
as Single quoted.
30.3.8.Admonitions
These are very useful and should be used where appropriate. Choose from the following (write all
caps and no, we cant easily add new ones):
Note
Note.
Tip
Tip.
Important
Important
Caution
Caution
Warning
Warning
A multiline variation:
486
Contributing to Neo4j
[TIP]
Tiptext.
Line 2.
Tip
Tiptext. Line 2.
30.3.9.Images
Important
All images in the entire manual share the same namespace. You know how to handle that.
Images Files
To include an image file, make sure it resides in the images/ directory relative to the document youre
including it from. Then go:
image::neo4j-logo.png[]
Static Graphviz/DOT
We use the Graphviz/DOT language to describe graphs. For documentation see http://graphviz.org/.
This is how to include a simple example graph:
["dot", "community-docs-graphdb-rels.svg"]
---"Start node" -> "End node" [label="relationship"]
----
relat ionship
End node
487
Contributing to Neo4j
"A Relationship" -> "Relationship type" [label="has a"]
"Name" [TEXTNODE]
"Relationship type" -> "Name" [label="uniquely identified by" color="EDGEHIGHLIGHT" fontcolor="EDGEHIGHLIGHT"]
----
has a
St art node
has a
End node
has a
The optional second argument given to the dot filter defines the style to use:
when not defined: Default styling for nodespace examples.
neoviz: Nodespace view generated by Neoviz.
meta: Graphs that dont resemble db contents, but rather concepts.
Caution
Keywords of the DOT language have to be surrounded by double quotes when used for
other purposes. The keywords include node, edge, graph, digraph, subgraph, and strict.
30.3.10.Attributes
Common attributes you can use in documents:
{neo4j-version} - rendered as "2.0.0"
{neo4j-git-tag} - rendered as "2.0.0"
{lucene-version} - rendered as "3_6_2"
These can substitute part of URLs that point to for example APIdocs or source code. Note that neo4jgit-tag also handles the case of snapshot/master.
Sample Asciidoc attributes which can be used:
{docdir} - root directory of the documents
{nbsp} - non-breaking space
30.3.11.Comments
Theres a separate build including comments. The comments show up with a yellow background. This
build doesnt run by default, but after a normal build, you can use make annotated to build it. You can
also use the resulting page to search for content, as the full manual is on a single page.
Heres how to write a comment:
// this is a comment
488
Contributing to Neo4j
The comments are not visible in the normal build. Comment blocks wont be included in the output of
any build at all. Heres a comment block:
////
Note that includes in here will still be processed, but not make it into the output.
That is, missing includes here will still break the build!
////
30.3.12.Code Snippets
Explicitly defined in the document
Warning
Use this kind of code snippets as little as possible. They are well known to get out of sync
with reality after a while.
This is how to do it:
[source,cypher]
---start n=(2, 1) where (n.age < 30 and n.name = "Tobias") or not(n.name = "Tobias")
----
return n
return n
Bash
Cypher
Groovy
Java
JavaScript
Python
XML
Note that the artifact has to be included as a Maven dependency of the Manual project so that the files
can be found.
Be aware of that the tag "abc" will match "abcd" as well. Its a simple on/off switch, meaning that
multiple occurrences will be assembled into a single code snippet in the output. This behavior can be
user to hide away assertions from code examples sourced from tests.
489
Contributing to Neo4j
This is how to define a code snippet inclusion:
[snippet,java]
---component=neo4j-examples
source=org/neo4j/examples/JmxDocTest.java
classifier=test-sources
tag=getStartTime
----
Query Results
Theres a special filter for Cypher query results. This is how to tag a query result:
.Result
[queryresult]
---+----------------------------------+
| friend_of_friend.name | count(*) |
+----------------------------------+
| Ian
| 2
|
| Derrick
| 1
|
| Jill
| 1
|
+----------------------------------+
3 rows, 12 ms
----
count(*)
Ian
Derrick
Jill
3 rows, 12 ms
490
Contributing to Neo4j
This is a sample documentation test, demonstrating different ways of
bringing code and other artifacts into Asciidoc form. The title of the
generated document is determined from the method name, replacing "+_+" with
" ".
Below you see a number of different ways to generate text from source,
inserting it into the JavaDoc documentation (really being Asciidoc markup)
via the +@@+ snippet markers and programmatic adding with runtime data
in the Java code.
- The annotated graph as http://www.graphviz.org/[GraphViz]-generated visualization:
.Hello World Graph
["dot", "Hello-World-Graph-hello-world-Sample-Chapter.svg", "neoviz", ""]
---N0 [
label = "{Node\[0\]name = \'you\'\l}"
]
N1 [
label = "{Node\[1\]name = \'I\'\l}"
]
N1 -> N0 [
color = "2e3436"
fontcolor = "2e3436"
label = "know\n"
]
---- A sample Cypher query:
[source,cypher]
---START n = node(1)
RETURN n
---- A sample text output snippet:
[source]
---Hello graphy world!
---- a generated source link to the original GIThub source for this test:
https://github.com/neo4j/neo4j/blob/{neo4j-git-tag}/community/embedded-examples/src/test/java/org/neo4j/examples/DocumentationDocTest
- The full source for this example as a source snippet, highlighted as Java code:
[snippet,java]
---component=neo4j-examples
source=org/neo4j/examples/DocumentationDocTest.java
classifier=test-sources
tag=sampleDocumentation
---This is the end of this chapter.
Contributing to Neo4j
This is a sample documentation test, demonstrating different ways of bringing code and other artifacts
into Asciidoc form. The title of the generated document is determined from the method name,
replacing "_" with " ".
Below you see a number of different ways to generate text from source, inserting it into the JavaDoc
documentation (really being Asciidoc markup) via the @@ snippet markers and programmatic adding
with runtime data in the Java code.
The annotated graph as GraphViz <http://www.graphviz.org/>-generated visualization:
Figure30.1.Hello World Graph
Node[ 1] nam e = 'I'
know
a generated source link to the original GIThub source for this test:
DocumentationDocTest.java <https://github.com/neo4j/neo4j/blob/2.0.0/community/embeddedexamples/src/test/java/org/neo4j/examples/DocumentationDocTest.java>
The full source for this example as a source snippet, highlighted as Java code:
// START SNIPPET: _sampleDocumentation
package org.neo4j.examples;
import org.junit.Test;
import org.neo4j.kernel.impl.annotations.Documented;
import org.neo4j.test.GraphDescription.Graph;
import static org.neo4j.visualization.asciidoc.AsciidocHelper.createGraphVizWithNodeId;
import static org.neo4j.visualization.asciidoc.AsciidocHelper.createOutputSnippet;
public class DocumentationDocTest extends ImpermanentGraphJavaDocTestBase
{
/**
* This is a sample documentation test, demonstrating different ways of
* bringing code and other artifacts into Asciidoc form. The title of the
* generated document is determined from the method name, replacing "+_+" with
* " ".
*
* Below you see a number of different ways to generate text from source,
492
Contributing to Neo4j
* inserting it into the JavaDoc documentation (really being Asciidoc markup)
* via the +@@+ snippet markers and programmatic adding with runtime data
* in the Java code.
*
* - The annotated graph as http://www.graphviz.org/[GraphViz]-generated visualization:
*
* @@graph
*
* - A sample Cypher query:
*
* @@cypher
*
* - A sample text output snippet:
*
* @@output
*
* - a generated source link to the original GIThub source for this test:
*
* @@github
*
* - The full source for this example as a source snippet, highlighted as Java code:
*
* @@sampleDocumentation
*
* This is the end of this chapter.
*/
@Test
// signaling this to be a documentation test
@Documented
// the graph data setup as simple statements
@Graph( "I know you" )
// title is determined from the method name
public void hello_world_Sample_Chapter()
{
// initialize the graph with the annotation data
data.get();
gen.get().addTestSourceSnippets( this.getClass(), "sampleDocumentation" );
gen.get()
.addGithubTestSourceLink( "github", this.getClass(),
"community/embedded-examples" );
gen.get().addSnippet( "output",
createOutputSnippet( "Hello graphy world!" ) );
gen.get().addSnippet(
"graph",
createGraphVizWithNodeId( "Hello World Graph", graphdb(),
gen.get().getTitle() ) );
// A cypher snippet referring to the generated graph in the start clause
gen.get().addSnippet(
"cypher",
createCypherSnippet( "start n = node(" + data.get().get( "I" ).getId()
+ ") return n" ) );
}
}
// END SNIPPET: _sampleDocumentation
Contributing to Neo4j
.Interactive Example
[console]
---CREATE (n {name: 'Neo4j'})
MATCH (n)
return n
----
30.3.16.Toolchain
Useful links when configuring the docbook toolchain:
http://www.docbook.org/tdg/en/html/docbook.html
http://www.sagehill.net/docbookxsl/index.html
http://docbook.sourceforge.net/release/xsl/1.76.1/doc/html/index.html
http://docbook.sourceforge.net/release/xsl/1.76.1/doc/fo/index.html
494
Contributing to Neo4j
30.4.1.Prerequisites
Apache Maven
GNU Make
Python
Perl
Perl module: Unicode::GCString
To check if you have the Unicode::GCString module installed, you can issue the following command:
perl -MUnicode::GCString -e ''
If theres no error, the module has been successfully installed on your system.
To install the module, you can use cpanminus <http://search.cpan.org/dist/App-cpanminus/lib/App/
cpanminus.pm>. For a convenient way to install it, see http://cpanmin.us. With cpanminus installed,
execute this command:
cpanm Unicode::GCString
You will probably want to use a .po file editor as well, see Section30.4.5, Translation tools.
Contributing to Neo4j
Files produced by later steps will overwrite existing files from earlier steps.
If the translation file does not already exist, it will be created. The document will be added to the
translation build configuration file as well. (The configuration is in the corresponding .conf file in the
po/ directory.)
If there exists a translated copy of the document at the location the DOCUMENT parameter points to, the
script will attempt to populate the translation file with translated paragraphs from that document.
Note that the structure of the document has to be a perfect match, or it will fail. However, the error
messages are helpful, so just fix and try again until it works! Translation file and configuration are
only changed when the first part succeeds.
Note
Only documents that need to be translated should be added. For example Cypher queries
and query results should not be translated. In general, documents residing in a directory
named includes should not be translated.
Also note that AsciiDoc include:: lines are normally not part of the translation at all, but handled
automatically. In case they need to be handled differently in a document, this has to be configured in
the corresponding .conf file. For example a normal document entry in such a file can look like this:
[type: asciidoc] target/original/src/operations/index.asciidoc fr:target/src/operations/index.asciidoc
To configure a single document not to handle include:: lines automatically, add the following at the
end of the line:
opt: "-o definitions=target/tools/main/resources/conf/translate-includes"
30.4.4.Workflow
First, use Maven to set up the environment and download the original manual and documentation
tools:
mvn clean package
To refresh the original manual and the tools, use the maven command again. For the sake of keeping
in sync with the original manual, a daily run of this command is recommended.
Once things are set up, use make during work.
Contributing to Neo4j
The actual work on translation is done by editing translation files. Suggested tools for that are found
below.
30.4.5.Translation tools
There are different editors for .po files containing the translations Below is a list of editors.
Gtranslator <http://projects.gnome.org/gtranslator/>
Lokalize <http://userbase.kde.org/Lokalize>
Virtaal <http://translate.sourceforge.net/wiki/virtaal/index>
Poedit <http://www.poedit.net/>
497
Contributing to Neo4j
30.5.2.Governance fundamentals
In a nutshell, you need to be aware of the following fundamentals if you wish to contribute code:
All software published by the Neo4j project must have been contributed under the Neo4j Code
Contributor License Agreement.
Neo4j is a free software and open source community. As a contributor, you are free to place your
work under any license that has been approved by either the Free Software Foundation <http://
www.fsf.org/> or the Open Source Initiative <http://opensource.org>. You still retain copyright, so
in addition to that license you can of course release your work under any other license (for example
a fully proprietary license), just not on the Neo4j infrastructure.
The Neo4j software is split into components. A Git repository holds either a single or multiple
components.
The source code should follow the Neo4j Code Style and fit in with the Neo4j infrastructure as
much as is reasonable for the specific component.
30.5.3.Contributor roles
Every individual that contributes code does so in the context of a role (a single individual can have
multiple roles). The role defines their responsibilities and privileges:
A patch submitter is a person who wishes to contribute a patch to an existing component. See
Workflow below.
A committer can contribute code directly to one or more components.
A component maintainer is in charge of a specific component. They can:
commit code in their components repository,
manage tickets for the repository,
grant push rights to the repository.
A Neo4j admin manages the Neo4j infrastructure. They:
define new components and assign component maintainership,
drive, mentor and coach Neo4j component development.
30.5.4.Contribution workflow
Code contributions to Neo4j are normally done via Github Pull Requests, following the workflow
shown below. Please check the pull request checklist before sending off a pull request as well.
1. Fork the appropriate Github repository.
498
Contributing to Neo4j
2.
3.
4.
5.
6.
7.
30.5.6.Unit Tests
You have a much higher chance of getting your changes accepted if you supply us with small,
readable unit tests covering the code youve written. Also, make sure your code doesnt break
any existing tests. Note that there may be downstream components that need to be tested as well,
depending on what you change.
To run tests, use Maven rather than your IDE to ensure others can replicate your test run. The
command for running Neo4j tests in any given component is mvn clean validate.
30.5.7.Code Style
The Neo4j Code style is maintained on GitHub in styles for the different IDEs <https://github.com/
neo4j/neo4j.github.com/tree/master/code-style>.
30.5.8.Commit messages
Please take some care in providing good commit messages. Use your common sense. In particular:
Use english. This includes proper punctuation and correct spelling. Commit messages are supposed
to convey some information at a glance theyre not a chat room.
Remember that a commit is a changeset, which describes a cohesive set of changes across
potentially many files. Try to group every commit as a logical change. Explain what it changes. If
you have to redo work, you might want to clean up your commit log before doing a pull request.
If you fix a bug or an issue thats related to a ticket, then refer to the ticket in the message. For
example, `Added this and then changed that. This fixes #14.' Just mentioning #xxx in the commit
will connect it to the GitHub issue with that number, see GitHub issues <https://github.com/
blog/831-issues-2-0-the-next-generation>. Any of these synonyms will also work:
fixes #xxx
fixed #xxx
fix #xxx
closes #xxx
close #xxx
closed #xxx.
499
Contributing to Neo4j
Remember to convey intent. Dont be too brief but dont provide too much detail, either. Thats
what git diff is for.
30.5.11.Single commit
If you have multiple commits, you should squash them into a single one for the pull request, unless
there is some extraordinary reason not to. Keeping your changes in a single commit makes the commit
history easier to read, it also makes it easy to revert and move features around.
One way to do this is to, while standing on your local branch with your changes, create a new branch
and then interactively rebase your commits into a single one.
Interactive rebasing with Git.
On branch mychanges
git checkout -b mychanges-clean
Assuming you have 4 commits, rebase the last four commits interactively:
git rebase -i HEAD~4
In the dialog git gives you, keep your first commit, and squash all others into it.
Then reword the commit description to accurately depict what your commit does.
If applicable, include any issue numbers like so: 760
500
Contributing to Neo4j
30.6.Contributors
As an Open Source Project, the Neo4j User community extends its warmest thanks to all the
contributors who have signed the Section30.1, Contributor License Agreement to date and are
contributing to this collective effort.
name
GIThub ID
Johan Svensson
johan-neo <https://github.com/johan-neo>
Emil Eifrem
emileifrem <https://github.com/emileifrem>
Peter Neubauer
peterneubauer <https://github.com/peterneubauer>
Mattias Persson
tinwelint <https://github.com/tinwelint>
Tobias Lindaaker
thobe <https://github.com/thobe>
Anders Nawroth
nawroth <https://github.com/nawroth>
Andrs Taylor
systay <https://github.com/systay>
Jacob Hansson
jakewins <https://github.com/jakewins>
Jim Webber
jimwebber <https://github.com/jimwebber>
Josh Adell
jadell <https://github.com/jadell>
Andreas Kollegger
akollegger <https://github.com/akollegger>
Chris Gioran
digitalstain <https://github.com/digitalstain>
Thomas Baum
tbaum <https://github.com/tbaum>
Alistair Jones
apcj <https://github.com/apcj>
Michael Hunger
jexp <https://github.com/jexp>
Jesper Nilsson
jespernilsson <https://github.com/jespernilsson>
Tom Sulston
tomsulston <https://github.com/tomsulston>
David Montag
dmontag <https://github.com/dmontag>
Marlon Richert
marlonrichert <https://github.com/marlonrichert>
Hugo Josefson
hugojosefson <https://github.com/hugojosefson>
Vivek Prahlad
vivekprahlad <https://github.com/vivekprahlad>
Adriano Almeida
adrianoalmeida7 <https://github.com/
adrianoalmeida7>
Benjamin Gehrels
BGehrels <https://github.com/BGehrels>
Christopher Schmidt
FaKod <https://github.com/FaKod>
Pascal Rehfeldt
prehfeldt <https://github.com/prehfeldt>
Bjrn Sderqvist
cybear <https://github.com/cybear>
abdulazeezsk <https://github.com/abdulazeezsk>
James Thornton
espeed <https://github.com/espeed>
Radhakrishna Kalyan
nrkkalyan <https://github.com/nrkkalyan>
promontis <https://github.com/promontis>
Brandon McCauslin
bm3780 <https://github.com/bm3780>
501
Contributing to Neo4j
name
GIThub ID
Hendy Irawan
ceefour <https://github.com/ceefour>
Luanne Misquitta
luanne <https://github.com/luanne>
Jim Radford
radford <https://github.com/radford>
Axel Morgner
amorgner <https://github.com/amorgner>
Taylor Buley
editor <https://github.com/editor>
Alex Smirnov
alexsmirnov <https://github.com/alexsmirnov>
Johannes Mockenhaupt
jotomo <https://github.com/jotomo>
pablopareja <https://github.com/pablopareja>
Bjrn Granvik
bjorngranvik <https://github.com/bjorngranvik>
Julian Simpson
simpsonjulian <https://github.com/simpsonjulian>
pablopareja <https://github.com/pablopareja>
Rickard berg
rickardoberg <https://github.com/rickardoberg>
Stefan Armbruster
sarmbruster <https://github.com/sarmbruster>
Stephan Hagemann
shageman <https://github.com/shageman>
Linan Wang
wangii <https://github.com/wangii>
Ian Robinson
iansrobinson <https://github.com/iansrobinson>
Marko Rodriguez
okram <https://github.com/okram>
Saikat Kanjilal
skanjila <https://github.com/skanjila>
Craig Taverner
craigtaverner <https://github.com/craigtaverner>
David Winslow
dwins <https://github.com/dwins>
Patrick Fitzgerald
paddydub <https://github.com/paddydub>
Stefan Berder
hrbonz <https://github.com/hrbonz>
Michael Kanner
SepiaGroup <https://github.com/SepiaGroup>
Lin Zhemin
miaoski <https://github.com/miaoski>
Christophe Willemsen
kwattro <https://github.com/kwattro>
Tony Liu
kooyeed <https://github.com/kooyeed>
Michael Klishin
michaelklishin <https://github.com/michaelklishin>
Wes Freeman
wfreeman <https://github.com/wfreeman>
Chris Leishman
cleishm <https://github.com/cleishm>
Brian Levine
blevine <https://github.com/blevine>
Ben Day
benday280412 <https://github.com/benday280412>
Davide Savazzi
svzdvd <https://github.com/svzdvd>
Nigel Small
nigelsmall <https://github.com/nigelsmall>
Lasse Westh-Nielsen
lassewesth <https://github.com/lassewesth>
Wujek Srujek
wujek-srujek <https://github.com/wujek-srujek>
502
Contributing to Neo4j
name
GIThub ID
Alexander Yastrebov
AlexanderYastrebov <https://github.com/
AlexanderYastrebov>
Mike Bryant
mikesname <https://github.com/mikesname>
Klaus Grossmann
iKlaus <https://github.com/iKlaus>
Pablo Lalloni
plalloni <https://github.com/plalloni>
Stefan Plantikow
boggle <https://github.com/boggle>
Trenton Strong
trentonstrong <https://github.com/trentonstrong>
Maciej Mazur
mamciek <https://github.com/mamciek>
German Borbolla
germanborbolla <https://github.com/
germanborbolla>
Laurent Raufaste
lra <https://github.com/lra>
Thomas Hfele
Perfect-Pixel <https://github.com/Perfect-Pixel>
Sevki Hasirci
Sevki <https://github.com/Sevki>
Max De Marzi
maxdemarzi <https://github.com/maxdemarzi>
PieterJanVanAeken <https://github.com/
PieterJanVanAeken>
Shane Gibbs
sgibbs-kellermed <https://github.com/sgibbskellermed>
Yin Wang
yinwang0 <https://github.com/yinwang0>
Volker Lanting
VolkerL <https://github.com/VolkerL>
Mark Needham
mneedham <https://github.com/mneedham>
Chris Vest
chrisvest <https://github.com/chrisvest>
Ben Butler-Cole
benbc <https://github.com/benbc>
Tatham Oddie
tathamoddie <https://github.com/tathamoddie>
503
PartVIII.Advanced Usage
This part contains information on advanced usage of Neo4j. Among the topics covered are embedding
Neo4j in your own software and writing plugins for the Neo4j Server.
505
31.1.Server Plugins
Quick info
The servers functionality can be extended by adding plugins.
Plugins are user-specified code which extend the capabilities of the database, nodes, or
relationships.
The neo4j server will then advertise the plugin functionality within representations as
clients interact via HTTP.
Plugins provide an easy way to extend the Neo4j REST API with new functionality, without the need
to invent your own API. Think of plugins as server-side scripts that can add functions for retrieving
and manipulating nodes, relationships, paths, properties or indices.
Tip
If you want to have full control over your API, and are willing to put in the effort, and
understand the risks, then Neo4j server also provides hooks for unmanaged extensions
based on JAX-RS.
The needed classes reside in the org.neo4j:server-api <http://search.maven.org/#search|gav|1|g
%3A%22org.neo4j%22%20AND%20a%3A%22server-api%22> jar file. See the linked page for
downloads and instructions on how to include it using dependency management. For Maven projects,
add the Server API dependencies in your pom.xml like this:
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>server-api</artifactId>
<version>${neo4j-version}</version>
</dependency>
506
Tip
Make sure the directories listings are retained in the jarfile by either building with default
Maven, or with jar -cvf myext.jar *, making sure to jar directories instead of specifying
single files.
The .jar file must include the file META-INF/services/org.neo4j.server.plugins.ServerPlugin with the
fully qualified name of the implementation class. This is an example with multiple entries, each on a
separate line:
507
The code above makes an extension visible in the database representation (via the @PluginTarget
annotation) whenever it is served from the Neo4j Server. Simply changing the @PluginTarget
parameter to Node.class or Relationship.class allows us to target those parts of the data model
should we wish. The functionality extensions provided by the plugin are automatically advertised
in representations on the wire. For example, clients can discover the extension implemented by the
above plugin easily by examining the representations they receive as responses from the server, e.g.
by performing a GET on the default database URI:
curl -v http://localhost:7474/db/data/
The response to the GET request will contain (by default) a JSON container that itself contains a
container called "extensions" where the available plugins are listed. In the following case, we only
have the GetAll plugin registered with the server, so only its extension functionality is available.
Extension names will be automatically assigned, based on method names, if not specifically specified
using the @Name annotation.
{
"extensions-info" : "http://localhost:7474/db/data/ext",
"node" : "http://localhost:7474/db/data/node",
"node_index" : "http://localhost:7474/db/data/index/node",
"relationship_index" : "http://localhost:7474/db/data/index/relationship",
"reference_node" : "http://localhost:7474/db/data/node/0",
"extensions_info" : "http://localhost:7474/db/data/ext",
"extensions" : {
"GetAll" : {
"get_all_nodes" : "http://localhost:7474/db/data/ext/GetAll/graphdb/get_all_nodes",
"get_all_relationships" : "http://localhost:7474/db/data/ext/GetAll/graphdb/getAllRelationships"
}
}
Performing a GET on one of the two extension URIs gives back the meta information about the service:
curl http://localhost:7474/db/data/ext/GetAll/graphdb/get_all_nodes
{
"extends" : "graphdb",
"description" : "Get all nodes from the Neo4j graph database",
"name" : "get_all_nodes",
"parameters" : [ ]
}
To use it, just POST to this URL, with parameters as specified in the description and encoded as
JSON data content. F.ex for calling the shortest path extension (URI gotten from a GET to http://
localhost:7474/db/data/node/123):
curl -X POST http://localhost:7474/db/data/ext/GetAll/node/123/shortestPath \
-H "Content-Type: application/json" \
-d '{"target":"http://localhost:7474/db/data/node/456&depth=5"}'
If everything is OK a response code 200 and a list of zero or more items will be returned. If nothing is
returned (null returned from extension) an empty result and response code 204 will be returned. If the
extension throws an exception response code 500 and a detailed error message is returned.
Extensions that do any kind of write operation will have to manage their own transactions, i.e.
transactions arent managed automatically.
Through this model, any plugin can naturally fit into the general hypermedia scheme that Neo4j
espouses meaning that clients can still take advantage of abstractions like Nodes, Relationships
508
509
31.2.Unmanaged Extensions
Quick info
Danger: Men at Work! The unmanaged extensions are a way of deploying arbitrary JAXRS code into the Neo4j server.
The unmanaged extensions are exactly that: unmanaged. If you drop poorly tested code into
the server, its highly likely youll degrade its performance, so be careful.
Some projects want extremely fine control over their server-side code. For this weve introduced an
unmanaged extension API.
Warning
Remember, the unmanaged API is a very sharp tool. Its all to easy to compromise the server by
deploying code this way, so think first and see if you cant use the managed extensions in preference.
However, a number of context parameters can be automatically provided for you, like the reference to
the database.
In order to specify the mount point of your extension, a full class looks like this:
Unmanaged extension example.
@Path( "/helloworld" )
public class HelloWorldResource
{
private final GraphDatabaseService database;
public HelloWorldResource( @Context GraphDatabaseService database )
{
this.database = database;
}
@GET
@Produces( MediaType.TEXT_PLAIN )
@Path( "/{nodeId}" )
public Response hello( @PathParam( "nodeId" ) long nodeId )
{
510
Tip
Make sure the directories listings are retained in the jarfile by either building with default
Maven, or with jar -cvf myext.jar *, making sure to jar directories instead of specifying
single files.
Tip
You will need to include a dependency to JAX-RS API on your classpath when you
compile. In Maven this would be achieved by adding the following to the pom file:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
Comma separated list of JAXRS packages containing JAXRS Resource, one package name for each mountpoint.
org.neo4j.server.thirdparty_jaxrs_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged
Which binds the hello method to respond to GET requests at the URI: http://{neo4j_server}:
{neo4j_port}/examples/unmanaged/helloworld/{nodeId}
curl http://localhost:7474/examples/unmanaged/helloworld/123
which results in
Hello World, nodeId=123
511
512
513
After selecting the appropriate edition for your platform, embed Neo4j in your Java application by
including the Neo4j library jars in your build. The following sections will show how to do this by
either altering the build path directly or by using dependency management.
32.1.2.Editions
The following table outlines the available editions and their names for use with dependency
management tools.
Tip
Follow the links in the table for details on dependency configuration with Apache Maven,
Apache Buildr, Apache Ivy, Groovy Grape, Grails, Scala SBT!
Neo4j editions
Edition
Community
Dependency
Description
License
org.neo4j:neo4j <http://
search.maven.org/
#search|gav|1|g
%3A%22org.neo4j
%22%20AND%20a
%3A%22neo4j%22>
a high performance,
fully ACID
transactional graph
database
GPLv3
514
Dependency
Description
License
Enterprise
org.neo4j:neo4jenterprise <http://
search.maven.org/
#search|gav|1|g
%3A%22org.neo4j
%22%20AND%20a
%3A%22neo4jenterprise%22>
adding advanced
monitoring, online
backup and High
Availability clustering
AGPLv3
Note
The listed dependencies do not contain the implementation, but pulls it in transitively.
For more information regarding licensing, see the Licensing Guide <http://www.neo4j.org/learn/
licensing>.
515
With that in place you can add Neo4j to the mix by having something along these lines to your ivy.xml
file:
..
<dependencies>
..
<dependency org="org.neo4j" name="neo4j" rev="2.0.0"/>
..
</dependencies>
..
Where the coordinates (org.neo4j:neo4j in the example) are found in the editions table above.
Note
The EmbeddedGraphDatabase instance can be shared among multiple threads. Note however
that you cant create multiple instances pointing to the same database.
To stop the database, call the shutdown() method:
graphDb.shutdown();
To make sure Neo4j is shut down properly you can add a shutdown hook:
private static void registerShutdownHook( final GraphDatabaseService graphDb )
{
// Registers a shutdown hook for the Neo4j instance so that it
// shuts down nicely when the VM exits (even if you "Ctrl-C" the
// running application).
Runtime.getRuntime().addShutdownHook( new Thread()
{
@Override
public void run()
{
graphDb.shutdown();
}
516
Or you could of course create you own Map<String, String> programmatically and use that instead,
like so:
Map<String, String> config = new HashMap<String, String>();
config.put( "neostore.nodestore.db.mapped_memory", "10M" );
config.put( "string_block_size", "60" );
config.put( "array_block_size", "300" );
GraphDatabaseService graphDb = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder( storeDir )
.setConfig( config )
.newGraphDatabase();
Note
Concurrent access to the same database files by multiple (read-only or write) instances is
not supported.
517
32.2.Hello World
Learn how to create and access nodes and relationships. For information on project setup, see
Section32.1, Include Neo4j in your project.
Remember, from Section2.1, What is a Graph Database?, that a Neo4j graph consist of:
Nodes that are connected by
Relationships, with
Properties on both nodes and relationships.
All relationships have a type. For example, if the graph represents a social network, a relationship type
could be KNOWS. If a relationship of the type KNOWS connects two nodes, that probably represents two
people that know each other. A lot of the semantics (that is the meaning) of a graph is encoded in the
relationship types of the application. And although relationships are directed they are equally well
traversed regardless of which direction they are traversed.
Tip
The next step is to start the database server. Note that if the directory given for the database doesnt
already exist, it will be created.
graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
registerShutdownHook( graphDb );
Note that starting a database server is an expensive operation, so dont start up a new instance
every time you need to interact with the database! The instance can be shared by multiple threads.
Transactions are thread confined.
As seen, we register a shutdown hook that will make sure the database shuts down when the JVM
exits. Now its time to interact with the database.
518
For more information on transactions, see Chapter16, Transaction Management and Java API for
Transaction <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/Transaction.html>.
Note
For brevity, we do not spell out wrapping of operations in a transaction throughout the
manual.
KNOWS
m essage = 'brave Neo4j '
519
Note that deleting a node which still has relationships when the transaction commits will fail. This is
to make sure relationships always have a start node and an end node.
520
Please read Section32.5, Managing resources when using long running transactions on
how to properly close ResourceIterators returned from index lookups.
You have a user database, and want to retrieve users by name using the legacy indexing system.
Tip
521
Tip
Then we have to configure the database to index users by name. This only needs to be done once.
IndexDefinition indexDefinition;
try ( Transaction tx = graphDb.beginTx() )
{
Schema schema = graphDb.schema();
indexDefinition = schema.indexFor( DynamicLabel.label( "User" ) )
.on( "username" )
.create();
tx.success();
}
Indexes are populated asynchronously when they are first created. It is possible to use the core API to
wait for index population to complete:
try ( Transaction tx = graphDb.beginTx() )
{
Schema schema = graphDb.schema();
schema.awaitIndexOnline( indexDefinition, 10, TimeUnit.SECONDS );
}
Note
Please read Section32.5, Managing resources when using long running transactions on
how to properly close ResourceIterators returned from index lookups.
Label label = DynamicLabel.label( "User" );
int idToFind = 45;
String nameToFind = "user" + idToFind + "@neo4j.org";
try ( Transaction tx = graphDb.beginTx() )
{
522
In case we change our data model, we can drop the index as well:
try ( Transaction tx = graphDb.beginTx() )
{
Label label = DynamicLabel.label( "User" );
for ( IndexDefinition indexDefinition : graphDb.schema()
.getIndexes( label ) )
{
// There is only one index
indexDefinition.drop();
}
tx.success();
}
523
524
Observe that the <type>test-jar</type> is crucial. Without it you would get the common neo4j-kernel
jar, not the one containing the testing facilities.
With that in place, were ready to code our tests.
Tip
For the full source code of this example see: Neo4jBasicDocTest.java <https://github.com/
neo4j/neo4j/blob/2.0.0/community/embedded-examples/src/test/java/org/neo4j/examples/
Neo4jBasicDocTest.java>
Before each test, create a fresh database:
@Before
public void prepareTestDatabase()
{
graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
}
After the test has executed, the database should be shut down:
@After
public void destroyTestDatabase()
{
525
During a test, create nodes and check to see that they are there, while enclosing write operations in a
transaction.
Node n = null;
try ( Transaction tx = graphDb.beginTx() )
{
n = graphDb.createNode();
n.setProperty( "name", "Nancy" );
tx.success();
}
// The node should have a valid id
assertThat( n.getId(), is( greaterThan( -1L ) ) );
// Retrieve a node by using the id of the created node. The id's and
// property should match.
try ( Transaction tx = graphDb.beginTx() )
{
Node foundNode = graphDb.getNodeById( n.getId() );
assertThat( foundNode.getId(), is( n.getId() ) );
assertThat( (String) foundNode.getProperty( "name" ), is( "Nancy" ) );
}
If you want to set configuration parameters at database creation, its done like this:
Map<String, String> config = new HashMap<String, String>();
config.put( "neostore.nodestore.db.mapped_memory", "10M" );
config.put( "string_block_size", "60" );
config.put( "array_block_size", "300" );
GraphDatabaseService db = new TestGraphDatabaseFactory()
.newImpermanentDatabaseBuilder()
.setConfig( config )
.newGraphDatabase();
526
32.7.Traversal
For reading about traversals, see Chapter33, The Traversal Framework.
For more examples of traversals, see Chapter5, Data Modeling Examples.
32.7.1.The Matrix
This is the first graph we want to traverse into:
Figure32.2.Matrix node space view
Tip
527
Tip
A
B
C
D
=
=
=
=
db.createNode();
db.createNode();
db.createNode();
db.createNode();
A.createRelationshipTo(
C.createRelationshipTo(
A.createRelationshipTo(
B.createRelationshipTo(
C,
D,
B,
C,
REL2
REL3
REL1
REL2
);
);
);
);
528
REL1
REL2
REL2
REL3
Now, the order of relationships (REL1 REL2 REL3) is stored in an ArrayList. Upon traversal,
the Evaluator can check against it to ensure that only paths are included and returned that have the
predefined order of relationships:
Define how to walk the path.
final ArrayList<RelationshipType> orderedPathContext = new ArrayList<RelationshipType>();
orderedPathContext.add( REL1 );
orderedPathContext.add( withName( "REL2" ) );
orderedPathContext.add( withName( "REL3" ) );
TraversalDescription td = Traversal.description()
.evaluator( new Evaluator()
{
@Override
public Evaluation evaluate( final Path path )
{
if ( path.length() == 0 )
{
return Evaluation.EXCLUDE_AND_CONTINUE;
}
RelationshipType expectedType = orderedPathContext.get( path.length() - 1 );
boolean isExpectedType = path.lastRelationship()
.isType( expectedType );
boolean included = path.length() == orderedPathContext.size() && isExpectedType;
boolean continued = path.length() < orderedPathContext.size() && isExpectedType;
return Evaluation.of( included, continued );
}
} )
.uniqueness( Uniqueness.NODE_PATH );
In this case we use a custom class to format the path output. This is how its done:
static class PathPrinter implements Traversal.PathDescriptor<Path>
{
private final String nodePropertyKey;
public PathPrinter( String nodePropertyKey )
{
this.nodePropertyKey = nodePropertyKey;
}
@Override
public String nodeRepresentation( Path path, Node node )
{
return "(" + node.getProperty( nodePropertyKey, "" ) + ")";
}
@Override
public String relationshipRepresentation( Path path, Node from, Relationship relationship )
{
String prefix = "--", suffix = "--";
if ( from.equals( relationship.getEndNode() ) )
{
prefix = "<--";
}
else
{
suffix = "-->";
}
return prefix + "[" + relationship.getType().name() + "]" + suffix;
}
}
This example is demonstrating the use of node uniqueness. Below an imaginary domain graph with
Principals that own pets that are descendant to other pets.
Figure32.3.Descendants Example Graph
Node[ 4] nam e = 'Principal1'
owns
owns
descendant
descendant
530
descendant
owns
In the default path.toString() implementation, (1)--[knows,2]-->(4) denotes a node with ID=1 having
a relationship with ID 2 or type knows to a node with ID-4.
Lets create a new TraversalDescription from the old one, having NODE_GLOBAL uniqueness to see the
difference.
Tip
The TraversalDescription object is immutable, so we have to use the new instance returned
with the new uniqueness setting.
TraversalDescription nodeGlobalTd = td.uniqueness( Uniqueness.NODE_GLOBAL );
results = nodeGlobalTd.traverse( start );
32.7.3.Social network
Note
Tip
531
The data model for a social network is pretty simple: Persons with names and StatusUpdates with
timestamped text. These entities are then connected by specific relationships.
Person
friend: relates two distinct Person instances (no self-reference)
status: connects to the most recent StatusUpdate
StatusUpdate
next: points to the next StatusUpdate in the chain, which was posted before the current one
Status graph instance
The StatusUpdate list for a Person is a linked list. The head of the list (the most recent status) is found
by following status. Each subsequent StatusUpdate is connected by next.
Heres an example where Andreas Kollegger micro-blogged his way to work in the morning:
532
Andreas Kollegger
st at us
next
next
This gives us a traverser that will start at one StatusUpdate, and will follow the chain of updates until
they run out. Traversers are lazy loading, so its performant even when dealing with thousands of
statuses they are not loaded until we actually consume them.
Activity stream
Once we have friends, and they have status messages, we might want to read our friends status'
messages, in reverse time order latest first. To do this, we go through these steps:
1.
2.
3.
4.
5.
Gather all friends status update iterators in a list latest date first.
Sort the list.
Return the first item in the list.
If the first iterator is exhausted, remove it from the list. Otherwise, get the next item in that iterator.
Go to step 2 until there are no iterators left in the list.
533
534
32.8.Domain entities
This page demonstrates one way to handle domain entities when using Neo4j. The principle at use is
to wrap the entities around a node (the same approach can be used with relationships as well).
Tip
535
The source code used in the example is found here: PathFindingDocTest.java <https://
github.com/neo4j/neo4j/blob/2.0.0/community/embedded-examples/src/test/java/org/
neo4j/examples/PathFindingDocTest.java>
Calculating the shortest path (least number of relationships) between two nodes:
Node startNode = graphDb.createNode();
Node middleNode1 = graphDb.createNode();
Node middleNode2 = graphDb.createNode();
Node middleNode3 = graphDb.createNode();
Node endNode = graphDb.createNode();
createRelationshipsBetween( startNode, middleNode1, endNode );
createRelationshipsBetween( startNode, middleNode2, middleNode3, endNode );
// Will find the shortest path between startNode and endNode via
// "MY_TYPE" relationships (in OUTGOING direction), like f.ex:
//
// (startNode)-->(middleNode1)-->(endNode)
//
PathFinder<Path> finder = GraphAlgoFactory.shortestPath(
Traversal.expanderForTypes( ExampleTypes.MY_TYPE, Direction.OUTGOING ), 15 );
Iterable<Path> paths = finder.findAllPaths( startNode, endNode );
Node nodeA =
Node nodeB =
Node nodeC =
Relationship
Relationship
Relationship
);
);
);
"length", 2d );
"length", 3d );
"length", 10d );
536
goal )
(Double) goal.getProperty( "x" );
(Double) goal.getProperty( "y" );
Math.pow( dy, 2 ) );
};
PathFinder<WeightedPath> astar = GraphAlgoFactory.aStar(
Traversal.expanderForAllTypes(),
CommonEvaluators.doubleCostEvaluator( "length" ), estimateEvaluator );
WeightedPath path = astar.findSinglePath( nodeA, nodeB );
537
Tip
Depending on which Neo4j edition you are using different sets of management beans are available.
For all editions, see the org.neo4j.jmx <http://components.neo4j.org/neo4j-jmx/2.0.0/apidocs/org/
neo4j/jmx/package-summary.html> package.
For the Enterprise edition, see the org.neo4j.management <http://components.neo4j.org/neo4jmanagement/2.0.0/apidocs/org/neo4j/management/package-summary.html> package as well.
538
While this is a working solution, please consider using the preferred Section32.11.1, Get
or create unique node using Cypher and unique constraints instead.
By using put-if-absent <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/
Index.html#putIfAbsent%28T,%20java.lang.String,%20java.lang.Object%29> functionality, entity
uniqueness can be guaranteed using an index.
Here the index acts as the lock and will only lock the smallest part needed to guarantee uniqueness
across threads and transactions. To get the more high-level get-or-create functionality make use
of UniqueFactory <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/
UniqueFactory.html> as seen in the example below.
Create a factory for unique nodes at application start.
try ( Transaction tx = graphDb.beginTx() )
{
UniqueFactory.UniqueNodeFactory result = new UniqueFactory.UniqueNodeFactory( graphDb, "users" )
{
@Override
protected void initialize( Node created, Map<String, Object> properties )
{
created.addLabel( DynamicLabel.label( "User" ) );
created.setProperty( "name", properties.get( "name" ) );
539
While this is a working solution, please consider using the preferred Section32.11.1, Get
or create unique node using Cypher and unique constraints instead.
One might be tempted to use Java synchronization for pessimistic locking, but this is dangerous. By
mixing locks in Neo4j and in the Java runtime, it is easy to produce deadlocks that are not detectable
by Neo4j. As long as all locking is done by Neo4j, all deadlocks will be detected and avoided. Also, a
solution using manual synchronization doesnt ensure uniqueness in an HA environment.
This example uses a single lock node for locking. We create it only as a place to put locks, nothing
else.
Create a lock node at application start.
try ( Transaction tx = graphDb.beginTx() )
{
final Node lockNode = graphDb.createNode();
tx.success();
return lockNode;
}
Use the lock node to ensure nodes are not created concurrently.
try ( Transaction tx = graphDb.beginTx() )
{
Index<Node> usersIndex = graphDb.index().forNodes( "users" );
Node userNode = usersIndex.get( "name", username ).getSingle();
if ( userNode != null )
{
return userNode;
}
tx.acquireWriteLock( lockNode );
userNode = usersIndex.get( "name", username ).getSingle();
if ( userNode == null )
{
userNode = graphDb.createNode( DynamicLabel.label( "User" ) );
usersIndex.add( userNode, "name", username );
userNode.setProperty( "name", username );
}
tx.success();
return userNode;
}
Note that finishing the transaction will release the lock on the lock node.
540
Execute a query:
ExecutionEngine engine = new ExecutionEngine( db );
ExecutionResult result;
try ( Transaction ignored = db.beginTx() )
{
result = engine.execute( "start n=node(*) where n.name = 'my node' return n, n.name" );
Note
Keep the ExecutionEngine around, dont create a new one for each query!
The result will be:
+-------------------------------------+
| n
| n.name
|
+-------------------------------------+
| Node[0]{name:"my node"} | "my node" |
+-------------------------------------+
1 row
Caution
The classes used here are from the org.neo4j.cypher.javacompat package, not
org.neo4j.cypher, see link to the Java API below.
You can get a list of the columns in the result:
List<String> columns = result.columns();
This contains:
[n, n.name]
541
This outputs:
n.name: my node; n: Node[0];
Caution
dumpToString(), columnAs()
Caution
When using an ExecutionResult, youll need to exhaust it by using any of the iterating
methods (columnAs() and iterator()) on it. Failing to do so will not properly clean up
resources used by the ExecutionResult, leading to unwanted behavior, such as leaking
transactions.
For more information on the Java interface to Cypher, see the Java API <http://components.neo4j.org/
neo4j-cypher/2.0.0/apidocs/index.html>.
For more information and examples for Cypher, see PartIII, Cypher Query Language and
Chapter5, Data Modeling Examples.
542
32.13.Query Parameters
For more information on parameters see Section7.5, Parameters.
Below follows example of how to use parameters when executing Cypher queries from Java.
Node id.
Map<String, Object> params = new HashMap<String, Object>();
params.put( "id", 0 );
String query = "START n=node({id}) RETURN n.name";
ExecutionResult result = engine.execute( query, params );
Node object.
Map<String, Object> params = new HashMap<String, Object>();
params.put( "node", andreasNode );
String query = "START n=node({node}) RETURN n.name";
ExecutionResult result = engine.execute( query, params );
String literal.
Map<String, Object> params = new HashMap<String, Object>();
params.put( "name", "Johan" );
String query = "MATCH (n) WHERE n.name = {name} RETURN n";
ExecutionResult result = engine.execute( query, params );
Index value.
Map<String, Object> params = new HashMap<String, Object>();
params.put( "value", "Michaela" );
String query = "START n=node:people(name = {value}) RETURN n";
ExecutionResult result = engine.execute( query, params );
Index query.
Map<String, Object> params = new HashMap<String, Object>();
params.put( "query", "name:Andreas" );
String query = "START n=node:people({query}) RETURN n";
ExecutionResult result = engine.execute( query, params );
Regular expression.
Map<String, Object> params = new HashMap<String, Object>();
params.put( "regex", ".*h.*" );
String query = "MATCH (n) WHERE n.name =~ {regex} RETURN n.name";
ExecutionResult result = engine.execute( query, params );
543
544
545
33.1.Main concepts
Here follows a short explanation of all different methods that can modify or add to a traversal
description.
Expanders define what to traverse, typically in terms of relationship direction and type.
Order for example depth-first or breadth-first.
Uniqueness visit nodes (relationships, paths) only once.
Evaluator decide what to return and whether to stop or continue traversal beyond the current
position.
Starting nodes where the traversal will begin.
Dept h First
Breadt h First
Order
Include/Exclude
Unique Pat hs
Evaluat or
None
Uniqueness
where t o go next
Prune/Cont inue
Traverser
result as result as
Pat hs
Nodes
result as
Relat ionships
546
Direct ion
Expander
what t o t raverse
applies
A Node
Unique Nodes
33.2.1.TraversalDescription
The TraversalDescription <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/
traversal/TraversalDescription.html> is the main interface used for defining and initializing traversals.
It is not meant to be implemented by users of the traversal framework, but rather to be provided
by the implementation of the traversal framework as a way for the user to describe traversals.
TraversalDescription instances are immutable and its methods returns a new TraversalDescription that
is modified compared to the object the method was invoked on with the arguments of the method.
Relationships
Adds a relationship type to the list of relationship types to traverse. By default that list is
empty and it means that it will traverse all relationships, irregardless of type. If one or more
relationships are added to this list only the added types will be traversed. There are two
methods, one including direction <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/
neo4j/graphdb/traversal/TraversalDescription.html#relationships> and another one excluding
direction <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/traversal/
TraversalDescription.html#relationships>, where the latter traverses relationships in both directions
<http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/Direction.html#BOTH>.
33.2.2.Evaluator
<http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/traversal/
Evaluator.html>s are used for deciding, at each position (represented as a Path): should the traversal
continue, and/or should the node be included in the result. Given a Path, it asks for one of four actions
for that branch of the traversal:
Evaluator
Evaluation.INCLUDE_AND_CONTINUE:
More than one evaluator can be added. Note that evaluators will be called for all positions the
traverser encounters, even for the start node.
33.2.3.Traverser
The Traverser <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/
traversal/Traverser.html> object is the result of invoking traverse() <http://
components.neo4j.org/neo4j-kernel/2.0.0/apidocs/org/neo4j/graphdb/traversal/
TraversalDescription.html#traverse(org.neo4j.graphdb.Node)> of a TraversalDescription object. It
represents a traversal positioned in the graph, and a specification of the format of the result. The actual
traversal is performed lazily each time the next()-method of the iterator of the Traverser is invoked.
547
33.2.4.Uniqueness
Sets the rules for how positions can be revisited during a traversal as stated in Uniqueness
<http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/kernel/Uniqueness.html>. Default
if not set is NODE_GLOBAL <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/kernel/
Uniqueness.html#NODE_GLOBAL>.
A Uniqueness can be supplied to the TraversalDescription to dictate under what circumstances a
traversal may revisit the same position in the graph. The various uniqueness levels that can be used in
Neo4j are:
NONE: Any position in the graph may be revisited.
NODE_GLOBAL uniqueness: No node in the entire graph may be visited more than once. This could
potentially consume a lot of memory since it requires keeping an in-memory data structure
remembering all the visited nodes.
RELATIONSHIP_GLOBAL uniqueness: no relationship in the entire graph may be visited more than once.
For the same reasons as NODE_GLOBAL uniqueness, this could use up a lot of memory. But since
graphs typically have a larger number of relationships than nodes, the memory overhead of this
uniqueness level could grow even quicker.
NODE_PATH uniqueness: A node may not occur previously in the path reaching up to it.
RELATIONSHIP_PATH uniqueness: A relationship may not occur previously in the path reaching up to it.
NODE_RECENT uniqueness: Similar to NODE_GLOBAL uniqueness in that there is a global collection of
visited nodes each position is checked against. This uniqueness level does however have a cap on
how much memory it may consume in the form of a collection that only contains the most recently
visited nodes. The size of this collection can be specified by providing a number as the second
argument to the TraversalDescription.uniqueness()-method along with the uniqueness level.
RELATIONSHIP_RECENT uniqueness: Works like NODE_RECENT uniqueness, but with relationships instead
of nodes.
Depth First / Breadth First
These are convenience methods for setting preorder depth-first <http://en.wikipedia.org/
wiki/Depth-first_search>/ breadth-first <http://en.wikipedia.org/wiki/Breadth-first_search>
BranchSelector|ordering policies. The same result can be achieved by calling the
order <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/traversal/
TraversalDescription.html#order> method with ordering policies from the Traversal <http://
components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/kernel/Traversal.html#preorderDepthFirst>
factory <http://components.neo4j.org/neo4j-kernel/2.0.0/apidocs/org/neo4j/kernel/
Traversal.html#preorderBreadthFirst>, or to write your own BranchSelector/BranchOrderingPolicy and
pass in.
33.2.6.BranchSelector
A BranchSelector is used for selecting which branch of the traversal to attempt next. This is used
for implementing traversal orderings. The traversal framework provides a few basic ordering
implementations:
548
Note
Please note that breadth first traversals have a higher memory overhead than depth first
traversals.
BranchSelectors carries state and hence needs to be uniquely instantiated for each traversal. Therefore
it is supplied to the TraversalDescription through a BranchOrderingPolicy interface, which is a factory
of BranchSelector instances.
A user of the Traversal framework rarely needs to implement his own BranchSelector or
BranchOrderingPolicy, it is provided to let graph algorithm implementors provide their own traversal
orders. The Neo4j Graph Algorithms package contains for example a BestFirst order BranchSelector/
BranchOrderingPolicy that is used in BestFirst search algorithms such as A* and Dijkstra.
BranchOrderingPolicy
A factory for creating BranchSelectors to decide in what order branches are returned (where
a branchs position is represented as a Path <http://components.neo4j.org/neo4j/2.0.0/
apidocs/org/neo4j/graphdb/Path.html> from the start node to the current node). Common
policies are depth-first <http://components.neo4j.org/neo4j-kernel/2.0.0/apidocs/org/
neo4j/graphdb/traversal/TraversalDescription.html#depthFirst()> and breadth-first
<http://components.neo4j.org/neo4j-kernel/2.0.0/apidocs/org/neo4j/graphdb/traversal/
TraversalDescription.html#breadthFirst()> and thats why there are convenience methods for those.
For example, calling TraversalDescriptiondepthFirst() <http://components.neo4j.org/neo4j/2.0.0/
apidocs/org/neo4j/graphdb/traversal/TraversalDescription.html#depthFirst()> is equivalent to:
description.order( Traversal.preorderDepthFirst() );
TraversalBranch
An object used by the BranchSelector to get more branches from a certain branch. In essence these are
a composite of a Path and a RelationshipExpander that can be used to get new TraversalBranch <http://
components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/traversal/TraversalBranch.html>es from
the current one.
33.2.7.Path
A Path <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/Path.html> is a general
interface that is part of the Neo4j API. In the traversal API of Neo4j the use of Paths are twofold.
Traversers can return their results in the form of the Paths of the visited positions in the graph that are
marked for being returned. Path objects are also used in the evaluation of positions in the graph, for
determining if the traversal should continue from a certain point or not, and whether a certain position
should be included in the result set or not.
549
33.2.8.PathExpander/RelationshipExpander
The traversal framework use PathExpanders (replacing RelationshipExpander) to discover the
relationships that should be followed from a particular path to further branches in the traversal.
33.2.9.Expander
A more generic version of relationships where a RelationshipExpander is injected, defining all
relationships to be traversed for any given node. By default (and when using relationships)
a default expander <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/kernel/
Traversal.html#emptyExpander> is used, where any particular order of relationships isnt
guaranteed. Theres another implementation which guarantees that relationships are traversed in
order of relationship type <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/kernel/
OrderedByTypeExpander.html>, where types are iterated in the order they were added.
The Expander interface is an extension of the RelationshipExpander interface that makes it possible to
build customized versions of an Expander. The implementation of TraversalDescription uses this to
provide methods for defining which relationship types to traverse, this is the usual way a user of the
API would define a RelationshipExpander by building it internally in the TraversalDescription.
All the RelationshipExpanders provided by the Neo4j traversal framework also implement the
Expander interface. For a user of the traversal API it is easier to implement the PathExpander/
RelationshipExpander interface, since it only contains one method the method for getting the
relationships from a path/node, the methods that the Expander interface adds are just for building new
Expanders.
550
LIKES
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
The graph can be traversed with for example the following traverser, starting at the Joe node:
for ( Path position : Traversal.description()
.depthFirst()
.relationships( Rels.KNOWS )
.relationships( Rels.LIKES, Direction.INCOMING )
.evaluator( Evaluators.toDepth( 5 ) )
.traverse( node ) )
{
output += position + "\n";
}
551
This traverser would yield the following output (we will keep starting from the Joe node):
(6)
(6)--[KNOWS,0]-->(1)
(6)--[KNOWS,0]-->(1)<--[KNOWS,2]--(4)
(6)--[KNOWS,0]-->(1)<--[KNOWS,2]--(4)<--[KNOWS,3]--(5)
(6)--[KNOWS,0]-->(1)<--[KNOWS,2]--(4)<--[KNOWS,3]--(5)<--[KNOWS,4]--(0)
(6)--[KNOWS,0]-->(1)<--[KNOWS,2]--(4)<--[KNOWS,3]--(5)<--[KNOWS,4]--(0)<--[KNOWS,5]--(2)
(6)--[KNOWS,0]-->(1)<--[KNOWS,2]--(4)<--[KNOWS,3]--(5)<--[KNOWS,4]--(0)<--[KNOWS,6]--(3)
Now lets create a new traverser from it, restricting depth to three:
for ( Path path : FRIENDS_TRAVERSAL
.evaluator( Evaluators.toDepth( 3 ) )
.traverse( node ) )
{
output += path + "\n";
}
Or how about from depth two to four? Thats done like this:
for ( Path path : FRIENDS_TRAVERSAL
.evaluator( Evaluators.fromDepth( 2 ) )
.evaluator( Evaluators.toDepth( 4 ) )
.traverse( node ) )
{
output += path + "\n";
}
Relationships <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/traversal/
Traverser.html#relationships()> are fine as well, heres how to get them:
for ( Relationship relationship : FRIENDS_TRAVERSAL
.traverse( node )
.relationships() )
{
output += relationship.getType() + "\n";
}
id:0]
id:0]
id:0]
id:0]
id:0]
id:0]
Tip
The source code for the traversers in this example is available at: TraversalExample.java
<https://github.com/neo4j/neo4j/blob/2.0.0/community/embedded-examples/src/main/java/
org/neo4j/examples/TraversalExample.java>
553
Chapter34.Legacy Indexing
Note
This is not the same as indexes defined in the schema, the documentation below is for the
legacy indexing in Neo4j.
This chapter focuses on how to use the Manual Indexes and Autoindexes. As of Neo4j 2.0, this is not
the favored method of indexing data in Neo4j, instead we recommend defining indexes in the database
schema.
However, support for legacy indexes remains, because certain features, such as uniqueness
constraints, are not yet handled by the new indexes.
554
Legacy Indexing
34.1.Introduction
Legacy Indexing operations are part of the Neo4j index API <http://components.neo4j.org/
neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/package-summary.html>.
Each index is tied to a unique, user-specified name (for example "first_name" or "books") and
can index either nodes <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/
Node.html> or relationships <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/
Relationship.html>.
The default index implementation is provided by the neo4j-lucene-index component, which is included
in the standard Neo4j download. It can also be downloaded separately from http://repo1.maven.org/
maven2/org/neo4j/neo4j-lucene-index/ . For Maven users, the neo4j-lucene-index component
has the coordinates org.neo4j:neo4j-lucene-index and should be used with the same version of
org.neo4j:neo4j-kernel. Different versions of the index and kernel components are not compatible in
the general case. Both components are included transitively by the org.neo4j:neo4j:pom artifact which
makes it simple to keep the versions in sync.
For initial import of data using indexes, see Section35.3, Index Batch Insertion.
Note
All modifying index operations must be performed inside a transaction, as with any
modifying operation in Neo4j.
555
Legacy Indexing
34.2.Create
An index is created if it doesnt exist when you ask for it. Unless you give it a custom configuration, it
will be created with default configuration and backend.
To set the stage for our examples, lets create some indexes to begin with:
IndexManager index = graphDb.index();
Index<Node> actors = index.forNodes( "actors" );
Index<Node> movies = index.forNodes( "movies" );
RelationshipIndex roles = index.forRelationships( "roles" );
This will create two node indexes and one relationship index with default configuration. See
Section34.8, Relationship indexes for more information specific to relationship indexes.
See Section34.10, Configuration and fulltext indexes for how to create fulltext indexes.
You can also check if an index exists like this:
IndexManager index = graphDb.index();
boolean indexExists = index.existsForNodes( "actors" );
556
Legacy Indexing
34.3.Delete
Indexes can be deleted. When deleting, the entire contents of the index will be removed as well as its
associated configuration. An index can be created with the same name at a later point in time.
IndexManager index = graphDb.index();
Index<Node> actors = index.forNodes( "actors" );
actors.delete();
Note that the actual deletion of the index is made during the commit of the surrounding transaction.
Calls made to such an index instance after delete() <http://components.neo4j.org/neo4j/2.0.0/
apidocs/org/neo4j/graphdb/index/Index.html#delete%28%29> has been called are invalid inside that
transaction as well as outside (if the transaction is successful), but will become valid again if the
transaction is rolled back.
557
Legacy Indexing
34.4.Add
Each index supports associating any number of key-value pairs with any number of entities (nodes or
relationships), where each association between entity and key-value pair is performed individually. To
begin with, lets add a few nodes to the indexes:
// Actors
Node reeves = graphDb.createNode();
reeves.setProperty( "name", "Keanu Reeves" );
actors.add( reeves, "name", reeves.getProperty( "name" ) );
Node bellucci = graphDb.createNode();
bellucci.setProperty( "name", "Monica Bellucci" );
actors.add( bellucci, "name", bellucci.getProperty( "name" ) );
// multiple values for a field, in this case for search only
// and not stored as a property.
actors.add( bellucci, "name", "La Bellucci" );
// Movies
Node theMatrix = graphDb.createNode();
theMatrix.setProperty( "title", "The Matrix" );
theMatrix.setProperty( "year", 1999 );
movies.add( theMatrix, "title", theMatrix.getProperty( "title" ) );
movies.add( theMatrix, "year", theMatrix.getProperty( "year" ) );
Node theMatrixReloaded = graphDb.createNode();
theMatrixReloaded.setProperty( "title", "The Matrix Reloaded" );
theMatrixReloaded.setProperty( "year", 2003 );
movies.add( theMatrixReloaded, "title", theMatrixReloaded.getProperty( "title" ) );
movies.add( theMatrixReloaded, "year", 2003 );
Node malena = graphDb.createNode();
malena.setProperty( "title", "Malna" );
malena.setProperty( "year", 2000 );
movies.add( malena, "title", malena.getProperty( "title" ) );
movies.add( malena, "year", malena.getProperty( "year" ) );
Note that there can be multiple values associated with the same entity and key.
Next up, well create relationships and index them as well:
// we need a relationship type
DynamicRelationshipType ACTS_IN = DynamicRelationshipType.withName( "ACTS_IN" );
// create relationships
Relationship role1 = reeves.createRelationshipTo( theMatrix, ACTS_IN );
role1.setProperty( "name", "Neo" );
roles.add( role1, "name", role1.getProperty( "name" ) );
Relationship role2 = reeves.createRelationshipTo( theMatrixReloaded, ACTS_IN );
role2.setProperty( "name", "Neo" );
roles.add( role2, "name", role2.getProperty( "name" ) );
Relationship role3 = bellucci.createRelationshipTo( theMatrixReloaded, ACTS_IN );
role3.setProperty( "name", "Persephone" );
roles.add( role3, "name", role3.getProperty( "name" ) );
Relationship role4 = bellucci.createRelationshipTo( malena, ACTS_IN );
role4.setProperty( "name", "Malna Scordia" );
roles.add( role4, "name", role4.getProperty( "name" ) );
558
Legacy Indexing
Figure34.1.Movie and Actor Graph
nam e = 'Keanu Reeves'
ACTS_IN
nam e = 'Neo'
ACTS_IN
nam e = 'Neo'
ACTS_IN
nam e = 'Persephone'
559
ACTS_IN
nam e = 'Malna Scordia'
t it le = 'Malna'
year = 2000
Legacy Indexing
34.5.Remove
Removing <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/
Index.html#remove%28T,%20java.lang.String,%20java.lang.Object%29> from an index is similar to
adding, but can be done by supplying one of the following combinations of arguments:
entity
entity, key
entity, key, value
// completely remove bellucci from the actors index
actors.remove( bellucci );
// remove any "name" entry of bellucci from the actors index
actors.remove( bellucci, "name" );
// remove the "name" -> "La Bellucci" entry of bellucci
actors.remove( bellucci, "name", "La Bellucci" );
560
Legacy Indexing
34.6.Update
Important
To update an index entry, the old one must be removed and a new one added. For details
on removing index entries, see Section34.5, Remove.
Remember that a node or relationship can be associated with any number of key-value pairs in an
index. This means that you can index a node or relationship with many key-value pairs that have
the same key. In the case where a property value changes and youd like to update the index, its not
enough to just index the new value youll have to remove the old value as well.
Heres a code example that demonstrates how its done:
// create a node with a property
// so we have something to update later on
Node fishburn = graphDb.createNode();
fishburn.setProperty( "name", "Fishburn" );
// index it
actors.add( fishburn, "name", fishburn.getProperty( "name" ) );
// update the index entry
// when the property value changes
actors.remove( fishburn, "name", fishburn.getProperty( "name" ) );
fishburn.setProperty( "name", "Laurence Fishburn" );
actors.add( fishburn, "name", fishburn.getProperty( "name" ) );
561
Legacy Indexing
34.7.Search
An index can be searched in two ways, get <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/
neo4j/graphdb/index/Index.html#get%28java.lang.String,%20java.lang.Object%29> and query
<http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/Index.html#query
%28java.lang.String,%20java.lang.Object%29>. The get method will return exact matches to
the given key-value pair, whereas query exposes querying capabilities directly from the backend
used by the index. For example the Lucene query syntax <http://lucene.apache.org/core/3_6_2/
queryparsersyntax.html> can be used directly with the default indexing backend.
34.7.1.Get
This is how to search for a single exact match:
IndexHits<Node> hits = actors.get( "name", "Keanu Reeves" );
Node reeves = hits.getSingle();
IndexHits <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/
IndexHits.html> is an Iterable with some additional useful methods. For example getSingle() <http://
components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/IndexHits.html#getSingle
%28%29> returns the first and only item from the result iterator, or null if there isnt any hit.
Heres how to get a single relationship by exact matching and retrieve its start and end nodes:
Relationship persephone = roles.get( "name", "Persephone" ).getSingle();
Node actor = persephone.getStartNode();
Node movie = persephone.getEndNode();
Finally, we can iterate over all exact matches from a relationship index:
for ( Relationship role : roles.get( "name", "Neo" ) )
{
// this will give us Reeves twice
Node reeves = role.getStartNode();
}
Important
In case you dont iterate through all the hits, IndexHits.close() <http://
components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/IndexHits.html#close
%28%29> must be called explicitly.
34.7.2.Query
There are two query methods, one which uses a key-value signature where the value represents a
query for values with the given key only. The other method is more generic and supports querying for
more than one key-value pair in the same query.
Heres an example using the key-query option:
for ( Node actor : actors.query( "name", "*e*" ) )
{
// This will return Reeves and Bellucci
}
562
Legacy Indexing
Note
Beginning a wildcard search with "*" or "?" is discouraged by Lucene, but will
nevertheless work.
Caution
You cant have any whitespace in the search term with this syntax. See Section34.11.3,
Querying with Lucene Query objects for how to do that.
563
Legacy Indexing
34.8.Relationship indexes
An index for relationships is just like an index for nodes, extended by providing support to constrain
a search to relationships with a specific start and/or end nodes These extra methods reside in the
RelationshipIndex <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/
RelationshipIndex.html> interface which extends Index<Relationship> <http://components.neo4j.org/
neo4j/2.0.0/apidocs/org/neo4j/graphdb/index/Index.html>.
Example of querying a relationship index:
// find relationships filtering on start node
// using exact matches
IndexHits<Relationship> reevesAsNeoHits;
reevesAsNeoHits = roles.get( "name", "Neo", reeves, null );
Relationship reevesAsNeo = reevesAsNeoHits.iterator().next();
reevesAsNeoHits.close();
// find relationships filtering on end node
// using a query
IndexHits<Relationship> matrixNeoHits;
matrixNeoHits = roles.query( "name", "*eo", null, theMatrix );
Relationship matrixNeo = matrixNeoHits.iterator().next();
matrixNeoHits.close();
And heres an example for the special case of searching for a specific relationship type:
// find relationships filtering on end node
// using a relationship type.
// this is how to add it to the index:
roles.add( reevesAsNeo, "type", reevesAsNeo.getType().name() );
// Note that to use a compound query, we can't combine committed
// and uncommitted index entries, so we'll commit before querying:
tx.success();
tx.finish();
// and now we can search for it:
try ( Transaction tx = graphDb.beginTx() )
{
IndexHits<Relationship> typeHits = roles.query( "type:ACTS_IN AND name:Neo", null, theMatrix );
Relationship typeNeo = typeHits.iterator().next();
typeHits.close();
Such an index can be useful if your domain has nodes with a very large number of relationships
between them, since it reduces the search time for a relationship between two nodes. A good example
where this approach pays dividends is in time series data, where we have readings represented as a
relationship per occurrence.
564
Legacy Indexing
34.9.Scores
The IndexHits interface exposes scoring <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/
graphdb/index/IndexHits.html#currentScore%28%29> so that the index can communicate scores
for the hits. Note that the result is not sorted by the score unless you explicitly specify that. See
Section34.11.2, Sorting for how to sort by score.
IndexHits<Node> hits = movies.query( "title", "The*" );
for ( Node movie : hits )
{
System.out.println( movie.getProperty( "title" ) + " " + hits.currentScore() );
}
565
Legacy Indexing
Tip
In order to search for tokenized words, the query method has to be used. The get method
will only match the full string value, not the tokens.
The configuration of the index is persisted once the index has been created. The provider
configuration key is interpreted by Neo4j, but any other configuration is passed onto the backend
index (e.g. Lucene) to interpret.
Lucene indexing configuration parameters
Parameter
Possible values
Effect
type
exact, fulltext
exact
to_lower_case
true, false
analyzer
566
Legacy Indexing
Note
The same type must be used for indexing and querying. That is, you cant index a value as
a Long and then query the index using an Integer.
By giving null as from/to argument, an open ended query is created. In the following example we are
doing that, and have added sorting to the query as well:
hits = movies.query(
QueryContext.numericRange( "year-numeric", from, null )
.sortNumeric( "year-numeric", false ) );
From/to in the ranges defaults to be inclusive, but you can change this behavior by using two extra
parameters:
movies.add( theMatrix, "score", new ValueContext( 8.7 ).indexNumeric() );
movies.add( theMatrixReloaded, "score", new ValueContext( 7.1 ).indexNumeric() );
movies.add( malena, "score", new ValueContext( 7.4 ).indexNumeric() );
// include 8.0, exclude 9.0
hits = movies.query( QueryContext.numericRange( "score", 8.0, 9.0, true, false ) );
34.11.2.Sorting
Lucene performs sorting very well, and that is also exposed in the index backend, through the
QueryContext <http://components.neo4j.org/neo4j-lucene-index/2.0.0/apidocs/org/neo4j/index/
lucene/QueryContext.html> class:
hits = movies.query( "title", new QueryContext( "*" ).sort( "title" ) );
for ( Node hit : hits )
{
// all movies with a title in the index, ordered by title
}
// or
hits = movies.query( new QueryContext( "title:*" ).sort( "year", "title" ) );
for ( Node hit : hits )
{
// all movies with a title in the index, ordered by year, then title
}
567
Legacy Indexing
// hits sorted by relevance (score)
}
34.11.4.Compound queries
Lucene supports querying for multiple terms in the same query, like so:
hits = movies.query( "title:*Matrix* AND year:1999" );
Caution
Compound queries cant search across committed index entries and those who havent got
committed yet at the same time.
34.11.5.Default operator
The default operator (that is whether AND or OR is used in between different terms) in a query is OR.
Changing that behavior is also done via the QueryContext <http://components.neo4j.org/neo4j-luceneindex/2.0.0/apidocs/org/neo4j/index/lucene/QueryContext.html> class:
QueryContext query = new QueryContext( "title:*Matrix* year:1999" )
.defaultOperator( Operator.AND );
hits = movies.query( query );
34.11.6.Caching
If your index lookups becomes a performance bottle neck, caching can be enabled for certain keys in
certain indexes (key locations) to speed up get requests. The caching is implemented with an LRU
<http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used> cache so that only the most
recently accessed results are cached (with "results" meaning a query result of a get request, not a
single entity). You can control the size of the cache (the maximum number of results) per index key.
Index<Node> index = graphDb.index().forNodes( "actors" );
((LuceneIndex<Node>) index).setCacheCapacity( "name", 300000 );
Caution
This setting is not persisted after shutting down the database. This means: set this value
after each startup of the database if you want to keep it.
568
Legacy Indexing
34.12.Automatic Indexing
Neo4j provides a single index for nodes and one for relationships in each database that automatically
follow property values as they are added, deleted and changed on database primitives. This
functionality is called auto indexing and is controlled both from the database configuration Map and
through its own API.
34.12.1.Configuration
By default Auto Indexing is off for both Nodes and Relationships. To configure this in the
neo4j.properties file, use the configuration keys node_auto_indexing and relationship_auto_indexing.
For embedded mode, use the configuration options GraphDatabaseSettings.node_auto_indexing and
GraphDatabaseSettings.relationship_auto_indexing. In both cases, set the value to true. This will
enable automatic indexing on startup. Just note that were not done yet, see below!
To actually auto index something, you have to set which properties should get indexed. You do
this by listing the property keys to index on. In the configuration file, use the node_keys_indexable
and relationship_keys_indexable configuration keys. When using embedded mode, use the
GraphDatabaseSettings.node_keys_indexable and GraphDatabaseSettings.relationship_keys_indexable
configuration keys. In all cases, the value should be a comma separated list of property keys to index
on.
When coding in Java, its done like this:
/*
* Creating the configuration, adding nodeProp1 and nodeProp2 as
* auto indexed properties for Nodes and relProp1 and relProp2 as
* auto indexed properties for Relationships. Only those will be
* indexed. We also have to enable auto indexing for both these
* primitives explicitly.
*/
GraphDatabaseService graphDb = new GraphDatabaseFactory().
newEmbeddedDatabaseBuilder( storeDirectory ).
setConfig( GraphDatabaseSettings.node_keys_indexable, "nodeProp1,nodeProp2" ).
setConfig( GraphDatabaseSettings.relationship_keys_indexable, "relProp1,relProp2" ).
setConfig( GraphDatabaseSettings.node_auto_indexing, "true" ).
setConfig( GraphDatabaseSettings.relationship_auto_indexing, "true" ).
newGraphDatabase();
Node node1 = null, node2 = null;
Relationship rel = null;
try ( Transaction tx = graphDb.beginTx() )
{
// Create the primitives
node1 = graphDb.createNode();
node2 = graphDb.createNode();
rel = node1.createRelationshipTo( node2,
DynamicRelationshipType.withName( "DYNAMIC" ) );
// Add indexable and non-indexable properties
node1.setProperty( "nodeProp1", "nodeProp1Value" );
node2.setProperty( "nodeProp2", "nodeProp2Value" );
node1.setProperty( "nonIndexed", "nodeProp2NonIndexedValue" );
rel.setProperty( "relProp1", "relProp1Value" );
rel.setProperty( "relPropNonIndexed", "relPropValueNonIndexed" );
// Make things persistent
tx.success();
}
569
Legacy Indexing
34.12.2.Search
The usefulness of the auto indexing functionality comes of course from the ability to actually query
the index and retrieve results. To that end, you can acquire a ReadableIndex object from the AutoIndexer
that exposes all the query and get methods of a full Index <http://components.neo4j.org/neo4j/2.0.0/
apidocs/org/neo4j/graphdb/index/Index.html> with exactly the same functionality. Continuing from
the previous example, accessing the index is done like this:
try ( Transaction tx = graphDb.beginTx() )
{
// Get the Node auto index
ReadableIndex<Node> autoNodeIndex = graphDb.index()
.getNodeAutoIndexer()
.getAutoIndex();
// node1 and node2 both had auto indexed properties, get them
assertEquals( node1,
autoNodeIndex.get( "nodeProp1", "nodeProp1Value" ).getSingle() );
assertEquals( node2,
autoNodeIndex.get( "nodeProp2", "nodeProp2Value" ).getSingle() );
// node2 also had a property that should be ignored.
assertFalse( autoNodeIndex.get( "nonIndexed",
"nodeProp2NonIndexedValue" ).hasNext() );
// Get the relationship auto index
ReadableIndex<Relationship> autoRelIndex = graphDb.index()
.getRelationshipAutoIndexer()
.getAutoIndex();
// One property was set for auto indexing
assertEquals( rel,
autoRelIndex.get( "relProp1", "relProp1Value" ).getSingle() );
// The rest should be ignored
assertFalse( autoRelIndex.get( "relPropNonIndexed",
"relPropValueNonIndexed" ).hasNext() );
}
34.12.3.Runtime Configuration
The same options that are available during database creation via the configuration can also be set
during runtime via the AutoIndexer API.
Gaining access to the AutoIndexer API and adding two Node and one Relationship properties to auto
index is done like so:
// Start without any configuration
GraphDatabaseService graphDb = new GraphDatabaseFactory().
newEmbeddedDatabase( storeDirectory );
// Get the Node AutoIndexer, set nodeProp1 and nodeProp2 as auto
// indexed.
AutoIndexer<Node> nodeAutoIndexer = graphDb.index()
.getNodeAutoIndexer();
nodeAutoIndexer.startAutoIndexingProperty( "nodeProp1" );
nodeAutoIndexer.startAutoIndexingProperty( "nodeProp2" );
// Get the Relationship AutoIndexer
AutoIndexer<Relationship> relAutoIndexer = graphDb.index()
.getRelationshipAutoIndexer();
relAutoIndexer.startAutoIndexingProperty( "relProp1" );
// None of the AutoIndexers are enabled so far. Do that now
nodeAutoIndexer.setEnabled( true );
relAutoIndexer.setEnabled( true );
570
Legacy Indexing
Note
Parameters to the AutoIndexers passed through the Configuration and settings made
through the API are cumulative. So you can set some beforehand known settings, do
runtime checks to augment the initial configuration and then enable the desired auto
indexers - the final configuration is the same regardless of the method used to reach it.
);
);
);
);
571
Legacy Indexing
node2.setProperty( "nodeProp2", "nodeProp2Value2" );
/*
* remove node4 property nodeProp2 from index.
*/
node4.removeProperty( "nodeProp2" );
// Make things persistent
tx.success();
}
try ( Transaction tx = graphDb.beginTx() )
{
// Verify
ReadableIndex<Node> nodeAutoIndex = nodeAutoIndexer.getAutoIndex();
// node1 is completely gone
assertFalse( nodeAutoIndex.get( "nodeProp1", "nodeProp1Value" ).hasNext() );
assertFalse( nodeAutoIndex.get( "nodeProp1", "nodeProp1Value2" ).hasNext() );
// node2 is updated
assertFalse( nodeAutoIndex.get( "nodeProp2", "nodeProp2Value" ).hasNext() );
assertEquals( node2,
nodeAutoIndex.get( "nodeProp2", "nodeProp2Value2" ).getSingle() );
/*
* node3 is still there, despite its nodeProp1 property not being monitored
* any more because it was not touched, in contrast with node1.
*/
assertEquals( node3,
nodeAutoIndex.get( "nodeProp1", "nodeProp3Value" ).getSingle() );
// Finally, node4 is removed because the property was removed.
assertFalse( nodeAutoIndex.get( "nodeProp2", "nodeProp4Value" ).hasNext() );
}
Caution
If you start the database with auto indexing enabled but different auto indexed properties
than the last run, then already auto-indexed properties will be deleted from the index when
a value is written to them (assuming the property isnt present in the new configuration).
Make sure that the monitored set is what you want before enabling the functionality.
572
Chapter35.Batch Insertion
Neo4j has a batch insertion facility intended for initial imports, which bypasses transactions and other
checks in favor of performance. This is useful when you have a big dataset that needs to be loaded
once.
Batch insertion is included in the neo4j-kernel <http://search.maven.org/#search|ga|1|neo4j-kernel>
component, which is part of all Neo4j distributions and editions.
Be aware of the following points when using batch insertion:
Warning
Always perform batch insertion in a single thread (or use synchronization to make only
one thread at a time access the batch inserter) and invoke shutdown when finished.
573
Batch Insertion
Tip
To gain good performance you probably want to set some configuration settings for the batch inserter.
Read Section22.11.3, Batch insert example for information on configuring a batch inserter. This is
how to start a batch inserter with configuration options:
Map<String, String> config = new HashMap<>();
config.put( "neostore.nodestore.db.mapped_memory", "90M" );
BatchInserter inserter = BatchInserters.inserter(
"target/batchinserter-example-config", fileSystem, config );
// Insert data here ... and then shut down:
inserter.shutdown();
In case you have stored the configuration in a file, you can load it like this:
try ( InputStream input = fileSystem.openAsInputStream( new File( "target/batchinsert-config" ) ) )
{
Map<String, String> config = MapUtil.load( input );
BatchInserter inserter = BatchInserters.inserter(
"target/batchinserter-example-config", fileSystem, config );
// Insert data here ... and then shut down:
inserter.shutdown();
}
574
Batch Insertion
Note
This will not perform as good as using the BatchInserter API directly.
Also be aware of the following:
Starting a transaction or invoking Transaction.finish()/close() or Transaction.success() will do
nothing.
Invoking the Transaction.failure() method will generate a NotInTransaction exception.
Node.delete() and Node.traverse() are not supported.
Relationship.delete() is not supported.
Event handlers and indexes are not supported.
GraphDatabaseService.getRelationshipTypes(), getAllNodes() and getAllRelationships() are not
supported.
With these precautions in mind, this is how to do it:
GraphDatabaseService batchDb =
BatchInserters.batchDatabase( "target/batchdb-example", fileSystem );
Label personLabel = DynamicLabel.label( "Person" );
Node mattiasNode = batchDb.createNode( personLabel );
mattiasNode.setProperty( "name", "Mattias" );
Node chrisNode = batchDb.createNode();
chrisNode.setProperty( "name", "Chris" );
chrisNode.addLabel( personLabel );
RelationshipType knows = DynamicRelationshipType.withName( "KNOWS" );
mattiasNode.createRelationshipTo( chrisNode, knows );
batchDb.shutdown();
Tip
575
Batch Insertion
The configuration parameters are the same as mentioned in Section34.10, Configuration and fulltext
indexes.
35.3.1.Best practices
Here are some pointers to get the most performance out of BatchInserterIndex:
Try to avoid flushing <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/unsafe/
batchinsert/BatchInserterIndex.html#flush%28%29> too often because each flush will result in all
additions (since last flush) to be visible to the querying methods, and publishing those changes can
be a performance penalty.
Have (as big as possible) phases where one phase is either only writes or only reads, and dont
forget to flush after a write phase so that those changes becomes visible to the querying methods.
Enable caching <http://components.neo4j.org/neo4j/2.0.0/apidocs/org/neo4j/unsafe/batchinsert/
BatchInserterIndex.html#setCacheCapacity%28java.lang.String,%20int%29> for keys you know
youre going to do lookups for later on to increase performance significantly (though insertion
performance may degrade slightly).
Note
Changes to the index are available for reading first after they are flushed to disk. Thus,
for optimal performance, read and lookup operations should be kept to a minimum during
batchinsertion since they involve IO and impact speed negatively.
576
AppendixA.Manpages
The Neo4j Unix manual pages are included on the following pages.
neo4j
neo4j-installer
neo4j-shell
neo4j-backup
neo4j-arbiter
577
Manpages
Name
neo4j Neo4j Server control
Synopsis
neo4j <command>
DESCRIPTION
Neo4j is a graph database, perfect for working with highly connected data. The neo4j command is
used to control the Neo4j Server.
The preferred way to install Neo4j on Linux systems is by using prebuilt installation packages, but
theres also the possibility to use the neo4j-installer command to install or remove it as a system
service. For information regarding Windows, see below.
COMMANDS
console
Start the server as an application, running as a foreground process. Stop the server using CTRL-C.
start
Start server as daemon, running as a background process.
stop
Stops a running daemonized server.
restart
Restarts the server.
status
Current running state of the server.
info
Displays configuration information, such as the current NEO4J_HOME and CLASSPATH.
Usage - Windows
Neo4j.bat
Double-clicking on the Neo4j.bat script will start the server in a console. To quit, just press control-C
in the console window.
For installing the Neo4j Server as a service, use the Neo4jInstaller.bat command.
Neo4j.bat start - will start the Neo4j service
will start the Neo4j service if installed or a console
session otherwise.
Neo4j.bat stop - stop the Neo4j service if running
Neo4j.bat restart - restart the Neo4j service if installed
Neo4j.bat status - report on the status of the Neo4j service
returns RUNNING, STOPPED or NOT INSTALLED
FILES
conf/neo4j-server.properties
Server configuration.
578
Manpages
conf/neo4j-wrapper.conf
Configuration for service wrapper.
conf/neo4j.properties
Tuning configuration for the database.
579
Manpages
Name
neo4j-installer Neo4j Server installation and removal
Synopsis
neo4j-installer <command>
DESCRIPTION
Neo4j is a graph database, perfect for working with highly connected data.
The preferred way to install Neo4j on Linux systems is by using prebuilt installation packages, but
theres also the possibility to use the neo4j-installer command to install or remove it as a system
service. For information regarding Windows, see below.
Use the neo4j command to control the Neo4j Server.
COMMANDS
install
Installs the server as a platform-appropriate system service.
remove
Uninstalls the system service.
Usage - Windows
To just control the Neo4j Server, use the Neo4j.bat command.
Neo4jInstaller.bat install/remove
Neo4j can be installed and run as a Windows Service, running without a console window. Youll need
to run the scripts with Administrator privileges. Just use the Neo4jInstaller.bat script with the proper
argument:
Neo4jInstaller.bat install - install as a Windows service
will install the service
Neo4jInstaller.bat remove - remove the Neo4j service
will stop and remove the Neo4j service
FILES
conf/neo4j-server.properties
Server configuration.
conf/neo4j-wrapper.conf
Configuration for service wrapper.
conf/neo4j.properties
Tuning configuration for the database.
580
Manpages
Name
neo4j-shell a command-line tool for exploring and manipulating a graph database
Synopsis
neo4j-shell [REMOTE OPTIONS]
neo4j-shell [LOCAL OPTIONS]
DESCRIPTION
Neo4j shell is a command-line shell for running Cypher queries. Theres also commands to get
information about the database. In addition, you can browse the graph, much like how the Unix shell
along with commands like cd, ls and pwd can be used to browse your local file system. The shell can
connect directly to a graph database on the file system. To access local a local database used by other
processes, use the readonly mode.
REMOTE OPTIONS
-port PORT
Port of host to connect to (default: 1337).
-host HOST
Domain name or IP of host to connect to (default: localhost).
-name NAME
RMI name, i.e. rmi://<host>:<port>/<name> (default: shell).
-readonly
Access the database in read-only mode.
LOCAL OPTIONS
-path PATH
The path to the database directory. If there is no database at the location, a new one will e created.
-pid PID
Process ID to connect to.
-readonly
Access the database in read-only mode.
-c COMMAND
Command line to execute. After executing it the shell exits.
-file FILE
File to read and execute. After executing it the shell exits. If - is supplied as filename data is read
from stdin instead.
-config CONFIG
The path to the Neo4j configuration file to be used.
EXAMPLES
Examples for remote:
neo4j-shell
neo4j-shell -port 1337
neo4j-shell -host 192.168.1.234 -port 1337 -name shell
neo4j-shell -host localhost -readonly
581
Manpages
Examples for local:
neo4j-shell -path /path/to/db
neo4j-shell -path /path/to/db -config /path/to/neo4j.properties
neo4j-shell -path /path/to/db -readonly
582
Manpages
Name
neo4j-backup Neo4j Backup Tool
Synopsis
neo4j-backup -from SourceURI -to Directory
DESCRIPTION
A tool to perform live backups over the network from a running Neo4j graph database onto a local
filesystem. Backups can be either full or incremental. The first backup must be a full backup, after that
incremental backups can be performed.
The source(s) are given as URIs in a special format, the target is a filesystem location.
BACKUP TYPE
-full
copies the entire database to a directory.
-incremental
copies the changes that have taken place since the last full or incremental backup to an existing
backup store.
The backup tool will automatically detect whether it needs to do a full or an incremental backup.
SOURCE URI
Backup sources are given in the following format:
<running mode>://<host>[:<port>][,<host>[:<port>]]
Note that multiple hosts can be defined.
running mode
'single' or 'ha'. 'ha' is for instances in High Availability mode, 'single' is for standalone databases.
host
In single mode, the host of a source database; in ha mode, the cluster address of a cluster member.
Note that multiple hosts can be given when using High Availability mode.
port
In single mode, the port of a source database backup service; in ha mode, the port of a cluster
instance. If not given, the default value 6362 will be used for single mode, 5001 for HA
IMPORTANT
Backups can only be performed on databases which have the configuration parameter
enable_online_backup=true set. That will make the backup service available on the default port (6362).
To enable the backup service on a different port use for example enable_online_backup=port=9999
instead.
Usage - Windows
The Neo4jBackup.bat script is used in the same way.
EXAMPLES
Performing a backup the first time: create a blank directory and run the backup tool
583
Manpages
mkdir /mnt/backup/neo4j-backup
neo4j-backup -from single://192.168.1.34 -to /mnt/backup/neo4j-backup
Subsequent backups using the same _target_-directory will be incremental and therefore quick
neo4j-backup -from single://freja -to /mnt/backup/neo4j-backup
Performing a backup where the service is registered on a custom port
neo4j-backup -from single://freja:9999 -to /mnt/backup/neo4j-backup
Performing a backup from HA cluster, specifying only one cluster member
./neo4j-backup -from ha://oden:5002 -to /mnt/backup/neo4j-backup
Performing a backup from HA cluster, specifying two cluster members
./neo4j-backup -from ha://oden:5001,loke:5002 -to /mnt/backup/neo4j-backup
584
Manpages
Name
neo4j-arbiter Neo4j Arbiter for High-Availability clusters
Synopsis
neo4j-arbiter <command>
DESCRIPTION
Neo4j Arbiter is a service that can help break ties in Neo4j clusters that have an even number of
cluster members.
COMMANDS
console
Start the server as an application, running as a foreground process. Stop the server using CTRL-C.
start
Start server as daemon, running as a background process.
stop
Stops a running daemonized server.
restart
Restarts a running server.
status
Current running state of the server
install
Installs the server as a platform-appropriate system service.
remove
Uninstalls the system service
FILES
conf/arbiter.cfg
Arbiter server configuration.
conf/arbiter-wrapper.cfg
Configuration for service wrapper.
585