查询数据库

连接到数据库后,您可以使用 Cypher 和函数 ExecuteQuery() 运行查询。

ExecuteQuery() 在驱动程序的 5.8 版本中引入。
对于早期版本的查询,请使用 会话和事务

写入数据库

要创建一个表示名为 Alice 的人的节点,请使用 Cypher 子句 CREATE

创建一个表示名为 Alice 的人的节点
result, err := neo4j.ExecuteQuery(ctx, driver,
    "CREATE (p:Person {name: $name}) RETURN p",  (1)
    map[string]any{  (2)
        "name": "Alice",
    }, neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))  (3)
if err != nil {
    panic(err)
}

summary := result.Summary  (4)
fmt.Printf("Created %v nodes in %+v.\n",
    summary.Counters().NodesCreated(),
    summary.ResultAvailableAfter())
1 Cypher 查询
2 查询参数 的映射
3 应在哪个数据库上运行查询
4 服务器返回的 执行摘要

从数据库读取

要从数据库检索信息,请使用 Cypher 子句 MATCH

检索所有 Person 节点
result, err := neo4j.ExecuteQuery(ctx, driver,
    "MATCH (p:Person) RETURN p.name AS name",
    nil,
    neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))
if err != nil {
    panic(err)
}

// Loop through results and do something with them
for _, record := range result.Records {  (1)
    name, _ := record.Get("name")  // .Get() 2nd return is whether key is present
    fmt.Println(name)
    // or
    // fmt.Println(record.AsMap())  // get Record as a map
}

// Summary information  (2)
fmt.Printf("The query `%v` returned %v records in %+v.\n",
    result.Summary.Query().Text(), len(result.Records),
    result.Summary.ResultAvailableAfter())
1 result.Records 包含结果,作为 Record 对象的数组
2 result.Summary 包含服务器返回的 执行摘要
当访问记录的内容时,所有属性都为 any 类型。 这意味着如果您想使用在这些类型上定义的方法/功能,则必须将它们强制转换为相关的 Go 类型。 例如,如果来自数据库的 name 属性是字符串,则 record.AsMap()["name"][1] 会在编译时导致无效操作 错误。 为了使其正常工作,请在将其用作字符串之前 将值强制转换为字符串:name := record.AsMap()["name"].(string),然后 name[1]

更新数据库

要更新数据库中节点的信息,请使用 Cypher 子句 MATCHSET

更新节点 Alice 以添加 age 属性
result, err := neo4j.ExecuteQuery(ctx, driver, `
    MATCH (p:Person {name: $name})
    SET p.age = $age
    `, map[string]any{
        "name": "Alice",
        "age": 42,
    }, neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))
if err != nil {
    panic(err)
}
fmt.Println("Query updated the database?",
    result.Summary.Counters().ContainsUpdates())

要创建一个新的关系,将其链接到两个已经存在的节点,请结合使用 Cypher 子句 MATCHCREATE

AliceBob 之间创建一个 :KNOWS 关系
result, err := neo4j.ExecuteQuery(ctx, driver, `
    MATCH (alice:Person {name: $name})  (1)
    MATCH (bob:Person {name: $friend})  (2)
    CREATE (alice)-[:KNOWS]->(bob)  (3)
    `, map[string]any{
        "name": "Alice",
        "friend": "Bob",
    }, neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))
if err != nil {
    panic(err)
}
fmt.Println("Query updated the database?",
    result.Summary.Counters().ContainsUpdates())
1 检索名为 Alice 的人员节点并将其绑定到变量 alice
2 检索名为 Bob 的人员节点并将其绑定到变量 bob
3 创建一个新的 :KNOWS 关系,从绑定到 alice 的节点发出,并将其附加到名为 BobPerson 节点

从数据库中删除

要删除节点及其连接的所有关系,请使用 Cypher 子句 DETACH DELETE

删除 Alice 节点及其所有关系
// This does not delete _only_ p, but also all its relationships!
result, err := neo4j.ExecuteQuery(ctx, driver, `
    MATCH (p:Person {name: $name})
    DETACH DELETE p
    `, map[string]any{
        "name": "Alice",
    }, neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))
if err != nil {
    panic(err)
}
fmt.Println("Query updated the database?",
    result.Summary.Counters().ContainsUpdates())

查询参数

不要将参数直接硬编码或连接到查询中。 相反,始终使用占位符并指定 Cypher 参数,如前面的示例所示。 这样做是为了

  1. 性能优势:Neo4j 编译并缓存查询,但只有在查询结构保持不变的情况下才能这样做;

  2. 安全原因:请参阅 防止 Cypher 注入

查询参数应该被分组到一个映射中,并作为第二个参数传递给 ExecuteQuery()。 如果查询没有参数,则可以传递 nil 而不是空映射。

parameters := map[string]any{
    "name": "Alice",
    "age": 42,
}
neo4j.ExecuteQuery(ctx, driver,
    "MERGE (:Person {name: $name, age: $age})",
    parameters,
    neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))
在某些情况下,您的查询结构可能会阻止在所有部分中使用参数。 对于这些罕见的情况,请参阅 属性键、关系类型和标签中的动态值

查询配置

您可以提供其他配置参数来更改 ExecuteQuery() 的默认行为。 这些作为从第 4 个函数参数开始的任意数量的回调提供。

数据库选择

建议始终使用 neo4j.ExecuteQueryWithDatabase("<dbName>") 回调显式指定数据库,即使在单数据库实例上也是如此。 这使驱动程序能够更高效地工作,因为它节省了到服务器的网络往返来解析主数据库。 如果没有提供数据库,则使用 用户的主数据库

