数据类型和 Cypher 类型映射

本节中的表格显示了 Cypher 数据类型与 Go 类型之间的映射关系。

当访问记录的内容时,其所有属性都为 any 类型。这意味着如果您想要使用在这些类型上定义的方法/功能,则必须将其强制转换为相关的 Go 类型。例如,如果来自数据库的 name 属性是字符串,则 record.AsMap()["name"][1] 会在编译时导致无效操作错误。为了使其工作,在使用它作为字符串之前将其强制转换为字符串:name := record.AsMap()["name"].(string),然后 name[1]

核心类型

Cypher 类型 Go 类型

NULL

nil

LIST

[]any

MAP

map[string]any

BOOLEAN

bool

INTEGER

int64

FLOAT

float64

STRING

string

ByteArray

[]byte

时间类型

驱动程序提供了一组符合 ISO-8601 和 Cypher 的时间数据类型。亚秒值以纳秒精度测量。

驱动程序的类型依赖于 Go 的 time 类型。除 neo4j.Duration 外,所有时间类型实际上都是 time.Date 对象。

  • 如果您想查询数据库中的时间类型,请实例化一个 time.Date 对象并将其用作查询参数(即,您无需关心驱动程序的类型)。

  • 如果您从 time.Date 对象检索之前插入的时间对象,您将获得 time.Date 对象(即,您无需关心驱动程序的类型)。

  • 如果您使用 Cypher 时间函数 中的任何一个接收时间对象,您将获得下表中所示的相应驱动程序类型。您随后可以使用 .Time() 将它们转换为 Go time.Date 对象。

    Cypher 类型 Go 类型

    DATE

    neo4j.Date

    ZONED TIME

    neo4j.OffsetTime

    LOCAL TIME

    neo4j.LocalTime

    ZONED DATETIME

    neo4j.Time

    LOCAL DATETIME

    neo4j.LocalDateTime

    DURATION

    neo4j.Duration

在查询中使用时间类型
package main

import (
    "fmt"
    "context"
    "time"
    "github.com/neo4j/neo4j-go-driver/v5/neo4j"
    "reflect"
)

func main() {
    ctx := context.Background()

    // Connection to database
    dbUri := "<URI for Neo4j database>"
    dbUser := "<Username>"
    dbPassword := "<Password>"
    driver, _ := neo4j.NewDriverWithContext(
        dbUri,
        neo4j.BasicAuth(dbUser, dbPassword, ""))
    driver.VerifyConnectivity(ctx)

    // 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("neo4j"))
    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/v5/neo4j"
    "reflect"
)

func main() {
    ctx := context.Background()

    // Connection to database
    dbUri := "<URI for Neo4j database>"
    dbUser := "<Username>"
    dbPassword := "<Password>"
    driver, _ := neo4j.NewDriverWithContext(
        dbUri,
        neo4j.BasicAuth(dbUser, dbPassword, ""))
    driver.VerifyConnectivity(ctx)

    // 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("neo4j"))
    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

表示两个时间点之间的差值。

duration := neo4j.Duration{
    Months: 1,
    Days: 2,
    Seconds: 3,
    Nanos: 4,
}
fmt.Println(duration)  // 'P1Y2DT3.000000004S'

有关完整文档,请参阅 API 文档 → Duration

空间类型

Cypher 支持 空间值(点),Neo4j 可以将这些点值存储为节点和关系上的属性。

对象属性 SpatialRefId空间参考标识符的缩写)是一个数字,用于识别要解释空间类型的坐标系。您可以将其视为每个空间类型的唯一标识符。

Cypher 类型 Go 类型 SpatialRefId

POINT (2D Cartesian)

neo4j.Point2D

7203

POINT (2D WGS-84)

neo4j.Point2D

4326

POINT (3D Cartesian)

neo4j.Point3D

9157

POINT (3D WGS-84)

neo4j.Point3D

4979

空间类型在 dbtype 包中实现,因此实际类型为 dbtype.Point2D/3D。但是,它们也导入到主 neo4j 包中,因此它们也可以用作 neo4j.Point2D/3D

Point2D

Point2D 类型可用于表示二维笛卡尔点或二维世界大地测量系统 (WGS84) 点,具体取决于 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

