Getting Started with Neo4J - Part 2


Bhaskar S 12/02/2017


Overview

In Part-1 of this series, we setup a docker instance for Neo4J and briefly explored the web-based user interface for Neo4J. In this part, we will get our hands dirty with the SQL like query language for Neo4J called Cypher.

Hands-on with Cypher

Just as SQL is the query and data manipulation language for the ralational databases, so is Cypher for Neo4J.

Cypher is a declarative language, in which, one indicates "WHAT" pattern one is looking for and the Neo4J figures the "HOW" to find and retrieve the data. As a result, one can express data patterns in a way that is natural to humans, making it easy to understand and maintain. Of course, there is a syntax to be followed that is native to Cypher.

The three most important aspects in Neo4J are the nodes, relationships, and properties.

The following are some of the basic syntax rules in Cypher:

Use the syntax () (parenthesis) to represent a node in Neo4J. The following are some usage patterns:

Use the syntax [] (square brackets) to represent a relationship in Neo4J. The following are some usage patterns:

Use the syntax {} (curly braces) to represent properties in Neo4J.

Use the syntax () -[]-> () to represent directional relationship from one node to another, where the syntax () on the left and right represent nodes and the syntax -[]-> represents the uni-directional relationship. Use the syntax -[]- (without the greater or arrow charater) to represent a bi-directional relationship.

To get started, we need some data (graph nodes and relationships) in the Neo4J database. We will create some graph elements (nodes and relationships) using the CREATE clause.

The following is the syntax for creating graph nodes:

    CREATE (n:L1:L2: ... :Lm {k1: v1, k2: v2, ... , kn:vn})

where, n is the name, L1, L2, through Lm are the labels, k1: v1, k2: v2, through kn: vn are the properties (key-value pairs).

The following is an example that creates a node that has the name Alice with the label User, and few properties such as name, uid, email, state, and type:

CREATE (Alice:User {name:'Alice Pink', uid: 'alice', email: 'alice.pink@earth.com', state: 'nj', type: 'employee'})

The following is another example that creates a node that has the name ProjectManagement with the label Group, and one property called name:

CREATE (ProjectManagement:Group {name:'Project Management'})

The following is the syntax for creating a graph relationship between two nodes:

    CREATE (n1) -[:R {k1: v1, k2: v2, ... , kn:vn}]-> (n2)

where, n1 & n2 are the nodes, k1: v1, k2: v2, through kn: vn are the properties (key-value pairs) of the relationship and R is the relationship type.

The following is an example that creates a uni-directional relationship between the node Alice and the node ProjectManagement with the relationship type BELONGS_TO with one property called context:

CREATE (Alice) -[:BELONGS_TO {context: 'pm'}]-> (ProjectManagement)

Now that we know the syntax for the creation of graph nodes and relationships using the CREATE clause, we will create some sample data using the Neo4J web browser interface.

The following is the set of sample nodes and relationships between them:

CREATE clauses
// Create User Nodes
CREATE (Alice:User {name:'Alice Pink', uid: 'alice', email: 'alice.pink@earth.com', state: 'nj', type: 'employee'})
CREATE (Bob:User {name:'Bob Green', uid: 'bob', email: 'bob.green@earth.com', state: 'ny', type: 'employee'})
CREATE (Charlie:User {name:'Charlie Brown', uid: 'charlie', email: 'charlie.brown@earth.com', state: 'nj', type: 'consultant'})
CREATE (David:User {name:'David Black', uid: 'david', email: 'david.black@earth.com', state: 'tx', type: 'employee'})
CREATE (Frank:User {name:'Frank Grey', uid: 'frank', email: 'frank.grey@earth.com', state: 'ny', type: 'employee'})
CREATE (Gary:User {name:'Gary White', uid: 'gary', email: 'gary.white@earth.com', state: 'tx', type: 'consultant'})
CREATE (Harry:User {name:'Harry Blue', uid: 'harry', email: 'harry.blue@earth.com', state: 'nj', type: 'consultant'})

// Create Group Nodes
CREATE (ProjectManagement:Group {name:'Project Management'})
CREATE (CoreEngineering:Group {name:'Core Engineering'})

