探索查询执行摘要

处理完查询返回的所有结果后,服务器通过返回执行摘要来结束事务。它以 ResultSummary 对象的形式提供,其中包含的信息包括:

  • 查询计数器 — 查询在服务器上触发了哪些更改

  • 查询执行计划 — 数据库将如何执行(或已执行)查询

  • 通知 — 服务器在运行查询时发出的额外信息

  • 时间信息和查询请求摘要

检索执行摘要

当使用 Driver.executableQuery() 运行查询时,执行摘要是默认返回对象的一部分,可通过 .summary() 方法检索。

var result = driver.executableQuery("""
    UNWIND ['Alice', 'Bob'] AS name
    MERGE (p:Person {name: name})
    """)
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();
var resultSummary = result.summary();

如果您正在使用事务函数,您可以使用 Result.consume() 方法检索查询执行摘要。请注意,一旦您请求执行摘要,结果流就会耗尽。这意味着任何尚未处理的记录都将被丢弃。

try (var session = driver.session(SessionConfig.builder().withDatabase("neo4j").build())) {
    var resultSummary = session.executeWrite(tx -> {
        var result = tx.run("""
        UNWIND ['Alice', 'Bob'] AS name
        MERGE (p:Person {name: name})
        """);
        return result.consume();
    });
}

查询计数器

ResultSummary.counters() 方法返回查询触发的操作的计数器(作为一个 SummaryCounters 对象)。

插入一些数据并显示查询计数器
var result = driver.executableQuery("""
    MERGE (p:Person {name: $name})
    MERGE (p)-[:KNOWS]->(:Person {name: $friend})
    """).withParameters(Map.of("name", "Mark", "friend", "Bob"))
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();
var queryCounters = result.summary().counters();
System.out.println(queryCounters);

/*
InternalSummaryCounters{nodesCreated=2, nodesDeleted=0, relationshipsCreated=1, relationshipsDeleted=0,
propertiesSet=2, labelsAdded=2, labelsRemoved=0, indexesAdded=0, indexesRemoved=0, constraintsAdded=0,
constraintsRemoved=0, systemUpdates=0}
*/

还有两个额外的布尔方法,它们充当元计数器

  • .containsUpdates() — 查询是否在其运行的数据库上触发了任何写入操作

  • .containsSystemUpdates() — 查询是否更新了 system 数据库

查询执行计划

如果您在查询前加上 EXPLAIN,服务器将返回它用于运行查询的计划,但实际上不会运行它。该计划以 Plan 对象的形式通过 ResultSummary.plan() 方法提供,并包含用于检索结果集的 Cypher 操作符列表。您可以使用此信息来查找潜在的瓶颈或性能改进空间(例如通过创建索引)。

var result = driver.executableQuery("EXPLAIN MATCH (p {name: $name}) RETURN p")
    .withParameters(Map.of("name", "Alice"))
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();
var queryPlan = result.summary().plan().arguments().get("string-representation");
System.out.println(queryPlan);

/*
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,服务器将返回它已用于运行查询的执行计划,以及分析器统计信息。这包括使用的操作符列表和每个中间步骤的额外分析信息。该计划以 Plan 对象的形式通过 ResultSummary.profile() 方法提供。请注意,查询也会被运行,因此结果对象也包含任何结果记录。

var result = driver.executableQuery("PROFILE MATCH (p {name: $name}) RETURN p")
    .withParameters(Map.of("name", "Alice"))
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();
var queryPlan = result.summary().profile().arguments().get("string-representation");
System.out.println(queryPlan);

/*
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 次优使用的其他提示。

对于驱动程序版本 >= 5.25 和服务器版本 >= 5.23,有两种形式的通知可用(Neo4j 状态码GQL 状态码)。对于早期版本,只有Neo4j 状态码可用。
GQL 状态码计划取代 Neo4j 状态码。
示例 1. 无边界最短路径引发性能通知

ResultSummary.notifications() 方法返回一个 Notification 对象列表。

var result = driver.executableQuery("""
    MATCH p=shortestPath((:Person {name: $start})-[*]->(:Person {name: $end}))
    RETURN p
    """)
    .withParameters(Map.of("start", "Alice", "end", "Bob"))
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();
var notifications = result.summary().notifications();
System.out.println(notifications);
/*
[
    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.,
    severityLevel=InternalNotificationSeverity[type=INFORMATION,
    level=800],
    rawSeverityLevel=INFORMATION,
    category=InternalNotificationCategory[type=PERFORMANCE],
    rawCategory=PERFORMANCE,
    position={offset=21, line=1, column=22}
]
*/

对于版本 >= 5.25,ResultSummary.gqlStatusObjects() 方法返回一个符合 GQL 的有序状态对象集。

