Neo4j Automatic Transactions Using Python

by John Singer November 10, 2020 at 8:34 am

In this post we will look at how you can create and manage automatic transactions in Neo4j using the Python Driver for Neo4j. Earlier posts covered the basics of setting up a connection to Neo4j using the Python driver including how to create driver objects and session objects.

Since we will be running queries that create and delete nodes and relationships, we will also look at the very basics of the result object and how it handles query results. This subject will be covered completely in another post.

This post is part of a series of posts that explains how to write python code that accesses and manipulates data in the Neo4j graph database. You can look at the Idle Python environment I use to run the sample scripts or you can use your favorite Python editor.

The beginning of the script sets things up by creating driver and session objects. This was covered in earlier posts.

from neo4j import GraphDatabase
from neo4j import DEFAULT_DATABASE

#
# AUTOMATIC TRANSACTION DEMO
#
print("tutorialtransaction1.py - starting")
# create the uri for the connection
uri = "neo4j://localhost:7687"
# create a driver object
driver = GraphDatabase.driver(uri, auth=("neo4j", "xxx"))
# create a session that connects to the default database
session = driver.session(database=DEFAULT_DATABASE)

Remember to change the password on the line that creates the driver object (the code shows it as “xxx”). The code above does the needed imports, and creates a driver and session object.

Neo4j Automatic Transactions Using Python

The tutorial example will create two nodes and then connect those nodes with a relationship. In the first example, we will use automatic transactions for each of the three create statements. An automatic transaction is just what it sounds like – the transaction object and commit processing are handled for you with no coding.

To make this script re-runnable, it starts out by deleting the two nodes and the relationship. See the code below:

# delete all the tutorial data
print("delete all tutorial data")
result = session.run("match (n:demoPerson) detach delete n")
resultSummary = result.consume()
print("result counters:{}".format(resultSummary.counters))

The session.run statement is what you use to run a cypher query in an automatic transaction. The example above is the simplest form of the run method where you pass in a query as a text string. The “detach delete” clause in the query above will delete all the nodes with the “demoPerson” label and delete any relationships those nodes have. This allows you to delete a node and it’s relationships all at once (you can’t delete a node if it has relationships). The last two lines get the resultSummary object and then prints out the “counters”. Only the counters that are non-zero will be displayed so the first time you run this there won’t be any as there weren’t any nodes or relationships to delete. While you probably don’t need this in an application it is a handy way to see what a query actually did.

Now let’s create some nodes and relationship!

Creating Nodes And Relationships

# create two nodes and link them together
result = session.run('''create (n:demoPerson
                        {name:"Fred", country:"US"})
                        return n''')

The above code uses the session.run command to run the cypher create statement and returns a result object. Note that the cypher statement is coded to return the node it just created (“return n”). Now we will process the result.

record = result.single()
print("record retrieved:{}".format(record))

The “single” method will retrieve the first record from the result object and consume all other records if there are any. If there aren’t any records it will return None. Since the create statement is coded to only create one node, we expect that to be returned from the single method. The print statement will display a string representation of the node we just created. The result and record objects will be covered in more detail in a later post.

The entire script is shown below.

from neo4j import GraphDatabase
from neo4j import DEFAULT_DATABASE

#
# AUTOMATIC TRANSACTION DEMO
#
print("tutorialtransaction1.py - starting")
# create the uri for the connection
uri = "neo4j://localhost:7687"
# create a driver object
driver = GraphDatabase.driver(uri, auth=("neo4j", "nachodog"))
# create a session that connects to the default database
session = driver.session(database=DEFAULT_DATABASE)

# delete all the tutorial data
print("delete all tutorial data")
result = session.run("match (n:demoPerson) detach delete n")
resultSummary = result.consume()
print("result counters:{}".format(resultSummary.counters))

# create two nodes and link them together
print("create a demoPerson node")
result = session.run('''create (n:demoPerson
                        {name:"Fred", country:"US"})
                        return n''')
record = result.single()
print("record retrieved:{}".format(record))
resultSummary = result.consume()
print("result counters:{}".format(resultSummary.counters))

print("create another demoPerson node")
result = session.run('''create (n:demoPerson
                        {name:"Barney", country:"US"})
                        return n''')
record = result.single()
print("record retrieved:{}".format(record))
resultSummary = result.consume()
print("result counters:{}".format(resultSummary.counters))

print("create a relationship between the two nodes")
result = session.run('''match (p1:demoPerson
                               {name:"Fred", country:"US"}),
                              (p2:demoPerson
                               {name:"Fred", country:"US"})
                      with p1,p2
                      create (p1)-[r:KNOWS]->(p2)
                      return r''')
record = result.single()
print("record retrieved:{}".format(record))
resultSummary = result.consume()
print("result counters:{}".format(resultSummary.counters))


# clean up
session.close()
driver.close()
print("tutorialtransaction1.py - driver closed")

The session.run method is used three times to create the Fred and Barney nodes, and then to create a “KNOWS” relationship between the two nodes.

When you run the script it will display the result counters for each query. Here is the output after creating the Fred node:

result counters:{'labels_added': 1, 'nodes_created': 1, 'properties_set': 2}

This shows that the query created 1 node, it added 1 label to the node, and it set 2 properties on the node.

Summary

In this post we looked at how to run Neo4j automatic transactions using Python. Automatic transactions are used to create two nodes and then create a relationship between them. This is convenient and easy to code as you don’t need to bother with creating transaction objects or managing commit processing – all of this is handled for you! However if your business logic requires all three of the create statements to succeed or fail together (called a unit of work) then you need to explicitly create and and manage the transaction yourself. This will be covered in the next post which will introduce the transaction object and it’s commit and rollback methods.

The entire script is designed so you can run it repeatedly and since it starts out by deleting the tutorial data it will not create a lot of duplicate Fred and Barney nodes. We also introduced the result and record objects which will be covered in greater detail later.

No Code Solution

Want to query, edit, and delete nodes and relationships without writing any code? Check out NodeEra – the worlds leading Neo4j data modeling and management tool.

Add Comment