Point3D 类型可用于表示三维笛卡尔点或三维世界大地测量系统 (WGS84) 点,具体取决于 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}

图形类型

图形类型仅作为查询结果返回,不能用作参数.

Cypher 类型 Python 类型

NODE

dbtype.Node

RELATIONSHIP

dbtype.Relationship

PATH

dbtype.Path

Node

表示图中的节点。
属性 ElementId 包含实体的数据库内部标识符。应谨慎使用此标识符,因为不保证在单个事务范围之外 id 值与元素之间的映射关系。换句话说,使用 ElementId 在不同事务中 MATCH 元素是有风险的。

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("neo4j"))
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]

有关完整文档,请参阅 API 文档 → Node

Relationship

表示图中的关系。
属性 ElementId 包含实体的数据库内部标识符。应谨慎使用此标识符,因为不保证在单个事务范围之外 id 值与元素之间的映射关系。

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("neo4j"))
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

有关完整文档,请参阅 API 文档 → Relationship

Path

表示图中的路径。

路径创建、检索和处理的示例
package main

import (
    "fmt"
    "context"
    "github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

func main() {
    ctx := context.Background()

    // Connection to database
    dbUri := "<URI for Neo4j database>"
    dbUser := "<Username>"
    dbPassword := "<Password>"
    driver, _ := neo4j.NewDriverWithContext(
        dbUri,
        neo4j.BasicAuth(dbUser, dbPassword, ""))
    driver.VerifyConnectivity(ctx)

    // 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("neo4j"))
    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.DriverWithContext, 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("neo4j"))
    if err != nil {
        panic(err)
    }
}

有关完整文档,请参阅 API 文档 → Path

异常

在大多数情况下,驱动程序只是转发服务器可能引发的任何错误。有关服务器可以返回的错误列表,请参阅 状态代码 页面。

某些服务器错误被标记为可以安全地重试,而无需更改原始请求。此类错误的示例包括死锁、内存问题或连接问题。当引发错误时,函数 neo4j.IsRetryable(error) 会提供有关后续尝试是否可能成功的见解。这在使用 显式事务 运行查询时尤其有用,以便了解失败的查询是否应该再次运行。请注意,托管事务 已经实现了重试机制,因此您无需实现自己的重试机制。

词汇表

LTS

长期支持版本是指保证支持多年的版本。Neo4j 4.4 是 LTS,Neo4j 5 也将具有 LTS 版本。

Aura

Aura 是 Neo4j 的完全托管云服务。它提供免费和付费计划。

Cypher

Cypher 是 Neo4j 的图查询语言,允许您从数据库中检索数据。它类似于 SQL,但适用于图。

APOC

Awesome Procedures On Cypher (APOC) 是一个包含(许多)函数的库,这些函数无法轻松地用 Cypher 本身表达。

Bolt

Bolt 是 Neo4j 实例和驱动程序之间交互使用的协议。默认情况下,它监听端口 7687。

ACID

原子性、一致性、隔离性、持久性 (ACID) 是保证数据库事务可靠处理的属性。符合 ACID 的 DBMS 确保数据库中的数据即使在发生故障的情况下也能保持准确性和一致性。

最终一致性

如果数据库提供了以下保证,则它是最终一致的:所有集群成员将在 *某个时间点* 存储数据的最新版本。

因果一致性

如果每个集群成员都以相同的顺序看到读写查询,则数据库是因果一致的。这比 *最终一致性* 更强大。

NULL

空标记不是一种类型,而是值不存在的占位符。有关更多信息,请参阅 Cypher → 使用 null

事务

事务是工作单元,要么 *完全提交*,要么在失败时 *回滚*。例如,银行转账:它涉及多个步骤,但这些步骤必须 *全部* 成功或回滚,以避免从一个账户中扣款但没有添加到另一个账户中。

背压

背压是阻止数据流的力。它确保客户端不会被数据淹没,而数据流的速度超过了客户端的处理能力。

事务函数

事务函数是在 ExecuteReadExecuteWrite 调用执行的回调。如果发生服务器故障,驱动程序会自动重新执行回调。

DriverWithContext

一个 DriverWithContext 对象包含与 Neo4j 数据库建立连接所需的所有详细信息。