探索查询执行摘要
在查询结果的所有记录都被处理完毕后,服务器通过返回执行摘要来结束事务。它以一个 ResultSummary 对象的形式出现,并包含以下信息
检索执行摘要
使用 ExecuteQuery() 运行查询时,执行摘要是默认返回对象的一部分,位于 Summary 键下。
result, _ := neo4j.ExecuteQuery(ctx, driver, `
UNWIND ["Alice", "Bob"] AS name
MERGE (p:Person {name: name})
`, nil,
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
summary := result.Summary
如果你使用 事务函数,可以通过方法 Result.Consume() 获取查询执行摘要。请注意,一旦请求执行摘要,结果流将被 耗尽:任何尚未处理的记录将不再可用。
session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "<database-name>"})
defer session.Close(ctx)
summary, _ := session.ExecuteWrite(ctx,
func(tx neo4j.ManagedTransaction) (any, error) {
result, err := tx.Run(ctx, `
UNWIND ["Alice", "Bob"] AS name
MERGE (p:Person {name: name})
`, nil)
summary, _ := result.Consume(ctx)
return summary, err
})
查询计数器
方法 ResultSummary.Counters() 返回查询触发的操作计数(作为 Counters 对象)。
result, _ := neo4j.ExecuteQuery(ctx, driver, `
MERGE (p:Person {name: $name})
MERGE (p)-[:KNOWS]->(:Person {name: $friend})
`, map[string]any{
"name": "Mark",
"friend": "Bob",
}, neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
summary := result.Summary
counters := summary.Counters()
fmt.Println("Nodes created:", counters.NodesCreated())
fmt.Println("Labels added:", counters.LabelsAdded())
fmt.Println("Properties set:", counters.PropertiesSet())
fmt.Println("Relationships created:", counters.RelationshipsCreated())
// Nodes created: 2
// Labels added: 2
// Properties set: 2
// Relationships created: 1
另外有两个布尔方法充当元计数器
-
.ContainsUpdates()— 查询是否在其所在的数据库上触发了任何写操作 -
.ContainsSystemUpdates()— 查询是否在system数据库上触发了任何写操作
查询执行计划
如果在查询前加上 EXPLAIN,服务器会返回它 将会 用来执行查询的计划,但并不会实际执行查询。你可以通过调用 ResultSummary.Plan() 获取该计划,它包含一系列 Cypher 操作符,这些操作符本会用于检索结果集。利用这些信息可以定位潜在的瓶颈或性能改进空间(例如通过创建索引)。
result, _ := neo4j.ExecuteQuery(ctx, driver,
"EXPLAIN MATCH (p {name: $name}) RETURN p",
map[string]any{
"name": "Alice",
},
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
fmt.Println(result.Summary.Plan().Arguments()["string-representation"])
/*
Planner COST
Runtime PIPELINED
Runtime version 5.0
Batch size 128
+-----------------+----------------+----------------+---------------------+
| Operator | Details | Estimated Rows | Pipeline |
+-----------------+----------------+----------------+---------------------+
| +ProduceResults | p | 1 | |
| | +----------------+----------------+ |
| +Filter | p.name = $name | 1 | |
| | +----------------+----------------+ |
| +AllNodesScan | p | 10 | Fused in Pipeline 0 |
+-----------------+----------------+----------------+---------------------+
Total database accesses: ?
*/
如果在查询前使用关键字 PROFILE,服务器会返回它实际用于运行查询的执行计划,并附带分析器统计信息。内容包括使用的操作符列表以及每个中间步骤的额外分析信息。你可以通过调用 ResultSummary.Profile() 访问该计划。请注意,查询同样会被 执行,因此结果对象也包含所有结果记录。
result, _ := neo4j.ExecuteQuery(ctx, driver,
"PROFILE MATCH (p {name: $name}) RETURN p",
map[string]any{
"name": "Alice",
},
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
fmt.Println(result.Summary.Profile().Arguments()["string-representation"])
/*
Planner COST
Runtime PIPELINED
Runtime version 5.0
Batch size 128
+-----------------+----------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
+-----------------+----------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +ProduceResults | p | 1 | 1 | 3 | | | | |
| | +----------------+----------------+------+---------+----------------+ | | |
| +Filter | p.name = $name | 1 | 1 | 4 | | | | |
| | +----------------+----------------+------+---------+----------------+ | | |
| +AllNodesScan | p | 10 | 4 | 5 | 120 | 9160/0 | 108.923 | Fused in Pipeline 0 |
+-----------------+----------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
Total database accesses: 12, total allocated memory: 184
*/
有关更多信息和示例,请参见 基本查询调优。
通知
执行查询后,服务器可以在查询结果旁返回通知。通知包括性能改进建议、关于使用已弃用特性的警告以及其他关于 Neo4j 使用不佳的提示。
方法 Summary.GqlStatusObjects() 返回一个 GqlStatusObject 列表。这些是符合 GQL 标准的状态对象。
有些(但不是全部)GqlStatusObjects 是通知,而有些则报告 结果 状态:00000 表示“成功”,02000 表示“无数据”,00001 表示“省略结果”。Summary.GqlStatusObjects() 始终至少包含一个条目,用于表示结果状态。
result, _ := neo4j.ExecuteQuery(ctx, driver, `
MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'}))
RETURN p
`, nil,
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
for _, status := range result.Summary.GqlStatusObjects() {
fmt.Println("GQLSTATUS:", status.GqlStatus())
fmt.Println("Description:", status.StatusDescription())
// Not all statuses are notifications.
fmt.Println("Is notification:", status.IsNotification())
// Notification and thus vendor-specific fields.
// These fields are only meaningful for notifications.
if status.IsNotification() == true {
fmt.Println("Position (offset, line, column):", status.Position().Offset(), status.Position().Line(), status.Position().Column())
fmt.Println("Classification:", status.Classification())
fmt.Println("Unparsed classification:", status.RawClassification())
fmt.Println("Severity:", status.Severity())
fmt.Println("Unparsed severity:", status.RawSeverity())
}
// Any raw extra information provided by the server
fmt.Println("Diagnostic record:", status.DiagnosticRecord())
fmt.Println(strings.Repeat("=", 80))
}
/*
GQLSTATUS: 02000
Description: note: no data
Is notification: false
Diagnostic record: map[CURRENT_SCHEMA:/ OPERATION: OPERATION_CODE:0]
================================================================================
GQLSTATUS: 03N91
Description: info: unbounded variable length pattern. The provided pattern `(:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'})` is unbounded. Shortest path with an unbounded pattern may result in long execution times. Use an upper limit (e.g. `[*..5]`) on the number of node hops in your pattern.
Is notification: true
Position (offset, line, column): 26 2 26
Classification: PERFORMANCE
Unparsed classification: PERFORMANCE
Severity: INFORMATION
Unparsed severity: INFORMATION
Diagnostic record: map[CURRENT_SCHEMA:/ OPERATION: OPERATION_CODE:0 _classification:PERFORMANCE _position:map[column:26 line:2 offset:26] _severity:INFORMATION _status_parameters:map[pat:(:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'})]]
================================================================================
*/
在 6.0 中已弃用
方法 Summary.Notifications() 返回一个 Notification 对象列表。
result, _ := neo4j.ExecuteQuery(ctx, driver, `
MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'}))
RETURN p
`, nil,
neo4j.EagerResultTransformer,
neo4j.ExecuteQueryWithDatabase("<database-name>"))
for _, notification := range result.Summary.Notifications() {
fmt.Println("Code:", notification.Code())
fmt.Println("Title:", notification.Title())
fmt.Println("Description:", notification.Description())
fmt.Println("Severity:", notification.SeverityLevel())
fmt.Println("Category:", notification.Category(), "\n")
}
/*
Code: Neo.ClientNotification.Statement.UnboundedVariableLengthPattern
Title: The provided pattern is unbounded, consider adding an upper limit to the number of node hops.
Description: Using shortest path with an unbounded pattern will likely result in long execution times. It is recommended to use an upper limit to the number of node hops in your pattern.
Severity: INFORMATION
Category: PERFORMANCE
*/
过滤通知
默认情况下,服务器会针对每个查询分析所有类别和严重程度的通知。使用参数 NotificationsMinSeverity 和/或 NotificationsDisabledCategories/NotificationsDisabledClassifications 可以限制你感兴趣的通知的严重程度和/或类别/分类。限制服务器可产生的通知数量可以略微提升性能。
严重程度过滤器适用于 Neo4j 和 GQL 通知。类别和分类过滤器之所以分开,仅是因为 GQL 与 Neo4j 在词汇上的差异;但两者都会影响任意形式的通知,因此应只使用其中一种。可以在创建 Driver 实例或创建会话时使用这些参数。
通过将最小严重程度设置为 "OFF" 可以完全禁用通知。
Warning 通知,排除 Hint 或 Generic 类别的通知// import (
// "github.com/neo4j/neo4j-go-driver/v6/neo4j/notifications"
// "github.com/neo4j/neo4j-go-driver/v6/neo4j/config"
// )
// At driver level
driverNot, _ := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""),
func (conf *config.Config) {
conf.NotificationsMinSeverity = notifications.WarningLevel // or "OFF" to disable entirely
conf.NotificationsDisabledClassifications = notifications.DisableClassifications(notifications.Hint, notifications.Generic) // filters categories as well
})
// At session level
sessionNot := driver.NewSession(ctx, neo4j.SessionConfig{
NotificationsMinSeverity: notifications.WarningLevel, // or "OFF" to disable entirely
NotificationsDisabledClassifications: notifications.DisableClassifications(notifications.Hint, notifications.Generic), // filters categories as well
DatabaseName: "<database-name>", // always provide the database name
})