// Create User to Group Relationships
CREATE (Alice) -[:BELONGS_TO {context: 'pm'}]-> (ProjectManagement)
CREATE (Bob) -[:BELONGS_TO {context: 'pm'}]-> (ProjectManagement)
CREATE (Bob) -[:BELONGS_TO {context: 'architect'}]-> (CoreEngineering)
CREATE (Charlie) -[:BELONGS_TO {context: 'pm'}]-> (ProjectManagement)
CREATE (David) -[:BELONGS_TO {context: 'developer'}]-> (CoreEngineering)
CREATE (Frank) -[:BELONGS_TO {context: 'developer'}]-> (CoreEngineering)
CREATE (Gary) -[:BELONGS_TO {context: 'pm'}]-> (ProjectManagement)
CREATE (Harry) -[:BELONGS_TO {context: 'architect'}]-> (CoreEngineering)

Fire up a web-browser, open the URL http://localhost:7474, and login to the Neo4J web interface. Enter the above CREATE clauses in the top command box (thats has the $ prompt) as shown in the illustration below:

Command Box
Command Box

After entering the CREATE clauses in the command box of the Neo4J web interface, click on the play button located on the right side as shown in the illustration below:

Execute Commands
Execute Commands

This action will create the necessary nodes and relationships in the Neo4J database and the web interface should display a result window as shown in the illustration below:

Execute Results
Execute Results

Clicking on the database icon located on the left side in the Neo4J web interface should display results like the one shown in the illustration below:

Database Info
Database Info

As can be seen from the illustration above, we have nodes with two types of labels Group and User, one relationship type BELONGS_TO, and few property keys such as context, email, name, state, type, and uid in the Neo4J database.

To query the graph elements (nodes and relationships) in the Neo4J database, one must use the MATCH clause.

To query all the nodes (irrepective of the labels), use the following MATCH pattern:

    MATCH (n) RETURN n;

where, n is an identifier for the nodes.

Enter the above MATCH statement in the command box of the Neo4J web interface, click on the play button located on the right side as shown in the illustration below:

MATCH All
MATCH All

This action will pattern match all the nodes in the Neo4J database and the web interface should display a result window as shown in the illustration below:

All Results
All Results

In the following paragraphs, we will not show the illustrations for entering the MATCH statements in the command box of the Neo4J web interface. Instead, we will indicate the text for the MATCH statement and just show the illustration for the results.

To query all the nodes, with the label Group, use the following MATCH pattern:

    MATCH (n:Group) RETURN n;

The result of the pattern match of all the nodes with the label Group in the web interface should display a result window as shown in the illustration below:

Label Group
Label Group

To query all the nodes, with the label User and with the property key state equals nj, use one of the following MATCH patterns:

    MATCH (n:User) WHERE n.state = 'nj' RETURN n;

        OR

    MATCH (n:User {state: 'nj'}) RETURN n;

The result of the pattern match of all the nodes with the label User and with the property key state equals nj in the web interface should display a result window as shown in the illustration below:

State Key
State Key

To query all the nodes, with the label User and with the property key state equals nj or ny, use the following MATCH pattern:

    MATCH (n:User) WHERE n.state IN ['nj', 'ny'] RETURN n;

The result of the pattern match of all the nodes with the label User and with the property key state equals nj or ny in the web interface should display a result window as shown in the illustration below:

State NJ/NY
State NJ/NY

One may be wondering if there is any command-line interface (like in the relational databases) for working with Neo4J database. Indeed, there is one called cypher-shell and in the following paragraphs, we will switch to the command-line interface, instead of the web interface.

Open a terminal window and execute the following docker command to launch the cypher-shell:

docker exec -ti neo4j bin/cypher-shell -u neo4j

One will be prompted for a password and on success, the terminal should look as shown in the illustration below:

Cypher Shell
Cypher Shell

To query the property values of the keys name and email for all the nodes with the label User, that is related to another node with the label Group that has a property key name with the value Project Management, execute the following MATCH query:

MATCH (u:User) -[]-> (g:Group {name: 'Project Management'}) RETURN u.name, u.email;

On executing the above query, the results should look like the one shown below:

Output.1

+---------------------------------------------+
| u.name          | u.email                   |
+---------------------------------------------+
| "Gary White"    | "gary.white@earth.com"    |
| "Charlie Brown" | "charlie.brown@earth.com" |
| "Bob Green"     | "bob.green@earth.com"     |
| "Alice Pink"    | "alice.pink@earth.com"    |
+---------------------------------------------+

4 rows available after 27 ms, consumed after another 1 ms

To query the property values of the keys name and type for all the nodes with the label User, that is related to another node through the relationship type BELONGS_TO with the property key context with value architect, execute the following MATCH query:

MATCH (u:User) -[:BELONGS_TO {context: 'architect'}]-> () RETURN u.name, u.type;

On executing the above query, the results should look like the one shown below:

