Data types and mapping to Cypher types
The tables in this section show the mapping between Cypher data types and Python types.
Core types
Cypher Type | Python Type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For full documentation, see API documentation — Core data types.
Temporal types
Temporal data types are implemented by the neo4j.time
module.
It provides a set of types compliant with ISO-8601 and Cypher, which are similar to those found in Python’s native datetime
module.
To convert between driver and native types, use the methods .from_native()
and .to_native()
(does not apply to Duration
).
This conversion is lossy, as the driver’s types support nanosecond precision, while Python’s native types do not.
The driver’s temporal types are designed to be used with pytz.
Other datetime.tzinfo implementations (e.g., datetime.timezone , zoneinfo , dateutil.tz ) are not supported and are unlikely to work well.
|
For a list of time zone abbreviations, see List of tz database time zones.
Cypher Type | Python Type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
† Where tzinfo
is not None
.
†† Where tzinfo
is None
.
from datetime import datetime
import pytz
from neo4j import GraphDatabase
from neo4j.time import DateTime
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
friends_since = DateTime(year=1999, month=11, day=23,
hour=7, minute=47, nanosecond=4123)
friends_since = pytz.timezone("US/Eastern").localize(friends_since)
# Python's native datetimes work as well.
# They don't support the full feature-set of Neo4j's type though.
# py_friends_since = datetime(year=1999, month=11, day=23, hour=7, minute=47)
# py_friends_since = pytz.timezone("US/Eastern").localize(py_friends_since)
# Create a friendship with the given DateTime, and return the DateTime
with GraphDatabase.driver(URI, auth=AUTH) as driver:
records, summary, keys = driver.execute_query("""
MERGE (a:Person {name: $name})
MERGE (b:Person {name: $friend})
MERGE (a)-[friendship:KNOWS {since: $friends_since}]->(b)
RETURN friendship.since
""", name="Alice", friend="Bob",
friends_since=friends_since # or friends_since=py_friends_since
)
out_datetime = records[0]["friendship.since"]
print(out_datetime) # 1999-11-23T07:47:00.000004123-05:00
# Converting DateTime to native datetime (lossy)
py_out_datetime = out_datetime.to_native() # type: datetime
print(py_out_datetime) # 1999-11-23 07:47:00.000004-05:00
For full documentation, see API documentation — Temporal data types.
Date
Represents an instant capturing the date, but not the time, nor the timezone.
from neo4j.time import Date
d = Date(year=2021, month=11, day=2)
print(d) # '2021-11-02'
For full documentation, see API documentation — Temporal data types — Date.
Time
Represents an instant capturing the time, and the timezone offset in seconds, but not the date.
from neo4j.time import Time
import pytz
t = Time(hour=7, minute=47, nanosecond=4123, tzinfo=pytz.FixedOffset(-240))
print(t) # '07:47:00.000004123-04:00'
For full documentation, see API documentation — Temporal data types — Time.
LocalTime
Represents an instant capturing the time of day, but not the date, nor the timezone.
from neo4j.time import Time
t = Time(hour=7, minute=47, nanosecond=4123)
print(t) # '07:47:00.000004123'
For full documentation, see API documentation — Temporal data types — Time.
DateTime
Represents an instant capturing the date, the time, and the timezone identifier.
from neo4j.time import DateTime
import pytz
dt = DateTime(year=2021, month=11, day=2, hour=7, minute=47, nanosecond=4123)
dt = pytz.timezone("US/Eastern").localize(dt) # time zone localization (optional)
print(dt) # '2021-11-02T07:47:00.000004123-04:00'
For full documentation, see API documentation — Temporal data types — DateTime.
LocalDateTime
Represents an instant capturing the date and the time, but not the timezone.
from neo4j.time import DateTime
dt = DateTime(year=2021, month=11, day=2, hour=7, minute=47, nanosecond=4123)
print(dt) # '2021-11-02T07:47:00.000004123'
For full documentation, see API documentation — Temporal data types — DateTime.
Duration
Represents the difference between two points in time.
A datetime.timedelta
object passed as a parameter will always be implicitly converted to neo4j.time.Duration
.
It is not possible to convert from neo4j.time.Duration
to datetime.timedelta
(because datetime.timedelta
lacks month support).
from neo4j.time import Duration
duration = Duration(years=1, days=2, seconds=3, nanoseconds=4)
print(duration) # 'P1Y2DT3.000000004S'
For full documentation, see API documentation — Temporal data types — Duration.
Spatial types
Cypher supports spatial values (points), and Neo4j can store these point values as properties on nodes and relationships.
The object attribute srid
(short for Spatial Reference Identifier) is a number identifying the coordinate system the spatial type is to be interpreted in.
You can think of it as a unique identifier for each spatial type.
Cypher Type | Python Type |
---|---|
|
|
|
|
|
|
For full documentation, see API documentation — Spatial types.
CartesianPoint
Represents a point in 2D/3D Cartesian space.
Exposes properties x
, y
, z
(the latter for 3D points only).
from neo4j.spatial import CartesianPoint
# A 2D CartesianPoint
point = CartesianPoint((1.23, 4.56))
print(point.x, point.y, point.srid)
# 1.23 4.56 7203
# A 3D CartesianPoint
point = CartesianPoint((1.23, 4.56, 7.89))
print(point.x, point.y, point.z, point.srid)
# 1.23 4.56 7.8 9157
For full documentation, see API documentation — Spatial types — CartesianPoint.
WGS84Point
Represents a point in the World Geodetic System (WGS84).
Exposes properties longitude
, latitude
, height
(the latter for 3D points only), which are aliases for x
, y
, z
.
from neo4j.spatial import WGS84Point
# A 2D WGS84Point
point = WGS84Point((1.23, 4.56))
print(point.longitude, point.latitude, point.srid)
# or print(point.x, point.y, point.srid)
# 1.23 4.56 4326
# A 3D WGS84Point
point = WGS84Point((1.23, 4.56, 7.89))
print(point.longitude, point.latitude, point.height, point.srid)
# or print(point.x, point.y, point.z, point.srid)
# 1.23 4.56 7.89 4979
For full documentation, see API documentation — Spatial types — WSG84Point.
Graph types
Graph types are only passed as results and may not be used as parameters. The section Manipulate query results — Transform to graph contains an example with graph types.
Cypher Type | Python Type |
---|---|
|
|
|
|
|
|
For full documentation, see API documentation — Graph types.
Node
Represents a node in a graph.
The property element_id
provides an identifier for the entity.
This should be used with care, as no guarantees are given about the mapping between id values and elements outside the scope of a single transaction. In other words, using an element_id
to MATCH
an element across different transactions is risky.
from neo4j import GraphDatabase
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
with GraphDatabase.driver(URI, auth=AUTH) as driver:
records, _, _ = driver.execute_query(
"MERGE (p:Person {name: $name}) RETURN p AS person",
name="Alice",
database_="neo4j",
)
for record in records:
node = record["person"]
print(f"Node ID: {node.element_id}\n"
f"Labels: {node.labels}\n"
f"Properties: {node.items()}\n"
)
# Node ID: 4:73e9a61b-b501-476d-ad6f-8d7edf459251:0
# Labels: frozenset({'Person'})
# Properties: dict_items([('name', 'Alice')])
For full documentation, see API documentation — Graph types — Node.
Relationship
Represents a relationship in a graph.
The property element_id
provides an identifier for the entity.
This should be used with care, as no guarantees are given about the mapping between id values and elements outside the scope of a single transaction.
from neo4j import GraphDatabase
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
with GraphDatabase.driver(URI, auth=AUTH) as driver:
records, _, _ = driver.execute_query("""
MERGE (p:Person {name: $name})
MERGE (p)-[r:KNOWS {status: $status, since: date()}]->(friend:Person {name: $friend_name})
RETURN r AS friendship
""", name="Alice", status="BFF", friend_name="Bob",
)
for record in records:
relationship = record["friendship"]
print(f"Relationship ID: {relationship.element_id}\n"
f"Start node: {relationship.start_node}\n"
f"End node: {relationship.end_node}\n"
f"Type: {relationship.type}\n"
f"Friends since: {relationship.get('since')}\n"
f"All properties: {relationship.items()}\n"
)
# Relationship ID: 5:73e9a61b-b501-476d-ad6f-8d7edf459251:1
# Start node: <Node element_id='4:73e9a61b-b501-476d-ad6f-8d7edf459251:0' labels=frozenset({'Person'}) properties={'name': 'Alice'}>
# End node: <Node element_id='4:73e9a61b-b501-476d-ad6f-8d7edf459251:2' labels=frozenset({'Person'}) properties={'name': 'Bob'}>
# Type: KNOWS
# Friends since: 2022-11-07
# All properties: dict_items([('since', neo4j.time.Date(2022, 11, 7)), ('status', 'BFF')])
For full documentation, see API documentation — Graph types — Relationship.
Path
Represents a path in a graph.
from neo4j import GraphDatabase
from neo4j.time import Date
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
def add_friend(driver, name, status, date, friend_name):
driver.execute_query("""
MERGE (p:Person {name: $name})
MERGE (p)-[r:KNOWS {status: $status, since: $date}]->(friend:Person {name: $friend_name})
""", name=name, status=status, date=date, friend_name=friend_name,
database_="neo4j",
)
with GraphDatabase.driver(URI, auth=AUTH) as driver:
# Create some :Person nodes linked by :KNOWS relationships
add_friend(driver, name="Alice", status="BFF", date=Date.today(), friend_name="Bob")
add_friend(driver, name="Bob", status="Fiends", date=Date.today(), friend_name="Sofia")
add_friend(driver, name="Sofia", status="Acquaintances", date=Date.today(), friend_name="Sofia")
# Follow :KNOWS relationships outgoing from Alice three times, return as path
records, _, _ = driver.execute_query("""
MATCH path=(:Person {name: $name})-[:KNOWS*3]->(:Person)
RETURN path AS friendship_chain
""", name="Alice",
database_="neo4j",
)
path = records[0]["friendship_chain"]
print("-- Path breakdown --")
for friendship in path:
print("{name} is friends with {friend} ({status})".format(
name=friendship.start_node.get("name"),
friend=friendship.end_node.get("name"),
status=friendship.get("status"),
))
For full documentation, see API documentation — Graph types — Path.
Extended types
The driver supports more types as query parameters, which get automatically mapped to one of the core types. Because of this conversion, and because the server does not know anything about the extended types, the driver will never return these types in results, but always their corresponding mapping.
Parameter Type | Mapped Python Type |
---|---|
tuple |
|
bytearray |
|
numpy |
|
pandas |
|
pandas |
|
pandas |
|
In general, if you are unsure about the type conversion that would happen on a given parameter, you can test it as in the following example:
import neo4j
with neo4j.GraphDatabase.driver(URI, auth=AUTH) as driver:
type_in = ("foo", "bar")
records, _, _ = driver.execute_query("RETURN $x AS type_out", x=type_in)
type_out = records[0].get("type_out")
print(type(type_out)) # <class 'list'>
print(type_out) # ['foo', 'bar']
Exceptions
The driver can raise a number of different exceptions. A full list is available in the API documentation. For a list of errors the server can return, see the Status code page.
Classification | Description |
---|---|
|
Errors reported by the Neo4j server (e.g., wrong Cypher syntax, bad connection, wrong credentials, …) |
|
Errors reported by the driver (e.g., bad usage of parameters or transactions, improper handling of results, …) |
Some server errors are marked as safe to retry without need to alter the original request.
Examples of such errors are deadlocks, memory issues, or connectivity issues.
All driver’s exception types implement the method .is_retryable()
, which gives insights into whether a further attempt might be successful.
This is particular useful when running queries in explicit transactions, to know if a failed query is worth re-running.
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.