Skip to main content

Getting to Know Neo4j

Mongkok, Hong Kong

Running in Docker

Create the folders Neo4j will need to persists it's data:

mkdir /opt/neo4j/data
mkdir /opt/neo4j/logs
mkdir /opt/neo4j/import
mkdir /opt/neo4j/plugins

chown 101:101 all folders to make them available for your Docker user.

Pull and run the latest version of Neo4j from Docker Hub.

docker run \
--name neo4j \
-p7474:7474 -p7687:7687 \
-d \
-v /opt/neo4j/data:/data \
-v /opt/neo4j/logs:/logs \
-v /opt/neo4j/import:/var/lib/neo4j/import \
-v /opt/neo4j/plugins:/plugins \
--env NEO4J_AUTH=neo4j/password \
neo4j:latest

By default, the Docker image exposes three ports for remote access:

  • 7474 for HTTP

  • 7473 for HTTPS

  • 7687 for Bolt

Now head over to localhost:7474 with your browser and login with neo4j/password.

Cypher

CREATE

Use the Create command to create nodes with properties and relationships.

CREATE(p1: Person{name:'Susanne Colgate'}) -[r:UNIVERSITY_BERKELEY{attr:'Susanne followed Simon'}]-> (p2: Person{name: 'Simon Scabourough'}) RETURN p1,p2,r

Create two nodes of type Person and the property Name and set them into a Relationship, e.g. both persons went to the same university. The return command allows us to see a visualization of the result of our query.

MATCH

Use the Match command to find pattern among your nodes.

MATCH (p1: Person {name:'Simon Scabourough'}), (p2:Person {name: 'Susanne Colgate'})
CREATE (p1) -[r:APPLIED_PHYSICS{attr:'Susanne invited Simon'}]-> (p2)
RETURN p1,p2,r

Find two nodes of type Person with specific Name properties and add a new relationship between them - a shared course at the university.

MATCH (p1:Person {name: 'Simon Scabourough'})-[r:APPLIED_PHYSICS]->(p2)
RETURN p2

Show me who joined Simon in his AP1 course.

MATCH ()-[r:UNIVERSITY_BERKLY]->(p2)
RETURN p2

Show me everyone who was invited into Berkley.

MERGE

Just like the match operator you get the nodes or relationships returned. But unlike match, if the node, property or relationship does not exist it will be created by the merge query:

MATCH (person:Person{name:'Josefine Strudelbäcker'})
RETURN person

Does not find anything - Merge creates the node for you:

MERGE (person:Person{name:'Josefine Strudelbäcker'})
MERGE (bäckerei:Work{name:'Zum Zipfelkratsch'})
MERGE (person)-[:Worked_IN]->(bäckerei)
RETURN person, bäckerei

ON CREATE SET

The Merge Operator will either create the node or update it if it already exists. Everything after On Create will only be executed if the node does not yet exists:

MERGE (location:Location{name: 'Linz'})
ON CREATE SET location.created_at = timestamp(), location.created_by = 'GPS100x5'
RETURN location

ON MATCH SET

If we want to update a property every time we get a match:

MERGE (location:Location{name: 'Linz'})
ON CREATE SET location.created_at = timestamp(), location.update_count = 0
ON MATCH SET location.modified_at = timestamp(), location.update_count = (location.update_count + 1)
RETURN location

How often die Keanu Reeves watch Top Gun? Increment a relationship property every time a query is run:

MATCH (actor:Person{name: 'Keanu Reeves'})
MATCH (movie:Movie{title: 'Top Gun'})
MERGE (actor)-[rel:VIEWED]->(movie)
ON CREATE SET rel.count = 1
ON MATCH SET rel.count = (rel.count + 1)
RETURN actor, movie

Getting to know Neo4j

WHERE, WITH and AND

MATCH (a:Person),(b:Person)
WHERE a.name='Simon Scabourough' AND b.name='Susanne Colgate'
WITH a,b
CREATE (a) -[r:ANALYTICAL_CHEMISTRY]->(b)
RETURN a,b,r

SET

Use the Set command to change a property of a node.

MATCH (p:Person{name: 'Susanne Colgate'})
SET p.name= 'Susan Colgate'
RETURN p

Set can also be used to create an property:

MATCH (p:Person {name: 'Susan Colgate'})
SET p.title = 'Freshman'
RETURN p

Or to set a relationship:

MATCH (director:Person{name:'Clint Eastwood'}),
(movies:Movie)<-[rel:DIRECTED]-(director)
SET rel.rating = 5

Or to set another label:

MATCH (director:Person{name:'Clint Eastwood'}),
(movies:Movie)<-[rel:DIRECTED]-(director)
SET director:Hero

Setting a relationship to a new type is not supported by Neo4j. But you can create a new relationship and copy over the old properties:

MATCH (actor1:Person{name:'Tom Hanks'})-[old_rel:HAS_CONTACT]->(actor2:Person{name:'Al Pacino'})
CREATE (actor1)-[new_rel:OLD_CONTACT]->(actor2)
SET new_rel=old_rel
DELETE old_rel
RETURN actor1, actor2

REMOVE

Remove a relationship:

MATCH (director:Person{name:'Clint Eastwood'}),
(movies:Movie)<-[rel:DIRECTED]-(director)
REMOVE rel.rating

Remove a label:

MATCH (director:Person{name:'Clint Eastwood'}),
(movies:Movie)<-[rel:DIRECTED]-(director)
REMOVE director:Hero

WITH

Set dynamic properties to nodes with the WITH operator:

