探索查询执行摘要

在来自查询的所有结果都处理完毕后,服务器通过返回执行摘要来结束事务。它以一个 ResultSummary 对象的形式出现,并且包含以下信息:

  • 查询计数器 — 查询在服务器上触发的更改

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

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

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

检索执行摘要

当使用 Driver.executeQuery() 运行查询时,执行摘要是默认返回对象的一部分,位于 summary 属性下。

let { records, summary } = await driver.executeQuery(`
  UNWIND ['Alice', 'Bob'] AS name
  MERGE (p:Person {name: name})
  `, {},
  { database: 'neo4j' }
)
// or summary = (await driver.executeQuery('<QUERY>')).summary

如果您使用的是 事务函数,则可以使用属性 Result.summary 检索查询执行摘要(如果您使用 await 来解析结果 promise),或者使用方法 Result.summary() 检索查询执行摘要(如果您将结果作为 promise 使用)。

请注意,一旦您请求执行摘要,结果流就会被耗尽。这意味着尚未处理的任何记录都会被丢弃。

let session = driver.session({ database: 'neo4j' })
try {
  let summary = await session.executeWrite(async tx => {
    let result = await tx.run(`
        UNWIND ['Alice', 'Bob'] AS name
        MERGE (p:Person {name: name})
    `)
    return result.summary
    // or result.summary(), if you don't await tx.run()
  })
} finally {
  session.close()
}

查询计数器

属性 ResultSummary.counters 包含查询触发的操作的计数器(作为 QueryStatistics 对象)。

插入一些数据并显示查询计数器
let { records, summary } = await driver.executeQuery(`
  UNWIND ['Alice', 'Bob'] AS name
  MERGE (p:Person {name: name})
  `, {},
  { database: 'neo4j' }
)
console.log(summary.counters.updates())
/*
{
  nodesCreated: 2,
  nodesDeleted: 0,
  relationshipsCreated: 0,
  relationshipsDeleted: 0,
  propertiesSet: 0,
  labelsAdded: 1,
  labelsRemoved: 0,
  indexesAdded: 0,
  indexesRemoved: 0,
  constraintsAdded: 0,
  constraintsRemoved: 0
}
*/
console.log(summary.counters.containsUpdates())  // true
console.log(summary.counters.containsSystemUpdates())  // false

ResultSummary.counters 上还有两个附加方法,它们充当元计数器

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

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

查询执行计划

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

let result = await driver.executeQuery('EXPLAIN MATCH (p {name: $name}) RETURN p', { name: 'Alice' })
console.log(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 下。请注意,查询也会运行,因此结果对象还包含任何结果记录。

let result = await driver.executeQuery('PROFILE MATCH (p {name: $name}) RETURN p', { name: 'Alice' })
console.log(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 使用不佳的其他提示。

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

属性 ResultSummary.notifications 包含 Notification 对象列表。

let { records, summary } = await driver.executeQuery(`
  MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'}))
  RETURN p
  `, {},
  { database: 'neo4j' }
)
console.log(summary.notifications)
/*
[
  Notification {
    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',
    position: { offset: 24, line: 2, column: 22 },
    severityLevel: 'INFORMATION',
    rawSeverityLevel: 'INFORMATION',
    category: 'PERFORMANCE',
    rawCategory: 'PERFORMANCE'
  }
]
*/

对于版本 >= 5.25,属性 ResultSummary.gqlStatusObjects 包含 GqlStatusObject 列表。这些是符合 GQL 的状态对象。

一些(但并非所有)GqlStatusObjects 是通知,而另一些则报告结果状态:00000 表示“成功”,02000 表示“无数据”,00001 表示“省略结果”。Summary.GqlStatusObjects() 始终包含至少一个条目,其中包含结果状态。

let { records, summary } = await driver.executeQuery(`
  MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'}))
  RETURN p
  `, {},
  { database: 'neo4j' }
)
console.log(summary.gqlStatusObjects)
/*
[
  GqlStatusObject {
    gqlStatus: '02000',
    statusDescription: 'note: no data',
    diagnosticRecord: { OPERATION: '', OPERATION_CODE: '0', CURRENT_SCHEMA: '/' },
    position: undefined,
    severity: 'UNKNOWN',
    rawSeverity: undefined,
    classification: 'UNKNOWN',
    rawClassification: undefined,
    isNotification: false
  },
  GqlStatusObject {
    gqlStatus: '03N91',
    statusDescription: "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.",
    diagnosticRecord: {
      OPERATION: '',
      OPERATION_CODE: '0',
      CURRENT_SCHEMA: '/',
      _classification: 'PERFORMANCE',
      _status_parameters: [Object],
      _severity: 'INFORMATION',
      _position: [Object]
    },
    position: { offset: 24, line: 2, column: 24 },
    severity: 'INFORMATION',
    rawSeverity: 'INFORMATION',
    classification: 'PERFORMANCE',
    rawClassification: 'PERFORMANCE',
    isNotification: true
  }
]
*/

筛选通知

默认情况下,服务器会针对所有类别和严重程度的通知分析每个查询。从版本 5.7 开始,您可以使用参数 minimumSeverityLevel 和/或 disabledCategories/disabledClassifications 来限制您感兴趣的通知的严重程度和/或类别/分类。限制服务器允许引发的通知数量,可以略微提高性能。

严重程度过滤器适用于 Neo4j 和 GQL 通知。类别和分类过滤器仅因 GQL 和 Neo4j 之间的词典差异而单独存在;但这两种过滤器都会影响任一形式的通知,因此您应该只使用其中一种。您可以在创建 Driver 实例时或创建会话时使用任何这些参数。

您可以将最小严重程度设置为 'OFF',完全禁用通知。

仅允许 WARNING 通知,但不允许 HINTGENERIC 分类
// at driver level
let driver = neo4j.driver(
  URI,  neo4j.auth.basic(USER, PASSWORD), {
    notificationsFilter: {
      minimumSeverityLevel: 'WARNING',  // or 'OFF' to disable entirely
      disabledClassifications: ['HINT', 'GENERIC']  // filters categories as well
    }
  }
)

// at session level
let session = driver.session({
    database: 'neo4j',
    notificationsFilter: {
      minimumSeverityLevel: 'WARNING',  // or 'OFF' to disable entirely
      disabledClassifications: ['HINT', 'GENERIC']  // filters categories as well
    }
})