该集合可以包含 Notification 对象和 GqlStatusObject 对象。后者编码查询的结果状态:00000 表示“成功”,02000 表示“无数据”,00001 表示“省略结果”。该集合始终包含至少一个条目,其中包含结果状态。

var result = driver.executableQuery("""
    MATCH p=shortestPath((:Person {name: $start})-[*]->(:Person {name: $end}))
    RETURN p
    """)
    .withParameters(Map.of("start", "Alice", "end", "Bob"))
    .withConfig(QueryConfig.builder().withDatabase("neo4j").build())
    .execute();
var statuses = result.summary().gqlStatusObjects();
System.out.println(statuses);
/*
[
    InternalGqlStatusObject{gqlStatus='02000', statusDescription='note: no data', diagnosticRecord={OPERATION_CODE="0", OPERATION="", CURRENT_SCHEMA="/"}},
    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., severityLevel=InternalNotificationSeverity[type=INFORMATION, level=800], rawSeverityLevel=INFORMATION, classification=PERFORMANCE, rawClassification=PERFORMANCE, position={offset=21, line=1, column=22}
]
*/

过滤通知

默认情况下,服务器会分析每个查询的所有类别和严重程度的通知。从版本 5.22 开始,您可以使用配置方法 .withMinimumNotificationSeverity().withDisabledNotificationClassification() 来调整您感兴趣的通知的严重程度和/或类别/分类,或完全禁用它们。限制服务器可以发出的通知数量会带来轻微的性能提升。

严重性过滤器同时适用于 Neo4j 和 GQL 通知。分类过滤器作用于类别和分类两者。

您可以在创建 Driver 实例时在 Config 对象上调用这些方法,也可以在创建会话时在 SessionConfig 对象上调用这些方法。

只允许 WARNING 通知,但排除 HINTGENERIC 分类
// import java.util.Set
// import org.neo4j.driver.Config;
// import org.neo4j.driver.NotificationClassification;
// import org.neo4j.driver.NotificationConfig;
// import org.neo4j.driver.NotificationSeverity;
// import org.neo4j.driver.SessionConfig;

// at `Driver` level
var driver = GraphDatabase.driver(
    dbUri, AuthTokens.basic(dbUser, dbPassword),
    Config.builder()
    .withMinimumNotificationSeverity(NotificationSeverity.WARNING)  // NotificationSeverity.OFF to disable entirely
    .withDisabledNotificationClassifications(Set.of(NotificationClassification.HINT, NotificationClassification.GENERIC))  // filters categories as well
    .build()
);

// at `Session` level
var session = driver.session(
    SessionConfig.builder()
    .withDatabase("neo4j")
    .withMinimumNotificationSeverity(NotificationSeverity.WARNING)  // NotificationSeverity.OFF to disable entirely
    .withDisabledNotificationClassifications(Set.of(NotificationClassification.HINT, NotificationClassification.GENERIC))  // filters categories as well
    .build()
);
5.22 以前版本的通知过滤

对于 5.22 以前的版本,通知过滤通过配置方法 .withNotificationConfig() 完成(版本 5.7+)。

NotificationConfig 接口提供 .enableMinimumSeverity().disableCategories().disableAllConfig() 方法来设置配置。

只允许 WARNING 通知,但排除 HINTGENERIC 类别
// import java.util.Set
// import org.neo4j.driver.Config;
// import org.neo4j.driver.NotificationCategory;
// import org.neo4j.driver.NotificationConfig;
// import org.neo4j.driver.NotificationSeverity;
// import org.neo4j.driver.SessionConfig;

// at `Driver` level
var driver = GraphDatabase.driver(
    dbUri, AuthTokens.basic(dbUser, dbPassword),
    Config.builder()
    .withNotificationConfig(NotificationConfig.defaultConfig()
        .enableMinimumSeverity(NotificationSeverity.WARNING)
        .disableCategories(Set.of(NotificationCategory.HINT, NotificationCategory.GENERIC))
    ).build()
);

// at `Session` level
var session = driver.session(
    SessionConfig.builder()
    .withDatabase("neo4j")
    .withNotificationConfig(NotificationConfig.defaultConfig()
        .enableMinimumSeverity(NotificationSeverity.WARNING)
        .disableCategories(Set.of(NotificationCategory.HINT, NotificationCategory.GENERIC))
    ).build()
);
禁用所有通知
// import org.neo4j.driver.Config;
// import org.neo4j.driver.NotificationConfig;
// import org.neo4j.driver.SessionConfig;

// at `Driver` level
var driver = GraphDatabase.driver(
    dbUri, AuthTokens.basic(dbUser, dbPassword),
    Config.builder()
    .withNotificationConfig(NotificationConfig.disableAllConfig())
    .build()
);

// at `Session` level
var session = driver.session(
    SessionConfig.builder()
    .withDatabase("neo4j")
    .withNotificationConfig(NotificationConfig.disableAllConfig())
    .build()
);
© . All rights reserved.