Processing Results

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.

python
Get Actors by Movie title
# 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:

  1. 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 as tx

  2. 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.

python
Peek
# 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.

python
Get Keys
# 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.

python
First Result
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.

python
Extract a value
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:

  1. The key or index of the field to return for each remaining record, and returns a list of single values.

  2. 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.

python
Extract values
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.

python
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.

python
Query Execution Times
# 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.

python
Write Statement Counters
print("{0} nodes created".format(info.counters.nodes_created))
print("{0} properties set".format(info.counters.properties_set))

You can read more about the result summary here.

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.

python
For Loop
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:

python
Get Available Keys
# 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.