MATCH (actor:Person{name:'Tom Hanks'})-[rel:ACTED_IN]->(movie)
WITH actor, count(movie) AS num_of_movies
SET actor.num_of_movies = num_of_movies
RETURN actor

Getting to know Neo4j

DELETE

MATCH (n)
DELETE n

Only works if Nodes don't have any relations - otherwise those have to be selected and deleted as well.

Delete All:

MATCH (n)
OPTIONAL MATCH (n)-[r]-()
DELETE n,r

Select all nodes and relationships and delete the selection.

Or short - remove node and cut all relations that were attached to it:

MATCH (node)
DETACH DELETE node

Selective delete:

MATCH (actor:Person{name:'Tom Cruise'}),
(movies:Movie)<-[:ACTED_IN]-(actor)
DETACH DELETE movies

NULL

Give me all person nodes that have a earnings property that is not undefined:

MATCH (person:Person)
WHERE person.earnings IS NOT NULL
RETURN person

Nth Degree Relationships

Get all 1st degree contacts of actor Keanu Reeves:

MATCH (actor:Person{name:'Keanu Reeves'})-[:HAS_CONTACT]->(contact)
RETURN actor, contact

Get all 2nd degree contacts:

MATCH (actor:Person{name:'Keanu Reeves'})-[:HAS_CONTACT]->(contact)-[:HAS_CONTACT]->(contact2)
RETURN actor, contact2

Get the nth degree contacts by simply adding *n to the relationship in question - e.g. get the 5th degree contacts:

MATCH (actor:Person{name:'Keanu Reeves'})-[rel:HAS_CONTACT*5]->(contact)
RETURN actor, rel, contact
LIMIT 1

Show all 0 - 2nd degree contacts:

MATCH (actor:Person{name:'Keanu Reeves'})-[rel:HAS_CONTACT*0..2]->(contact)
RETURN actor, rel, contact
LIMIT 5

PATH

How many contacts are between Keanu Reeves and Tom Cruise:

MATCH (neo:Person{name:'Keanu Reeves'})
MATCH (maverick:Person{name:'Tom Cruise'})
MATCH path=((neo)-[rel:HAS_CONTACT*]->(maverick))
RETURN length(path)
LIMIT 1

To find the shortest path limit the degree of contact until no path can be found anymore:

MATCH (neo:Person{name:'Keanu Reeves'})
MATCH (maverick:Person{name:'Tom Cruise'})
MATCH path=((neo)-[rel:HAS_CONTACT*..2]->(maverick))
RETURN length(path) AS path_length
LIMIT 1

The minimum path length is 2.

The easier way to find the path length is the SHORTEST PATH function:

MATCH (neo:Person{name:'Keanu Reeves'})
MATCH (maverick:Person{name:'Tom Cruise'})
MATCH path=shortestPath((neo)-[rel:HAS_CONTACT*..20]->(maverick))
RETURN length(path) AS path_length

Since there might be several shortest paths we can use the allShortestPath function to resolve all of them:

MATCH (neo:Person{name:'Keanu Reeves'})
MATCH (maverick:Person{name:'Tom Cruise'})
MATCH path=allShortestPaths((neo)-[rel:HAS_CONTACT*..20]->(maverick))
RETURN path, length(path) AS path_length

Get the shortest path between actors that acted in the movie The Matrix and actors from the movie Top Gun:

MATCH (actor1:Person)-[:ACTED_IN]->(movie1:Movie{title:'Top Gun'}),(actor2:Person)-[:ACTED_IN]->(movie2:Movie{title:'The Matrix'}), path = shortestPath((actor1)-[:HAS_CONTACT*..20]->(actor2))
RETURN path, movie1, movie2, length(path) AS pathLength
ORDER BY pathLength
LIMIT 2

To exclude path where an actor worked in both movies:

MATCH (actor1:Person)-[:ACTED_IN]->(movie1:Movie{title:'Top Gun'}),(actor2:Person)-[:ACTED_IN]->(movie2:Movie{title:'The Matrix'}), path = shortestPath((actor1)-[:HAS_CONTACT*..20]->(actor2))
WHERE actor1 <> actor2
RETURN path, movie1, movie2, length(path) AS pathLength
ORDER BY pathLength
LIMIT 1

Getting to know Neo4j

Importing Scripts

Just write down all your Cypher queries into a text file. Add a comment on top to name the script:

// Neo4j Sample Data
CREATE(p1: Person{name:'Susanne Colgate'}) -[r:UNIVERSITY_BERKELEY{attr:'Susanne followed Simon'}]-> (p2: Person{name: 'Simon Scabourough'})
MATCH (p1: Person {name:'Simon Scabourough'}), (p2:Person {name: 'Susanne Colgate'})
CREATE (p1) -[r:APPLIED_PHYSICS{attr:'Susanne invited Simon'}]-> (p2)
MATCH (p1:Person {name: 'Simon Scabourough'})-[r:APPLIED_PHYSICS]->(p2)
MATCH (a:Person),(b:Person)
WHERE a.name='Simon Scabourough' AND b.name='Susanne Colgate'
WITH a,b
CREATE (a) -[r:ANALYTICAL_CHEMISTRY]->(b)
MATCH (p:Person{name: 'Susanne Colgate'})
SET p.name= 'Susan Colgate'
MATCH (p:Person {name: 'Susan Colgate'})
SET p.title = 'Freshman'

And drag&drop them into the favorite section of the Neo4j web interface:

Getting to know Neo4j

You can run all scripts at once by clicking on the play button next to your scripts name.