2

I have just started playing with the new neo4j driver for python and got totally stuck with transactions. How do I check if a transaction completed successfully? As far as I can say commit function does not automatically raise any errors, i.e. if for instance I provide it with an incorrect Cypher query I get no information on this.

I tried reading the last_result argument from the Session object and came up with sth like:

import neo4j.v1 as neo

def db_confirm_transaction_success(session):
    try:
        w = list(session.last_result)
        return True
    except neo.CypherError as e:
        session.last_result._consumed = True
        return False
    except neo.ResultError as e:
        session.last_result._consumed = True
        return False

It kind a works... Yet, it does require modifying private attributes and simply do not seem right/correct. There must be a simpler and more elegant solution.

Thank you in advance for help.

Edit: Just to make it clear Transaction.success attribute indicates if a transaction should get committed or rolled back. Yet, e.g. Cypher errors can be identified as late as of the queries' execution time.

Szpilona
  • 121
  • 2
  • 9

2 Answers2

6

I was banging my head on the same thing until I gave the developer manual a read.

Up until then, I couldn't figure out why running several bad statements with session.run(statement) would not raise an exception, but rather, session.close() would.

Then i tried using something like:

with session.begin_transaction() as tx:
    tx.run(statement)
    tx.success = True

If you don't want the context manager, you can use:

tx = session.begin_transaction()
tx.run(statement)
tx.commit()

If you read the python docs you might notice tx.commit() is the same thing as running tx.success=True and tx.close().

The problem with this, is that calling commit() only appends a COMMIT cypher message to the connection's stream. To my knowledge it does not verify the success of the transaction.


After reading section 18 in the manual, I found that because I was not explicitly consuming the results, there was no guarentee that the statement was processed because the library using lazy loading (retreival of results only on demand). See the note from 18.1 below:

"Result records are loaded lazily as the cursor is moved through the stream. This means that the cursor must be moved to the first result before this result can be consumed. It also means that the entire stream must be explicitly consumed before summary information and metadata is available. It is generally best practice to explicitly consume results and close sessions, in particular when running update statements. This applies even if the summary information is not required. Failing to consume a result can lead to unpredictable behaviour as there will be no guarantee that the server has seen and handled the Cypher statement."


So, essentially, you need to explicitly consume your results after running a cypher statement. This can be done like this:

res = session.run(statement) # or the equivalent iusing transaction style
res.consume()

I noticed the .consume() function calls list(self) to iterate over all the results (The StatementResult class defines an __iter__ method). So, while I haven't tested it, it might be the case that simply looping over the results does the consumption for you:

res = session.run(statement)
for r in res:
    continue

Hopefully this helps!

spanishgum
  • 1,030
  • 1
  • 14
  • 29
0

Just wanted to post an updated answer for Neo4j 4.x.

def commit_data(query):
    with driver.session(database="neo4j") as session:
        tx = session.begin_transaction()
        result = tx.run(query)
        result = [dict(i) for i in result]
        
       # uncomment below line to see the complete txn summary.
        # print(result) 

        if result[0]['failedOperations']>0:
            print(query)
            print(result)
            raise 'Error! Please recheck your query.'
        
        tx.commit()

Hope it will help others.

Rheatey Bash
  • 779
  • 6
  • 17