查询数据库
连接到数据库后,您可以使用 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] 。 |
更新数据库
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 子句 MATCH
和 CREATE
Alice
和 Bob
之间创建一个 :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 的节点发出,并将其附加到名为 Bob 的 Person 节点 |
从数据库中删除
要删除节点及其连接的所有关系,请使用 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 参数,如前面的示例所示。 这样做是为了
-
性能优势:Neo4j 编译并缓存查询,但只有在查询结构保持不变的情况下才能这样做;
-
安全原因:请参阅 防止 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
。 - 事务
-
事务是工作的基本单元,要么完全提交,要么在失败时回滚。例如银行转账:它涉及多个步骤,但必须全部成功或被还原,以避免资金从一个账户中扣除但未添加到另一个账户中。
- 背压
-
背压是一种阻碍数据流动的力量。它确保客户端不会被超出其处理能力的数据淹没。
- 事务函数
-
事务函数是通过
ExecuteRead
或ExecuteWrite
调用执行的回调函数。驱动程序会在服务器故障的情况下自动重新执行回调函数。 - DriverWithContext
-
一个
DriverWithContext
对象包含建立与 Neo4j 数据库连接所需的信息。