neo4j.ExecuteQuery(ctx, driver,
    "MATCH (p:Person) RETURN p.name",
    nil,
    neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"))
通过配置方法指定数据库优于使用 USE Cypher 子句。 如果服务器在集群上运行,则使用 USE 的查询需要启用服务器端路由。 查询的执行时间也可能更长,因为它们可能在第一次尝试时没有到达正确的集群成员,并且需要路由到包含请求数据库的成员。

请求路由

在集群环境中,默认情况下,所有查询都将定向到领导者节点。 为了提高读取查询的性能,您可以使用回调 neo4j.ExecuteQueryWithReadersRouting() 将查询路由到读取节点。

neo4j.ExecuteQuery(ctx, driver,
    "MATCH (p:Person) RETURN p.name",
    nil,
    neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"),
    neo4j.ExecuteQueryWithReadersRouting())

尽管在读取模式下执行写入查询可能会导致运行时错误,但您不应该依赖此方法进行访问控制。 两种模式之间的区别在于,读取 事务将被路由到集群的任何节点,而写入 事务将被定向到领导者。 换句话说,不能保证以读取模式提交的写入查询会被拒绝。

以其他用户身份运行查询

您可以使用回调 neo4j.ExecuteQueryWithImpersonatedUser("<somebodyElse>") 在不同用户的安全上下文中执行查询,指定要模拟的用户的名字。 为了使此方法有效,创建 DriverWithContext 对象的用户需要具有 适当的权限。 模拟用户比创建新的 DriverWithContext 对象更便宜。

neo4j.ExecuteQuery(ctx, driver,
    "MATCH (p:Person) RETURN p.name",
    nil,
    neo4j.EagerResultTransformer,
    neo4j.ExecuteQueryWithDatabase("neo4j"),
    neo4j.ExecuteQueryWithImpersonatedUser("<somebodyElse>"))

模拟用户时,查询将在模拟用户的完整安全上下文中运行,而不是在经过身份验证的用户(即主数据库、权限等)上下文中运行。

完整示例

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, err := neo4j.NewDriverWithContext(
        dbUri,
        neo4j.BasicAuth(dbUser, dbPassword, ""))
    if err != nil {
        panic(err)
    }
    defer driver.Close(ctx)
    err = driver.VerifyConnectivity(ctx)
    if err != nil {
        panic(err)
    }

    // Prepare data
    people := []map[string]any {
       {"name": "Alice", "age": 42, "friends": []string{"Bob", "Peter", "Anna"},},
       {"name": "Bob", "age": 19,},
       {"name": "Peter", "age": 50,},
       {"name": "Anna", "age": 30,},
    }

    // Create some nodes
    for _, person := range people {
        _, err := neo4j.ExecuteQuery(ctx, driver,
            "MERGE (p:Person {name: $person.name, age: $person.age})",
            map[string]any{
                "person": person,
            }, neo4j.EagerResultTransformer,
            neo4j.ExecuteQueryWithDatabase("neo4j"))
        if err != nil {
            panic(err)
        }
    }

    // Create some relationships
    for _, person := range people {
        if person["friends"] != "" {
            _, err := neo4j.ExecuteQuery(ctx, driver, `
                MATCH (p:Person {name: $person.name})
                UNWIND $person.friends AS friend_name
                MATCH (friend:Person {name: friend_name})
                MERGE (p)-[:KNOWS]->(friend)
                `, map[string]any{
                    "person": person,
                }, neo4j.EagerResultTransformer,
                neo4j.ExecuteQueryWithDatabase("neo4j"))
            if err != nil {
                panic(err)
            }
        }
    }

    // Retrieve Alice's friends who are under 40
    result, err := neo4j.ExecuteQuery(ctx, driver, `
        MATCH (p:Person {name: $name})-[:KNOWS]-(friend:Person)
        WHERE friend.age < $age
        RETURN friend
        `, map[string]any{
            "name": "Alice",
            "age": 40,
        }, neo4j.EagerResultTransformer,
        neo4j.ExecuteQueryWithDatabase("neo4j"))
    if err != nil {
        panic(err)
    }

    // Loop through results and do something with them
    for _, record := range result.Records {
        person, _ := record.Get("friend")
        fmt.Println(person)
        // or
        // fmt.Println(record.AsMap())
    }

    // Summary information
    fmt.Printf("\nThe query `%v` returned %v records in %+v.\n",
        result.Summary.Query().Text(), len(result.Records),
        result.Summary.ResultAvailableAfter())
}

有关更多信息,请参阅 API 文档 → ExecuteQuery()

词汇表

LTS

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

Aura

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

Cypher

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

APOC

Cypher 上的强大过程 (APOC) 是一个包含(许多)函数的库,这些函数无法轻松地用 Cypher 本身表达。

Bolt

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

ACID

原子性、一致性、隔离性、持久性 (ACID) 是保证数据库事务可靠处理的属性。 符合 ACID 的 DBMS 确保数据库中的数据在出现故障时仍然准确一致。

最终一致性

如果数据库保证所有集群成员在某个时间点 将存储数据的最新版本,则该数据库最终一致。

因果一致性

如果数据库保证所有集群成员都以相同的顺序看到读写查询,则该数据库具有因果一致性。 这比最终一致性 更强。

NULL

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

事务

事务是工作的基本单元,要么完全提交,要么在失败时回滚。例如银行转账:它涉及多个步骤,但必须全部成功或被还原,以避免资金从一个账户中扣除但未添加到另一个账户中。

背压

背压是一种阻碍数据流动的力量。它确保客户端不会被超出其处理能力的数据淹没。

事务函数

事务函数是通过ExecuteReadExecuteWrite调用执行的回调函数。驱动程序会在服务器故障的情况下自动重新执行回调函数。

DriverWithContext

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