Manipulate query results
This section shows how to work with a query’s result so as to extract data in the form that is most convenient for your application.
Result as a list
By default, Driver.execute_query()
returns an EagerResult
object.
records, summary, keys = driver.execute_query(
"MATCH (a:Person) RETURN a.name AS name, a.age AS age",
database_="neo4j",
)
for person in records: (1)
print(person)
# person["name"] or person["age"] are also valid
# Some summary information (2)
print("Query `{query}` returned {records_count} records in {time} ms.".format(
query=summary.query, records_count=len(records),
time=summary.result_available_after
))
print(f"Available keys are {keys}") # ['name', 'age'] (3)
1 | The result records as a list, so it is easy to loop through them. |
2 | A summary of execution, with metadata and information about the result. |
3 | The keys available in the returned rows. |
Transform to Pandas DataFrame
The driver can transform the result into a Pandas DataFrame.
To achieve this, use the .execute_query()
keyword argument result_transformer_
and set it to neo4j.Result.to_df
.
This method is only available if the pandas
library is installed.
n
, m
) and 10 rowsimport neo4j
pandas_df = driver.execute_query(
"UNWIND range(1, 10) AS n RETURN n, n+1 AS m",
database_="neo4j",
result_transformer_=neo4j.Result.to_df
)
print(type(pandas_df)) # <class 'pandas.core.frame.DataFrame'>
This transformer accepts two optional arguments:
-
expand
— IfTrue
, some data structures in the result will be recursively expanded and flattened. More info in the API documentation. -
parse_dates
— IfTrue
, columns exclusively containingtime.DateTime
objects,time.Date
objects, orNone
, will be converted topandas.Timestamp
.
If you need to pass parameters to to_df , use lambda functions:result_transformer_=lambda res: res.to_df(True)
|
Transform to graph
The driver can transform the result into a collection of graph objects.
To achieve this, use the .execute_query()
keyword argument result_transformer_
and set it to neo4j.Result.graph
.
To make the most out of this method, your query should return a graph-like result instead of a single column.
The graph transformer returns a Graph
object exposing the properties nodes
and relationships
, which are set views into Node
and Relationship
objects.
You can use the graph format for further processing or to visualize the query result.
An example implementation that uses the pyvis
library to draw the graph is below.
pyvis
import pyvis
from neo4j import GraphDatabase
import neo4j
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
def main():
with GraphDatabase.driver(URI, auth=AUTH) as driver:
# Create some friends
input_list = [("Arthur", "Guinevre"),
("Arthur", "Lancelot"),
("Arthur", "Merlin")]
driver.execute_query("""
UNWIND $pairs AS pair
MERGE (a:Person {name: pair[0]})
MERGE (a)-[:KNOWS]->(friend:Person {name: pair[1]})
""", pairs=input_list,
database_="neo4j",
)
# Create a film
driver.execute_query("""
MERGE (film:Film {title: $title})
MERGE (liker:Person {name: $person_name})
MERGE (liker)-[:LIKES]->(film)
""", title="Wall-E", person_name="Arthur",
database_="neo4j",
)
# Query to get a graphy result
graph_result = driver.execute_query("""
MATCH (a:Person {name: $name})-[r]-(b)
RETURN a, r, b
""", name="Arthur",
result_transformer_=neo4j.Result.graph,
)
# Draw graph
nodes_text_properties = { # what property to use as text for each node
"Person": "name",
"Film": "title",
}
visualize_result(graph_result, nodes_text_properties)
def visualize_result(query_graph, nodes_text_properties):
visual_graph = pyvis.network.Network()
for node in query_graph.nodes:
node_label = list(node.labels)[0]
node_text = node[nodes_text_properties[node_label]]
visual_graph.add_node(node.element_id, node_text, group=node_label)
for relationship in query_graph.relationships:
visual_graph.add_edge(
relationship.start_node.element_id,
relationship.end_node.element_id,
title=relationship.type
)
visual_graph.show('network.html', notebook=False)
if __name__ == "__main__":
main()
Custom transformers
For more advanded scenarios, you can use the parameter result_transformer_
to provide a custom function that further manipulates the Result
object resulting from your query.
A transformer takes a Result
object and can output any data structure. The transformer’s return value is in turn returned by .execute_query()
.
Inside a transformer function you can use any of the Result
methods.
single
and consume
# Get a single record (or an exception) and the summary from a result.
def get_single_person(result):
record = result.single(strict=True)
summary = result.consume()
return record, summary
record, summary = driver.execute_query(
"MERGE (a:Person {name: $name}) RETURN a.name AS name",
name="Alice",
database_="neo4j",
result_transformer_=get_single_person,
)
print("The query `{query}` returned {record} in {time} ms.".format(
query=summary.query, record=record, time=summary.result_available_after))
fetch
and peek
# Get exactly 5 records, or an exception.
def exactly_5(result):
records = result.fetch(5)
if len(records) != 5:
raise Exception(f"Expected exactly 5 records, found only {len(records)}.")
if result.peek():
raise Exception("Expected exactly 5 records, found more.")
return records
records = driver.execute_query("""
UNWIND ['Alice', 'Bob', 'Laura', 'John', 'Patricia'] AS name
MERGE (a:Person {name: name}) RETURN a.name AS name
""", database_="neo4j",
result_transformer_=exactly_5,
)
A transformer must not return the
|
Glossary
- LTS
-
A Long Term Support release is one guaranteed to be supported for a number of years. Neo4j 4.4 is LTS, and Neo4j 5 will also have an LTS version.
- Aura
-
Aura is Neo4j’s fully managed cloud service. It comes with both free and paid plans.
- Cypher
-
Cypher is Neo4j’s graph query language that lets you retrieve data from the database. It is like SQL, but for graphs.
- APOC
-
Awesome Procedures On Cypher (APOC) is a library of (many) functions that can not be easily expressed in Cypher itself.
- Bolt
-
Bolt is the protocol used for interaction between Neo4j instances and drivers. It listens on port 7687 by default.
- ACID
-
Atomicity, Consistency, Isolation, Durability (ACID) are properties guaranteeing that database transactions are processed reliably. An ACID-compliant DBMS ensures that the data in the database remains accurate and consistent despite failures.
- eventual consistency
-
A database is eventually consistent if it provides the guarantee that all cluster members will, at some point in time, store the latest version of the data.
- causal consistency
-
A database is causally consistent if read and write queries are seen by every member of the cluster in the same order. This is stronger than eventual consistency.
- NULL
-
The null marker is not a type but a placeholder for absence of value. For more information, see Cypher → Working with
null
. - transaction
-
A transaction is a unit of work that is either committed in its entirety or rolled back on failure. An example is a bank transfer: it involves multiple steps, but they must all succeed or be reverted, to avoid money being subtracted from one account but not added to the other.
- backpressure
-
Backpressure is a force opposing the flow of data. It ensures that the client is not being overwhelmed by data faster than it can handle.
- transaction function
-
A transaction function is a callback executed by an
execute_read
orexecute_write
call. The driver automatically re-executes the callback in case of server failure. - Driver
-
A
Driver
object holds the details required to establish connections with a Neo4j database.