Query results are typically consumed as a stream of records. The drivers provide a way to iterate through that stream.
Result
Here is an example query which retrieves a list of :Person
nodes related to a given Movie.
# Unit of work
def get_actors(tx, movie): # (1)
result = tx.run("""
MATCH (p:Person)-[:ACTED_IN]->(:Movie {title: $title})
RETURN p
""", title=movie)
# Access the `p` value from each record
return [ record["p"] for record in result ]
# Open a Session
with driver.session() as session:
# Run the unit of work within a Read Transaction
actors = session.execute_read(get_actors, movie="The Green Mile") # (2)
for record in actors:
print(record["p"])
session.close()
The code can be broken down into two elements:
-
The
get_actors()
function defines a unit of work to be executed within a transaction, passed as the first argument of the function, in this case referenced astx
-
The
execute_read()
method executes the unit of work within a Read Transaction
The result of the execute_read()
is a Result object.
The result object acts as a buffer for an iterable list of records and provides a number of options for accessing those records. Once a result is consumed, it is removed from the buffer.
Peeking at Results
If you wish to preview a result without consuming it, you can use the peek
method.
# Check the first record without consuming it
peek = result.peek()
print(peek)
This can be used to preview the first record in the result without removing it from the buffer.
Keys
To get the keys for each record in the result, you can call the keys()
method.
# Get all keys available in the result
print(result.keys()) # ["p", "roles"]
Single Result
If you only expect a single record, you can use the single()
method on the result
to return the first record.
def get_actors_single(tx, movie):
result = tx.run("""
MATCH (p:Person)-[:ACTED_IN]->(:Movie {title: $title})
RETURN p
""", title=movie)
return result.single()
If more than one record is available from the result then a warning will be generated, but the first result will still be returned.
If no results are available, then the method call will return None
.
Value
If you wish to extract a single value from the remaining list of results, you can use the value()
method.
def get_actors_values(tx, movie):
result = tx.run("""
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie {title: $title})
RETURN p.name AS name, m.title AS title, r.roles AS roles
""", title=movie)
return result.value("name", False)
# Returns the `name` value, or False if unavailable
This method expects two parameters:
-
The key or index of the field to return for each remaining record, and returns a list of single values.
-
Optionally, you can provide a default value to be used if the value is
None
or unavailable.
Values
If you need to extract more than one item from each record, use the values()
method.
The method expects one parameter per item requested from the RETURN
statement of the query.
def get_actors_values(tx, movie):
result = tx.run("""
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie {title: $title})
RETURN p.name AS name, m.title AS title, r.roles AS roles
""", title=movie)
return result.values("name", "title", "roles")
In the above example, a list
will be returned, with each entry containing values representing name
, title
, and roles
.
Consume
The consume()
method will consume the remainder of the results and return a Result Summary.
def get_actors_consume(tx, name):
result = tx.run("""
MERGE (p:Person {name: $name})
RETURN p
""", name=name)
info = result.consume()
The Result Summary contains a information about the server, the query, execution times and a counters
object which provide statistics about the query.
# The time it took for the server to have the result available. (milliseconds)
print(info.result_available_after)
# The time it took for the server to consume the result. (milliseconds)
print(info.result_consumed_after)
The counters
object can be used to retrieve the number of nodes, relationships, properties or labels that were affected during a write transaction.
print("{0} nodes created".format(info.counters.nodes_created))
print("{0} properties set".format(info.counters.properties_set))
Exploring Records
When accessing a record, either within a loop, list comprehension or within a single record, you can use the []
bracket syntax.
The following example extracts the p
value from each record
in the result buffer.
for record in result:
print(record["p"]) # Person Node
You can also access a value using its index, as it relates to the value contained in the keys array:
# Get all keys available in the result
print(result.keys()) # ["p", "roles"]
Check Your Understanding
1. Consuming the only Result left
Which method would you use to consume the only record left in a result?
-
❏
result.first()
-
❏
result[0]
-
❏
result.get(0)
-
✓
result.single()
Hint
Your application will be expecting a single record.
Solution
You should call result.single()
to get the one and only record left in the result.
2. Query Statistics
Which method would you use to obtain statistics on a result of a write query?
-
✓
result.consume()
-
❏
result.info()
-
❏
result.summary()
Hint
You must consume all records of the result to obtain statistics.
Solution
You should call result.consume()
to obtain statistics for the executed Cypher statement.
3. Accessing values within a Record
Which of the following examples will allow you to extract the name
property for the remaining records within a result?
-
✓
[ record["name"] for record in result ]
-
❏
result.all("name")
-
✓
result.value("name", None)
-
✓
result.values("name")
Hint
There are three valid methods in the list above.
Solution
The valid ways of extracting the name
property from each remaining record are [ record["name"] for record in result ]
, result.value("name", None)
and result.values("name")
.
Lesson Summary
You now have all of the information required to send Cypher queries to Neo4j and consume the results.
Next, we will look at the Neo4j Type System and some of the considerations that you need to make when working with values coming from Neo4j in your Python application.