其他查询机制
隐式(或自动提交)事务
隐式事务是唯一可用于 CALL { … } IN TRANSACTIONS 查询的事务类型。 |
隐式事务是最基础且受限的事务形式。驱动程序**不会**自动重试隐式事务,而使用 ExecuteQuery() 和 托管事务 运行的查询则会进行自动重试。只有在其他查询接口不适用或仅用于快速原型设计时,才应使用隐式事务。
你可以使用 Session.Run() 方法运行隐式事务。它返回一个 Result 对象,该对象需要被相应地处理。
session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "<database-name>"})
defer session.Close(ctx)
result, err := session.Run(
ctx,
"CREATE (p:Person {name: $name}) RETURN p",
map[string]any{
"name": "Lucia",
})
为确保隐式事务被提交,请通过调用结果上的 .Consume(ctx) 或迭代所有记录来消耗掉所有记录。如果不消耗所有记录,可能会导致意想不到的行为:无法保证隐式事务在会话生命周期内的确切提交时间。
由于驱动程序无法判断 Session.Run() 调用中的查询需要读取还是写入会话,因此默认使用写入模式。如果你的隐式事务仅包含读取查询,通过在创建会话时设置会话配置 AccessMode: neo4j.AccessModeRead,告知驱动程序可以提升性能。
导入 CSV 文件
使用 Session.Run() 最常见的用例是使用 LOAD CSV Cypher 子句将大型 CSV 文件导入数据库,并防止因事务过大而导致的超时错误。
session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "<database-name>"})
defer session.Close(ctx)
result, err := session.Run(
ctx, `
LOAD CSV FROM 'https://data.neo4j.com/bands/artists.csv' AS line
CALL {
WITH line
MERGE (:Artist {name: line[1], age: toInteger(line[2])})
} IN TRANSACTIONS OF 2 ROWS
`, nil)
summary, _ := result.Consume(ctx)
fmt.Println("Query updated the database?",
summary.Counters().ContainsUpdates())
虽然 LOAD CSV 很方便,但将 CSV 文件的解析工作推迟到 Go 应用程序中并避免使用 LOAD CSV 并没有什么不妥。事实上,将解析逻辑移至应用程序可以让你更好地控制导入过程。有关高效批量数据插入的信息,请参阅 性能 → 批量数据创建。 |
更多信息,请参阅 Cypher → 子句 → Load CSV。
事务配置
你可以通过在 Session.Run() 调用的第三个参数之后提供配置回调,来进一步控制隐式事务。配置回调允许指定查询超时时间并为事务附加元数据。更多信息,请参阅 事务 — 事务配置。
session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "<database-name>"})
defer session.Close(ctx)
people, err := session.Run(ctx,
"MATCH (:Person) RETURN count(*) AS n",
nil,
neo4j.WithTxTimeout(5*time.Second), // remember to import `time`
neo4j.WithTxMetadata(map[string]any{"appName": "peopleTracker"}))
属性键、关系类型和标签中的动态值
通常,你不应该直接将参数拼接到查询字符串中,而应该使用 查询参数。然而,在某些情况下,查询结构可能导致无法在所有地方使用参数。实际上,尽管参数适用于字面量、表达式以及 节点标签和关系类型,但它们不能用于属性键,因此 MATCH (n) WHERE n.$param = 'something' 是无效的。
使用字符串拼接时,请将动态值用反引号括起来,并自行进行转义以防止 Cypher 注入。请注意,Cypher 处理 Unicode,因此也要留意 Unicode 字面量 \u0060。
dangerousKey := "name\\u0060n"
// convert \u0060 to literal backtick and then escape backticks
escapedKey := strings.ReplaceAll(dangerousKey, "\\u0060", "`")
escapedKey = strings.ReplaceAll(escapedKey, "`", "``")
result, err := neo4j.ExecuteQuery(ctx, driver,
"MATCH (p:Person {`" + escapedKey + "`: $name}) RETURN p.name",
map[string]any{
"name": "Alice",
},
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
// rewritten to
// MATCH (p:Person {`name```: $name}) RETURN p.name
避免字符串拼接的另一种变通方法是使用 APOC 过程,例如 apoc.merge.node,它支持动态标签和属性键。
apoc.merge.node 创建带有动态标签/属性键的节点propertyKey := "name"
label := "Person"
result, err := neo4j.ExecuteQuery(ctx, driver,
"CALL apoc.merge.node($labels, $properties)",
map[string]any{
"labels": []string{label},
"properties": map[string]any{propertyKey: "Alice"},
},
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
| 如果你在 Docker 中运行 Neo4j,则在启动容器时需要启用 APOC。请参阅 APOC → 安装 → Docker。 |
日志记录
驱动程序将日志分为驱动程序事件和 Bolt 事件。要启用驱动程序日志,请在实例化驱动程序时使用 Config.Log 选项。
// import "github.com/neo4j/neo4j-go-driver/v6/neo4j/config"
// import "github.com/neo4j/neo4j-go-driver/v6/neo4j/log"
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""),
func(conf *config.Config) {
conf.Log = log.ToConsole(log.DEBUG)
})
2023-07-03 08:07:19.316 INFO [pool 1] Created
2023-07-03 08:07:19.316 INFO [router 1] Created {context: map[address:localhost:7687]}
2023-07-03 08:07:19.316 INFO [driver 1] Created { target: localhost:7687 }
2023-07-03 08:07:19.316 DEBUG [session 2] Created
2023-07-03 08:07:19.316 INFO [router 1] Reading routing table from initial router: localhost:7687
2023-07-03 08:07:19.316 DEBUG [pool 1] Trying to borrow connection from [localhost:7687]
2023-07-03 08:07:19.316 INFO [pool 1] Connecting to localhost:7687
2023-07-03 08:07:19.320 INFO [bolt5 bolt-58@localhost:7687] Connected
2023-07-03 08:07:19.320 INFO [bolt5 bolt-58@localhost:7687] Retrieving routing table
2023-07-03 08:07:19.320 DEBUG [pool 1] Returning connection to localhost:7687 {alive:true}
2023-07-03 08:07:19.320 DEBUG [bolt5 bolt-58@localhost:7687] Resetting connection internal state
2023-07-03 08:07:19.320 DEBUG [router 1] New routing table for 'neo4j', TTL 300
2023-07-03 08:07:19.320 DEBUG [session 2] Resolved home database, uses db 'neo4j'
2023-07-03 08:07:19.320 DEBUG [pool 1] Trying to borrow connection from [localhost:7687]
2023-07-03 08:07:19.321 DEBUG [pool 1] Returning connection to localhost:7687 {alive:true}
2023-07-03 08:07:19.321 DEBUG [bolt5 bolt-58@localhost:7687] Resetting connection internal state
2023-07-03 08:07:19.321 DEBUG [router 1] Cleaning up
2023-07-03 08:07:19.321 DEBUG [session 2] Closed
Bolt 日志可以通过以下方式启用:
-
按查询:使用配置回调
neo4j.ExecuteQueryBoltLogger()。这适用于使用ExecuteQuery()运行的单个查询。 -
按会话:使用配置选项
BoltLogger。这适用于会话内的所有查询。
ExecuteQuery 运行的查询启用日志记录result, err := neo4j.ExecuteQuery(ctx, driver,
"RETURN 42 AS n", nil, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"),
neo4j.ExecuteQueryWithBoltLogger(log.BoltToConsole()))
session := driver.NewSession(ctx, neo4j.SessionConfig{
DatabaseName: "<database-name>",
BoltLogger: log.BoltToConsole(),
})
defer session.Close(ctx)
session.Run(ctx, "RETURN 42 AS n", nil)
2023-07-03 07:57:09.929 BOLT [bolt-53@localhost:7687] C: BEGIN {"db":"<database-name>"}
2023-07-03 07:57:09.930 BOLT [bolt-53@localhost:7687] S: SUCCESS {}
2023-07-03 07:57:09.930 BOLT [bolt-53@localhost:7687] C: RUN "RETURN 42 AS n" null null
2023-07-03 07:57:09.930 BOLT [bolt-53@localhost:7687] C: PULL {"n":1000}
2023-07-03 07:57:09.936 BOLT [bolt-53@localhost:7687] S: SUCCESS {"fields":["n"],"t_first":5}
2023-07-03 07:57:09.937 BOLT [bolt-53@localhost:7687] S: RECORD [42]
2023-07-03 07:57:09.937 BOLT [bolt-53@localhost:7687] S: SUCCESS {"t_first":1,"db":"<database-name>"}
2023-07-03 07:57:09.937 BOLT [bolt-53@localhost:7687] C: COMMIT
2023-07-03 07:57:09.938 BOLT [bolt-53@localhost:7687] S: SUCCESS {"bookmark":"FB:kcwQhRyDJPONRxudy+QyzPSuSAaQ"}
术语表
- LTS (长期支持版)
-
长期支持 (Long Term Support) 版本是保证在若干年内得到支持的版本。Neo4j 4.4 和 5.26 是 LTS 版本。
- Aura
-
Aura 是 Neo4j 的全托管云服务。它提供免费和付费计划。
- Cypher
-
Cypher 是 Neo4j 的图查询语言,允许您从数据库中检索数据。它就像 SQL,但专用于图数据库。
- APOC
-
Awesome Procedures On Cypher (APOC) 是一个包含(许多)函数的库,这些函数在 Cypher 本身中难以轻松实现。
- Bolt
-
Bolt 是用于 Neo4j 实例和驱动程序之间交互的协议。默认监听 7687 端口。
- ACID
-
原子性 (Atomicity)、一致性 (Consistency)、隔离性 (Isolation)、持久性 (Durability) (ACID) 是保证数据库事务可靠处理的属性。符合 ACID 的 DBMS 确保即使发生故障,数据库中的数据也能保持准确和一致。
- 最终一致性
-
如果一个数据库能保证所有集群成员在某个时间点都存储了数据的最新版本,则该数据库具有最终一致性。
- 因果一致性
-
如果读写查询被集群中的每个成员以相同的顺序看到,则数据库具有因果一致性。这比最终一致性更强。
- NULL
-
空标记不是一种类型,而是缺失值的占位符。更多信息,请参阅 Cypher → 使用
null。 - 事务
-
事务是一个工作单元,要么被提交,要么在失败时被回滚。例如银行转账:它涉及多个步骤,但它们必须全部成功或全部撤销,以避免钱从一个账户扣除却未存入另一个账户的情况。
- 背压
-
背压是对数据流的抵抗力。它确保客户端不会被过快发送的数据压垮,从而超出其处理能力。
- 书签
-
书签是代表数据库某种状态的标记。通过将一个或多个书签与查询一起传递,服务器将确保在所表示的状态建立之前,该查询不会被执行。
- 事务函数
-
事务函数是由
ExecuteRead或ExecuteWrite调用执行的回调。如果发生服务器故障,驱动程序会自动重新执行该回调。 - 驱动程序 (Driver)
-
Driver对象包含与 Neo4j 数据库建立连接所需的详细信息。