Data types and mapping to Cypher types
The tables in this section show the mapping between Cypher data types and Go types.
When accessing a record’s content, all its properties are of type any.
This means that you have to cast them to the relevant Go type if you want to use methods/features defined on such types.
For example, if the name property coming from the database is a string, record.AsMap()["name"][1] would result in an invalid operation error at compilation time.
For it to work, cast the value to string before using it as a string: name := record.AsMap()["name"].(string) and then name[1].
|
Core types
| Cypher Type | Go Type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Temporal types
The driver provides a set of temporal data types compliant with ISO-8601 and Cypher. Sub-second values are measured to nanosecond precision.
The driver’s types rely on Go’s time types.
All temporal types, except neo4j.Duration, are in fact time.Date objects under the hood.
This means that:
-
if you want to query the database with a temporal type, instantiate a
time.Dateobject and use it as query parameter (i.e. you don’t need to care about driver’s types) -
if you retrieve a temporal object that you had previously inserted starting from a
time.Dateobject, you will get back atime.Dateobject (i.e. you don’t need to care about driver’s types) -
if you receive a temporal object using one of Cypher temporal functions, you will get back the corresponding driver type as displayed in the table below. You may then use
.Time()on them to convert them into Gotime.Dateobjects.Cypher Type Go Type DATEneo4j.DateZONED TIMEneo4j.OffsetTimeLOCAL TIMEneo4j.LocalTimeZONED DATETIMEneo4j.TimeLOCAL DATETIMEneo4j.LocalDateTimeDURATIONneo4j.Duration
package main
import (
"fmt"
"context"
"time"
"github.com/neo4j/neo4j-go-driver/v6/neo4j"
"reflect"
)
func main() {
ctx := context.Background()
// Connection to database
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
defer driver.Close(ctx)
if err != nil {
panic(err)
}
// Define a date, with timezone
location, _ := time.LoadLocation("Europe/Stockholm")
friendsSince := time.Date(2006, time.December, 16, 13, 59, 59, 999999999, location)
result, err := neo4j.ExecuteQuery(ctx, driver, `
MERGE (a:Person {name: $name})
MERGE (b:Person {name: $friend})
MERGE (a)-[friendship:KNOWS {since: $friendsSince}]->(b)
RETURN friendship.since AS date
`, map[string]any{
"name": "Alice",
"friend": "Bob",
"friendsSince": friendsSince,
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
if err != nil {
panic(err)
}
date, _ := result.Records[0].Get("date")
fmt.Println(reflect.TypeOf(date)) // time.Time
fmt.Println(date) // 2006-12-16 13:59:59.999999999 +0200 EET
}
package main
import (
"fmt"
"context"
"time"
"github.com/neo4j/neo4j-go-driver/v6/neo4j"
"reflect"
)
func main() {
ctx := context.Background()
// Connection to database
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
defer driver.Close(ctx)
if err != nil {
panic(err)
}
// Query and return a neo4j.Time object
result, err := neo4j.ExecuteQuery(ctx, driver, `
MERGE (a:Person {name: $name})
MERGE (b:Person {name: $friend})
MERGE (a)-[friendship:KNOWS {since: time()}]->(b)
RETURN friendship.since AS time
`, map[string]any{
"name": "Alice",
"friend": "Sofia",
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
if err != nil {
panic(err)
}
time, _ := result.Records[0].Get("time")
fmt.Println(reflect.TypeOf(time)) // time.Time
castDate, _ := time.(neo4j.Time) // cast from `any` to `neo4j.Time`
fmt.Println(castDate.Time()) // -0001-11-30 12:18:08.973 +0000 Offset
}
Duration
Represents the difference between two points in time.
duration := neo4j.Duration{
Months: 1,
Days: 2,
Seconds: 3,
Nanos: 4,
}
fmt.Println(duration) // 'P1Y2DT3.000000004S'
For full documentation, see API documentation → Duration.
Spatial types
Cypher supports spatial values (points), and Neo4j can store these point values as properties on nodes and relationships.
The object attribute SpatialRefId (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 | Go Type | SpatialRefId |
|---|---|---|
|
|
7203 |
|
|
4326 |
|
|
9157 |
|
|
4979 |
Spatial types are implemented in the dbtype package, so that the actual types are dbtype.Point2D/3D.
However, they are also imported in the main neo4j package, so that they can also be used as neo4j.Point2D/3D.
|
Point2D
The type Point2D can be used to represent either a 2D Cartesian point or a 2D World Geodetic System (WGS84) point, depending on the value of SpatialRefId.
// A 2D Cartesian Point
cartesian2d := neo4j.Point2D{
X: 1.23,
Y: 4.56,
SpatialRefId: 7203,
}
fmt.Println(cartesian2d)
// Point{srId=7203, x=1.230000, y=4.560000}
// A 2D WGS84 Point
wgs842d := neo4j.Point2D{
X: 1.23,
Y: 4.56,
SpatialRefId: 9157,
}
fmt.Println(wgs842d)
// Point{srId=9157, x=1.230000, y=4.560000}
Point3D
The type Point3D can be used to represent either a 3D Cartesian point or a 3D World Geodetic System (WGS84) point, depending on the value of SpatialRefId.
// A 3D Cartesian Point
cartesian3d := neo4j.Point3D{
X: 1.23,
Y: 4.56,
Z: 7.89,
SpatialRefId: 9157,
}
fmt.Println(cartesian3d)
// Point{srId=9157, x=1.230000, y=4.560000, z=7.890000}
// A 3D WGS84 Point
wgs843d := neo4j.Point3D{
X: 1.23,
Y: 4.56,
Z: 7.89,
SpatialRefId: 4979,
}
fmt.Println(wgs843d)
// Point{srId=4979, x=1.230000, y=4.560000, z=7.890000}
Graph types
Graph types are only returned as query results and may not be used as parameters.
| Cypher Type | Python Type |
|---|---|
|
|
|
|
|
|
Node
Represents a node in a graph.
The property ElementId contains the database internal 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 ElementId to MATCH an element across different transactions is risky.
result, err := neo4j.ExecuteQuery(ctx, driver, `
MERGE (p:Person {name: $name}) RETURN p AS person, p.name as name
`, map[string]any{
"name": "Alice",
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
if err != nil {
panic(err)
}
node, _ := result.Records[0].AsMap()["person"].(neo4j.Node)
fmt.Println("Node ID:", node.ElementId)
fmt.Println("Node labels:", node.Labels)
fmt.Println("Node properties:", node.Props)
// Node ID: 4:2691aa68-87cc-467d-9d09-431df9f5c456:0
// Node labels: [Person]
// Node properties: map[name:Alice]
For full documentation, see API documentation → Node.
Relationship
Represents a relationship in a graph.
The property ElementId contains the database internal 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.
result, err := neo4j.ExecuteQuery(ctx, driver, `
MERGE (p:Person {name: $name})
MERGE (p)-[r:KNOWS {status: $status, since: date()}]->(friend:Person {name: $friendName})
RETURN r AS friendship
`, map[string]any{
"name": "Alice",
"status": "BFF",
"friendName": "Bob",
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
if err != nil {
panic(err)
}
relationship, _ := result.Records[0].AsMap()["friendship"].(neo4j.Relationship)
fmt.Println("Relationship ID:", relationship.ElementId)
fmt.Println("Relationship type:", relationship.Type)
fmt.Println("Relationship properties:", relationship.Props)
fmt.Println("Relationship start elID:", relationship.StartElementId)
fmt.Println("Relationship end elID:", relationship.EndElementId)
// Relationship ID: 5:2691aa68-87cc-467d-9d09-431df9f5c456:0
// Relationship type: KNOWS
// Relationship properties: map[since:{0 63824025600 <nil>} status:BFF]
// Relationship start elID: 4:2691aa68-87cc-467d-9d09-431df9f5c456:0
// Relationship end elID: 4:2691aa68-87cc-467d-9d09-431df9f5c456:1
For full documentation, see API documentation → Relationship.
Path
Represents a path in a graph.
package main
import (
"fmt"
"context"
"github.com/neo4j/neo4j-go-driver/v6/neo4j"
)
func main() {
ctx := context.Background()
// Connection to database
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
defer driver.Close(ctx)
if err != nil {
panic(err)
}
// Create some :Person nodes linked by :KNOWS relationships
addFriend(ctx, driver, "Alice", "BFF", "Bob")
addFriend(ctx, driver, "Bob", "Fiends", "Sofia")
addFriend(ctx, driver, "Sofia", "Acquaintances", "Sofia")
// Follow :KNOWS relationships outgoing from Alice three times, return as path
result, err := neo4j.ExecuteQuery(ctx, driver, `
MATCH path=(:Person {name: $name})-[:KNOWS*3]->(:Person)
RETURN path AS friendshipChain
`, map[string]any{
"name": "Alice",
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
if err != nil {
panic(err)
}
path := result.Records[0].AsMap()["friendshipChain"].(neo4j.Path)
fmt.Println("-- Path breakdown --")
for i := range path.Relationships {
name := path.Nodes[i].Props["name"]
status := path.Relationships[i].Props["status"]
friendName := path.Nodes[i+1].Props["name"]
fmt.Printf("%s is friends with %s (%s)\n", name, friendName, status)
}
}
func addFriend(ctx context.Context, driver neo4j.Driver, name string, status string, friendName string) {
_, err := neo4j.ExecuteQuery(ctx, driver, `
MERGE (p:Person {name: $name})
MERGE (p)-[r:KNOWS {status: $status, since: date()}]->(friend:Person {name: $friendName})
`, map[string]any{
"name": name,
"status": status,
"friendName": friendName,
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
if err != nil {
panic(err)
}
}
For full documentation, see API documentation → Path.
VectorIntroduced in 6.0
The type Vector maps to the Cypher type VECTOR.
Vector objects are stored as contiguous blocks of memory, containing homogeneous values.
You can create vectors from lists of float64, float32, int8, int16, int32, int64.
Storing VECTOR objects in the database requires a server version >= 2025.10, Enterprise Edition.
|
package main
import (
"context"
"fmt"
"github.com/neo4j/neo4j-go-driver/v6/neo4j"
)
func main() {
// Connection to database
ctx := context.Background()
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
defer driver.Close(ctx)
if err != nil {
panic(err)
}
// Create a vector...
vec := neo4j.Vector[float64]{Elems: []float64{1.0, 2.0, 3.0}}
// ... and store it as a node property
_, err = neo4j.ExecuteQuery(ctx, driver,
"MERGE (n:Doc {vector: $vec})",
map[string]any{"vec": vec},
neo4j.EagerResultTransformer)
if err != nil {
panic(err)
}
// Retrieve the vector from the database
result, err := neo4j.ExecuteQuery(ctx, driver,
"MATCH (n:Doc) RETURN n.vec AS vec LIMIT 1",
nil,
neo4j.EagerResultTransformer)
if err != nil {
panic(err)
}
record := result.Records[0]
if v, ok := record.Values[0].(neo4j.Vector[float64]); ok {
fmt.Printf("Read vector: %v\n", v)
}
// Read vector: vector([1.0, 2.0, 3.0], 3, FLOAT64 NOT NULL)
}
UnsupportedTypeIntroduced in 6.0
The type UnsupportedType is used for data types returned by Cypher queries, but that the driver doesn’t recognize.
This happens when the client version is too old with respect to the server version.
Glossary
- LTS
-
A Long Term Support release is one guaranteed to be supported for a number of years. Neo4j 4.4 and 5.26 are LTS versions.
- 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.
- bookmark
-
A bookmark is a token representing some state of the database. By passing one or multiple bookmarks along with a query, the server will make sure that the query does not get executed before the represented state(s) have been established.
- transaction function
-
A transaction function is a callback executed by an
ExecuteReadorExecuteWritecall. The driver automatically re-executes the callback in case of server failure. - Driver
-
A
Driverobject holds the details required to establish connections with a Neo4j database.