Output.2

+-----------------------------+
| u.name       | u.type       |
+-----------------------------+
| "Bob Green"  | "employee"   |
| "Harry Blue" | "consultant" |
+-----------------------------+

2 rows available after 18 ms, consumed after another 1 ms

To query the property values of the keys name and uid for all the nodes with the label User and limit the output to just 5 rows, execute the following MATCH query:

MATCH (u:User) RETURN u.name, u.uid LIMIT 5;

On executing the above query, the results should look like the one shown below:

Output.3

+-----------------------------+
| u.name          | u.uid     |
+-----------------------------+
| "Alice Pink"    | "alice"   |
| "Bob Green"     | "bob"     |
| "Charlie Brown" | "charlie" |
| "David Black"   | "david"   |
| "Frank Grey"    | "frank"   |
+-----------------------------+

5 rows available after 12 ms, consumed after another 0 ms

To query and return unique values of the property key type for all the nodes with the label User, that is related to another node through the relationship type BELONGS_TO, execute the following MATCH query:

MATCH (u:User) -[:BELONGS_TO]-> () RETURN DISTINCT u.type;

On executing the above query, the results should look like the one shown below:

Output.4

+--------------+
| u.type       |
+--------------+
| "employee"   |
| "consultant" |
+--------------+

2 rows available after 21 ms, consumed after another 2 ms

To query and return values of the property key name for all the nodes with the label User, that is related to another node with label Group and the property key name having the values Core Engineering and Project Management, through the relationship type BELONGS_TO, execute the following MATCH query:

MATCH (u:User) -[:BELONGS_TO]-> (:Group {name: 'Core Engineering'}), (u) -[:BELONGS_TO]-> (:Group {name: 'Project Management'}) RETURN u.name;

On executing the above query, the results should look like the one shown below:

Output.5

+-------------+
| u.name      |
+-------------+
| "Bob Green" |
+-------------+

1 row available after 80 ms, consumed after another 2 ms

To query and return values of the property key name for all the nodes with the label User in an ascending order, execute the following MATCH query:

MATCH (u:User) RETURN u.name ORDER BY u.name;

On executing the above query, the results should look like the one shown below:

Output.6

+-----------------+
| u.name          |
+-----------------+
| "Alice Pink"    |
| "Bob Green"     |
| "Charlie Brown" |
| "David Black"   |
| "Frank Grey"    |
| "Gary White"    |
| "Harry Blue"    |
+-----------------+

7 rows available after 36 ms, consumed after another 0 ms

To query and return values of the property key name for all the nodes with the label User in a descending order, execute the following MATCH query:

MATCH (u:User) RETURN u.name ORDER BY u.name DESC;

On executing the above query, the results should look like the one shown below:

Output.7

+-----------------+
| u.name          |
+-----------------+
| "Harry Blue"    |
| "Gary White"    |
| "Frank Grey"    |
| "David Black"   |
| "Charlie Brown" |
| "Bob Green"     |
| "Alice Pink"    |
+-----------------+

7 rows available after 19 ms, consumed after another 0 ms

To set a new property key gid with the value cengg for the node with the label Group that has the property key name with value Core Engineering, execute the following MATCH query:

MATCH (g:Group {name: 'Core Engineering'}) SET g.gid = 'cengg';

On executing the above query, the results should look like the one shown below:

Output.8

0 rows available after 48 ms, consumed after another 0 ms
Set 1 properties

Similarly, set a new property key gid with the value pmgmt for the node with the label Group that has the property key name with value Project Management, execute the following MATCH query:

MATCH (g:Group {name: 'Project Management'}) SET g.gid = 'pmgmt';

On executing the above query, the results should look like the one shown below:

Output.9

0 rows available after 11 ms, consumed after another 0 ms
Set 1 properties

To query and return values for the property key gid for all the nodes with the label Group, execute the following MATCH query:

MATCH (g:Group) RETURN g.gid;

On executing the above query, the results should look like the one shown below:

Output.10

+---------+
| g.gid   |
+---------+
| "pmgmt" |
| "cengg" |
+---------+

2 rows available after 19 ms, consumed after another 0 ms

To remove the property key gid from all the node with the label Group, execute the following MATCH query:

MATCH (g:Group) REMOVE g.gid;

On executing the above query, the results should look like the one shown below:

Output.11

0 rows available after 12 ms, consumed after another 0 ms
Set 2 properties

To exit the cypher-shell, execute the following command:

:exit

References

Getting Started with Neo4J - Part 1

Neo4J Official Site