Graph NoSQL Databases with Neo4j
Graph NoSQL Databases with Neo4j
20ITEL609 L T P C
SDG NO. 4, 9, 11 NoSQL DATABASE TECHNIQUES
3 0 0 3
& 12
1
Figure: An example graph structure
• We also see that edges have types, such as likes, author, and so on.
• These properties let us organize the nodes; for example, the nodes Martin and
Pramod have an edge connecting them with a relationship type of friend.
• Edges can have multiple properties. We can assign a property of since on the
friend relationship type between Martin and Pramod.
• Relationship types have directional significance; the friend relationship type
is bidirectional but likes is not.
• When Dawn likes NoSQL Distilled, it does not automatically mean NoSQL
Distilled likes Dawn.
• Once we have a graph of these nodes and edges created, we can query the
graph in many ways, such as “get all nodes employed by Big Co that like
NoSQL Distilled.”
• A query on the graph is also known as traversing the graph. An advantage of
the graph databases is that we can change the traversing requirements without
having to change the nodes or edges.
• If we want to “get all nodes that like NoSQL Distilled,” we can do so without
having to change the existing data or the model of the database, because we
can traverse the graph any way we like.
• Usually, when we store a graph-like structure in RDBMS, it’s for a single
type of relationship (“who is my manager” is a common example).
• Adding another relationship to the mix usually means a lot of schema
changes and data movement, which is not the case when we are using
graph databases.
2
• Similarly, in relational databases we model the graph beforehand based
on the Traversal we want; if the Traversal changes, the data will have to
change.
• In graph databases, traversing the joins or relationships is very fast.
• The relationship between nodes is not calculated at query time but is actually
persisted as a relationship.
• Traversing persisted relationships is faster than calculating them for every
query.
• Nodes can have different types of relationships between them.
• Since there is no limit to the number and kind of relationships a node can
have, all they can be represented in the same graph database.
The Neo4j browser is a graphical user interface (GUI) that can be run through
a web browser. The Neo4j browser can be used for adding data,
running queries, creating relationships, and more. It also provides an easy
way to visualize the data in the database.
• The relationship between nodes is not calculated at query time but is actually
persisted as a relationship.
• Traversing persisted relationships is faster than calculating them for every
query.
• Nodes can have different types of relationships between them.
• Since there is no limit to the number and kind of relationships a node can
have, all they can be represented in the same graph database.
3
Overview of the Neo4j browser interface
Editor:
This is where you enter queries and commands. For example, to create or
retrieve data. You can get help at any time by entering :help and pressing enter
(or clicking the "Run" arrow to the right of the Editor).
Stream:
This is where the results of your queries appear. Each result has its own
frame. Each frame appears above the previous. So this enables you to scroll
down and view the results of a previous query if needed. You can clear the
Stream at any time by using the command :clear.
4
Sidebar
The Sidebar has various options, such as viewing the details of your
database, viewing/changing Neo4j Browser Settings, viewing Neo4j
Documentation, and more. Clicking on an option results in a wider
sidebar sliding open, with details about that option.
For example, clicking the "Database" icon opens details about the
database.
5
Neo4j Query Language - Cypher
• Neo4j has its own query language called Cypher. Cypher uses a similar syntax
to SQL (Structured Query Language).
Example
MATCH (p:Person { name:"Homer Flinstone" })
RETURN p
This Cypher statement returns a "Person" node where the name property is
"Homer Flinstone".
If this was SQL querying a relational database, it might look more like this:
SELECT * FROM Person
WHERE name = "Homer Flinstone";
• However, remember, Neo4j doesn't store its data in tables like the relational
database model. It's all in nodes and relationships. So the Cypher query above
is querying nodes, their labels, and their properties. The SQL example on the
other hand, is querying tables, rows, and columns.
• SQL was designed to be used with relational database management systems
(DBMS). Neo4j is a NoSQL DBMS, in that it doesn't use the relational model
and it doesn't use SQL.
6
• Cypher was designed specifically for working with the Neo4j data model,
which is all about nodes and their relationships with each other.
ASCII-Art Syntax
• Cypher uses ASCII-Art to represent patterns. This is a handy thing to
remember when first learning the language. If you forget how to write
something, just visualise how the graph will look and it should help.
(a)-[:KNOWS]->(b)
Here are some more points to remember when working with Cypher:
• Nodes usually have labels. Examples could include "Person", "User",
"Actor", "Employee", "Customer".
• Nodes usually have properties. Properties provide extra information about
the node. Examples could include "Name", "Age", "Born", etc
• Relationships can also have properties.
• Relationships usually have a type (this is basically like a node's label).
Examples could include "KNOWS", "LIKES", "WORKS_FOR",
"PURCHASED", etc.
7
• Let's create a music database that contains band names and their albums.
• The first band will be called Strapping Young Lad. So we will create an Artist
node and call it Strapping Young Lad.
• Our first node will look something like this. Note that the name is cut short
only because it's too long to be displayed on the node. The full name is still
stored in the database.
Once Neo4j has created the node, you should see a message like this:
8
• The above statement creates a node with an Album label. It has two
properties: Name and Released.
• Note that we return the node by using its variable name (in this case b).
CREATE (a)-[r:RELEASED]->(b)
RETURN r
This graph shows that Devin Townsend plays in the band, performed on the
album that the band released, and he also produced the album.
So let's start by creating the node for Devin Townsend:
CREATE (p:Person { Name: "Devin Townsend" })
10
CREATE (p)-[pr:PRODUCED]->(b), (p)-[pf:PERFORMED_ON]-
>(b), (p)-[pl:PLAYS_IN]->(a)
RETURN a,b,p
11
Index Hints
• Once an index has been created, it will automatically be used when you
perform relevant queries.
• However, Neo4j also allows you to enforce one or more indexes with a hint.
You can create an index hint by including USING INDEX ... in your query.
• So we could enforce the above index as follows:
Constraint Types
• In Neo4j, you can create uniqueness constraints and property existence
constraints.
• Uniqueness Constraint
Specifies that the property must contain a unique value (i.e. no two nodes
with an Artist label can share a value for the Name property.)
• Property Existence Constraint
Ensures that a property exists for all nodes with a specific label or for all
relationships with a specific type. Property existence constraints are only
available in the Neo4j Enterprise Edition.
12
View the Constraint
• Constraints (and indexes) become part of the (optional) database schema. We
can view the constraint we just created by using the :schema command. Like
this: :schema
• You will see the newly created constraint, as well as the index that was created
with it. We can also see the index that was created previously:
13
• Property existence constraints can be used to ensure all nodes with a certain
label have a certain property. For example, you could specify that all nodes
labelled withArtist must contain a Name property.
• To create a property existence constraint, use theASSERT
exists([Link]) syntax.
Like this:
Retrieve a Node
Example:
MATCH (p:Person)
WHERE [Link] = "Devin Townsend"
RETURN p
The WHERE clause works the same way as SQL's WHERE clause,
in that it allows you to narrow down the results by providing extra criteria.
However, you can achieve the same result without using a WHERE
clause. You can also search for a node by providing the same notation
you used to create the node.
The following code provides the same results as the above statement:
• Running either of the above queries will result in the following node being
displayed:
14
You may have noticed that clicking on a node expands an outer circle separated
into three sections — each representing a different option:
15
Relationships
You can also traverse relationships with the MATCH statement. In fact, this
is one of the things Neo4j is really good at.
For example, if we wanted to find out which artist released the album called
Heavy as a Really Heavy Thing, we could use the following query:
MATCH (a:Artist)-[:RELEASED]->(b:Album)
WHERE [Link] = "Heavy as a Really Heavy Thing"
RETURN a
This will return the following node:
• You can see that the pattern we use in the MATCH statement is almost self-
explanatory. It matches all artists that released an album that had a name of
Heavy as a Really Heavy Thing.
• We use variables (i.e. a and b) so that we can refer to them later in the
query. We didn't provide any variables for the relationship, as we didn't need to
refer to the relationship later in the query.
• You might also notice that the first line uses the same pattern that we used
to create the relationship in the first place. This highlights the simplicity of the
Cypher language. We can use the same patterns in different contexts (i.e. to
create data and to retrieve data).
16
• You can also click on the Rows icon on the side to display the data in row format:
17
Limit the Results
Use LIMIT to limit the number of records in the output. It's a good idea to use
this when you're not sure how big the result set is going to be.
So we could simply append LIMIT 5 to the previous statement to limit the output
to 5 records:
MATCH (n) RETURN n
LIMIT 5
Running the above statement should produce the following success message:
18
You can follow that up with a query to see the newly created nodes:
MATCH (n:Genre) RETURN n
Which should result in the nodes scattered around the data visualization frame:
19
Neo4j browser and it should import directly into your database (assuming you
are connected to the Internet).
• You can also download the file here: [Link]
20
Custom Field Delimiter
You can specify a custom field delimiter if required. For example, you could
specify a semi-colon instead of a comma if that's how the CSV file is
formatted.
To do this, simply add the FIELDTERMINATOR clause to the statement. Like
this:
LOAD CSVWITH HEADERS FROM
'[Link] AS line
FIELDTERMINATOR ';'
CREATE (:Track { TrackId: [Link], Name: [Link], Length: [Link]})
21
View the Schema
You can now use the :schema command to verify that the applicable index has
been removed from the schema.
Simply type this:
:schema
You will see that the index is no longer in the schema:
22
:schema
You will see that the index is no longer in the schema:
23
This error message is telling us that we have to delete any relationships before we
delete the node.
24
Let's delete the relationship of type RELEASED. There are several ways we could
go about this. Let's look at three.
The following statement is quite broad — it will delete all relationships
of type RELEASED:
MATCH ()-[r:RELEASED]-()
DELETE r
You could also be more specific and write something like this:
MATCH (:Artist)-[r:RELEASED]-(:Album)
DELETE r
The above statement will match all Artist nodes that have a relationship type of
RELEASED with an Album node.
25
This is because that node has a relationship connected.
One option is to delete all relationships, then delete the node.
26
• Count( )
Count the employees whose salary is greater than 25000.
MATCH (n:employee)
where [Link]>25000
return count(n)
• Relationship
create (Dhawan: Player{name: “Shikhar Dhawan”, YOB:1985,
POB: “ Delhi”})
create (Ind: Country {name: “India”})
create (Dhawan)-[r:Batsman_of]->(Ind)
return Dhawan, Ind
• Creating relationship with label and properties
MATCH (a:Player),(b:Country) where [Link]=“Shikhar Dhawan” and
[Link]=“India”
create(a)-[r:Batsman_of {matches:5,Avg:90.75}]->(b)
return a,b
• Creating a complete path
create p=(Dhawan{name:“Shikhar Dhawan”})-[:Topscorer_of]-> (Ind
{name:“India”})-[:winnner_of]->(CT2013{name:“Champions Trophy
2013”})
return p
• Set property
MATCH(Dhawan:Player{name: “Shikhar Dhawan”, YOB:1985, POB:
“Delhi”})
SET [Link]=187
return Dhawan
• Set multiple property
SET [Link]=187, [Link]=2
27
RETURN Jadeja
Consistency
• Since graph databases are operating on connected nodes, most graph database
solutions usually do not support distributing the nodes on different servers.
There are some solutions, however, that support node distribution across a
cluster of servers, such as Infinite Graph.
• Within a single server, data is always consistent, especially in Neo4J which is
fully ACID-compliant.
• When running Neo4Jin a cluster, a write to the master is eventually
synchronized to the slaves, while slaves are always available for read.
• Graph databases ensure consistency through transactions. They do not
allow dangling relationships: The start node and nd node always have to exist,
and nodes can only be deleted if they don’t have any relationships attached
to them.
Transactions
• Neo4J is ACID-compliant. Before changing any nodes or adding any
relationships to existing nodes, we have to start a transaction.
• Without wrapping operations in transactions, we will get a
NotInTransactionException. Read operations can be done without initiating a
transaction.
Availability
• Neo4J, as of version 1.8, achieves high availability by providing for
replicated slaves.
• These slaves can also handle writes: When they are written to, they
synchronize the write to the current master, and the write is committed first at
the master and then at the slave. Other slaves will eventually get the update.
• Other graph databases, such as Infinite Graph and FlockDB, provide for
distributed storage of the nodes.
• Neo4J uses the Apache ZooKeeper[ZooKeeper] to keep track of the last
transaction IDs persisted on each slave node and the current master node.
Once a server starts up, it communicates with ZooKeeper and finds out which
server is the master. If the server is the first one to join the cluster, it becomes
the master; when a master goes down, the cluster elects a master from the
available nodes, thus providing high availability.
Query Features
• Neo4J has the Cypher [Cypher] query language for querying the graph. Neo4J
allows you to query the graph for properties of the nodes, traverse the graph, or
navigate the nodes relationships using language bindings.
• Properties of a node can be indexed using the indexing service. Similarly,
properties of relationships or edges can be indexed, so a node or edge can be
found by the value.
• Indexes should be queried to find the starting node to begin a traversal. Let’s
look at searching for the node using node indexing.
29
• If we have the graph shown in Figure, we can index the nodes as they are
added to the database, or we can index all the nodes later by iterating over
them. We first need to create an index for the nodes using the IndexManager.
• Index<Node>nodeIndex = [Link]().forNodes("nodes");
• We are indexing the nodes for the name property. Neo4J uses Lucene
[Lucene] as its indexing service.
• We get the node whose name is Martin; given the node, we can get all its
relationships.
Node martin = [Link]("name",
"Martin").getSingle();
allRelationships = [Link]();
• We can get both INCOMING or OUTGOING relationships.
30
incomingRelations=
[Link]([Link]);
• We can also apply directional filters on the queries when querying for a
relationship.
• If we want to find all people who like NoSQL Distilled, we can find the
NoSQL Distilled node and then get its relationships with [Link].
• At this point we can also add the type of relationship to the query filter, since
we are looking only for nodes that LIKE NoSQL Distilled.
Node nosqlDistilled = [Link]("name", "NoSQL
Distilled").getSingle();
relationships = [Link](INCOMING,LIKES);
for (Relationship relationship : relationships) {
[Link]([Link]());
}
• Graph databases are really powerful when you want to traverse the graphs
at any depth and specify a starting node for the traversal. This is especially
useful when you are trying to find nodes that are related to the starting node
at more than one level down.
• As the depth of the graph increases, it makes more sense to traverse the
relationships by using a Traverser where you can specify that you are looking
for INCOMING, OUTGOING, or BOTH types of relationships.
• You can also make the traverser go top-down or sideways on the raph by using
Order values of BREADTH_FIRST or DEPTH_FIRST.
• The traversal has to start at some node—in this example, we try to find all the
nodes at any depth that are related as a FRIEND with Barbara:
Node barbara = [Link]("name",
"Barbara").getSingle();
Traverser friendsTraverser =
[Link](Order.BREADTH_FIRST,
StopEvaluator.END_OF_GRAPH,
ReturnableEvaluator.ALL_BUT_START_NODE,
[Link],
[Link]);
• The friendsTraverser provides us a way to find all the odes that are related
to Barbara where the relationship type is FRIEND.
• The nodes can be at any depth—friend of a friend at any level—allowing
you to explore tree structures.
31
• One of the good features of graph databases is finding paths between two
nodes—determining if there are multiple paths, finding all of the paths or the
shortest path.
• In the graph in Figure, we know that Barbara is connected to Jill by two distinct
paths; to find all these paths and the distance between Barbara and Jill along
those different paths, we can use
• This feature is used in social networks to show relationships between any two
nodes. To find all the paths and the distance between the nodes for each path, we
first get a list of distinct paths between the two nodes.
• The length of each path is the number of hops on the graph needed to reach the
destination node from the start node. Often, you need to get the shortest path
between two nodes; of the two paths from Barbara to Jill, the shortest path can be
found by using
PathFinder<Path> finder = [Link](
[Link](FRIEND,
[Link]) , MAX_DEPTH);
Iterable<Path> paths = [Link](barbara, jill);
• Many other graph algorithms can be applied to the graph at hand, such as
Dijkstra’s algorithm[Dijkstra’s] for finding the shortest or cheapest path between
nodes.
START beginingNode = (beginning node specification)
MATCH (relationship, pattern matches)
WHERE (filtering condition: on data in nodes and
relationships)
RETURN (What to return: nodes, relationships, properties)
ORDER BY (properties to order by)
SKIP (nodes to skip from top)
LIMIT (limit results)
Scaling
• In NoSQL databases, one of the commonly used scaling techniques is
sharding, where data is split and distributed across different servers. With graph
32
databases, sharding is difficult, as graph databases are not aggregate-oriented
but relationship-oriented.
• Since any given node can be related to any other node, storing related nodes
on the same server is better for graph traversal. Traversing a graph when
the nodes are on different machines is not good for performance. Knowing
this limitation of the graph databases, we can still scale them using some common
techniques.
• Generally speaking, there are three ways to scale graph databases. Since
machines now can come with lots of RAM, we can add enough RAM to the
server so that the working set of nodes and relationships is held entirely in
memory. This technique is only helpful if the dataset that we are working
with will fit in a realistic amount of RAM.
• We can improve the read scaling of the database by adding more slaves with
read-only access to the data, with all the writes going to the master. This pattern
of writing once and reading from many servers is a proven technique in
MySQL clusters and is really useful when the dataset is large enough
to not fit in a single machine’s RAM, but small enough to be replicated across
multiple machines.
• When the dataset size makes replication impractical, we can shard the data
from the application side using domain specific knowledge.
• For example, nodes that relate to the North America can be created on one
server while the nodes that relate to Asia on another.
• This application-level sharding needs to understand that nodes are stored on
physically different databases.
33
Suitable Use Cases
Connected Data
Social networks are where graph databases can be deployed and used very
effectively. These social graphs don’t have to be only of the friend kind; for
example, they can represent employees, their knowledge, and where they
worked with other employees on different projects. Any link-rich domain is
well suited for graph databases.
Recommendation Engines
As nodes and relationships are created in the system, they can be used to make
recommendations like “your friends also bought this product” or “when
invoicing this item, these other items are usually invoiced.”
35