查询数据库

连接到数据库后,您可以通过方法 Driver.executeQuery() 运行 Cypher 查询。

Driver.executeQuery() 是随驱动程序 5.8 版本引入的。
对于早期版本的查询,请使用 会话和事务

由于使用了 async/await,本页面中的示例需要封装在一个 async 函数中。如果您不确定如何操作,请参阅完整示例

写入数据库

要创建两个分别名为 AliceDavid 的节点,以及它们之间名为 KNOWS 的关系,请使用 Cypher 子句 CREATE

创建两个节点和一个关系
let { records, summary } = await driver.executeQuery(`  (1)
  CREATE (a:Person {name: $name})
  CREATE (b:Person {name: $friendName})
  CREATE (a)-[:KNOWS]->(b)
  `,
  { name: 'Alice', friendName: 'David' },  (2)
  { database: 'neo4j' }  (3)
)
console.log(
  `Created ${summary.counters.updates().nodesCreated} nodes ` +
  `in ${summary.resultAvailableAfter} ms.`
)
1 Cypher 查询。
2 一个查询参数对象。
3 运行查询的数据库

从数据库读取

要从数据库检索信息,请使用 Cypher 子句 MATCH

检索所有喜欢其他 PersonPerson 节点
let { records, summary } = await driver.executeQuery(`
  MATCH (p:Person)-[:KNOWS]->(:Person)
  RETURN p.name AS name
  `,
  {},
  { database: 'neo4j' }
)

// Loop through users and do something with them
for(let record of records) {  (1)
  console.log(`Person with name: ${record.get('name')}`)
  console.log(`Available properties for this node are: ${record.keys}\n`)
}

// Summary information
console.log(  (2)
  `The query \`${summary.query.text}\` ` +
  `returned ${records.length} nodes.\n`
)
1 records 包含作为 Record 对象列表的实际结果。
2 summary 包含服务器返回的执行摘要

更新数据库

要更新数据库中节点的信息,请使用 Cypher 子句 MATCHSET

更新节点 Alice 以添加 age 属性
let { _, summary } = await driver.executeQuery(`
  MATCH (p:Person {name: $name})
  SET p.age = $age
  `,
  { name: 'Alice', age: 42 },
  { database: 'neo4j' }
)
console.log('Query counters:')
console.log(summary.counters.updates())

要创建新关系,并将其链接到两个已存在的节点,请结合使用 Cypher 子句 MATCHCREATE

AliceBob 之间创建 :KNOWS 关系
let { records, summary } = await driver.executeQuery(`
  MATCH (alice:Person {name: $name})  (1)
  MATCH (bob:Person {name: $friendName})  (2)
  CREATE (alice)-[:KNOWS]->(bob)  (3)
  `, { name: 'Alice', friendName: 'Bob' },
  { database: 'neo4j' }
)
console.log('Query counters:')
console.log(summary.counters.updates())
1 检索名为 Alice 的人物节点,并将其绑定到变量 alice
2 检索名为 Bob 的人物节点,并将其绑定到变量 bob
3 创建一个从绑定到 alice 的节点发出的新 :KNOWS 关系,并将名为 BobPerson 节点附加到其上

从数据库删除

要删除节点及其附加的任何关系,请使用 Cypher 子句 DETACH DELETE

删除 Alice 节点
// This does not delete _only_ p, but also all its relationships!
let { _, summary } = await driver.executeQuery(`
  MATCH (p:Person WHERE p.name = $name)
  DETACH DELETE p
  `, { name: 'Alice' },
  { database: 'neo4j' }
)
console.log('Query counters:')
console.log(summary.counters.updates())

查询参数

请勿将参数硬编码或直接拼接进查询中。相反,请始终使用占位符,并如前面的示例所示,将 Cypher 参数指定为关键字参数或在字典中指定。这样做是为了

  1. 性能优势:Neo4j 会编译并缓存查询,但只有在查询结构不变的情况下才能这样做;

  2. 安全原因防范 Cypher 注入

在某些情况下,您的查询结构可能会阻止在所有部分中使用参数。对于那些罕见的用例,请参阅属性键、关系类型和标签中的动态值

错误处理

为了避免单个查询中的错误导致您的应用程序崩溃,您可以将查询封装在 try/catch 块中。在本手册中,我们避免了完整的错误处理,以使示例更易于理解,并且因为适当的错误处理取决于具体的应用程序。下面是带有 try/catch 块的示例。

try {
  let result = await driver.executeQuery('MATCH (p:Person) RETURN p')
} catch(err) {
  console.log(`Error in query\n${err}`)
}
如果故障被认为是瞬时的(例如由于服务器暂时不可用),驱动程序会自动重试运行失败的查询。如果操作在多次尝试后仍然失败,将引发异常。

查询配置

您可以提供一个 QueryConfig 对象作为第三个(可选)参数,以更改 .executeQuery() 的默认行为。

数据库选择

建议始终使用 database 参数明确指定数据库,即使是在单数据库实例上也是如此。这使得驱动程序能更高效地工作,因为它省去了到服务器解析主数据库的网络往返。如果未指定数据库,则使用用户的主数据库

await driver.executeQuery(
  'MATCH (p:Person) RETURN p.name',
  {},
  {
    database: 'neo4j'
  }
)
通过配置参数指定数据库比使用 USE Cypher 子句更受推荐。如果服务器在集群上运行,带有 USE 的查询需要启用服务器端路由。查询执行也可能需要更长时间,因为它们可能无法在第一次尝试时到达正确的集群成员,需要被路由到包含所请求数据库的成员。

请求路由

在集群环境中,所有查询默认都定向到主节点。为了提高读取查询的性能,您可以使用配置 routing: 'READ' 将查询路由到读取节点。

await driver.executeQuery(
  'MATCH (p:Person) RETURN p.name',
  {},
  {
    routing: 'READ',  // short for neo4j.routing.READ
    database: 'neo4j'
  }
)

尽管在读取模式下执行写入查询很可能会导致运行时错误,但您不应将其用于访问控制。这两种模式的区别在于,读取事务将被路由到集群的任何节点,而写入事务则定向到主节点。换句话说,无法保证以读取模式提交的写入查询会被拒绝。

以不同用户身份运行查询

您可以使用配置参数 auth 以不同用户身份执行查询。在查询级别切换用户比创建新的 Driver 对象更经济。然后,查询将在给定用户的安全上下文中运行(即,主数据库、权限等)。
查询范围的身份验证需要服务器版本 >= 5.8。

await driver.executeQuery(
  'MATCH (p:Person) RETURN p.name',
  {},
  {
    auth: neo4j.auth.basic('somebodyElse', 'theirPassword'),
    database: 'neo4j'
  }
)

参数 impersonatedUser 提供类似的功能,并且在驱动程序/服务器版本 >= 4.4 中可用。不同之处在于,您无需知道用户的密码即可模拟他们,但创建 Driver 的用户需要拥有适当的权限

await driver.executeQuery(
  'MATCH (p:Person) RETURN p.name',
  {},
  {
    impersonatedUser: 'somebodyElse',
    database: 'neo4j'
  }
)

完整示例

const neo4j = require('neo4j-driver');

(async () => {
  const URI = '{neo4j-database-uri}'
  const USER = '{neo4j-username}'
  const PASSWORD = '{neo4j-password}'
  let driver, result

  let people = [{name: 'Alice', age: 42, friends: ['Bob', 'Peter', 'Anna']},
                {name: 'Bob', age: 19},
                {name: 'Peter', age: 50},
                {name: 'Anna', age: 30}]

  // Connect to database
  try {
    driver = neo4j.driver(URI,  neo4j.auth.basic(USER, PASSWORD))
    await driver.verifyConnectivity()
  } catch(err) {
    console.log(`Connection error\n${err}\nCause: ${err.cause}`)
    await driver.close()
    return
  }

  // Create some nodes
  for(let person of people) {
    await driver.executeQuery(
      'MERGE (p:Person {name: $person.name, age: $person.age})',
      { person: person },
      { database: 'neo4j' }
    )
  }

  // Create some relationships
  for(let person of people) {
    if(person.friends != undefined) {
      await driver.executeQuery(`
        MATCH (p:Person {name: $person.name})
        UNWIND $person.friends AS friendName
        MATCH (friend:Person {name: friendName})
        MERGE (p)-[:KNOWS]->(friend)
        `, { person: person },
        { database: 'neo4j' }
      )
    }
  }

  // Retrieve Alice's friends who are under 40
  result = await driver.executeQuery(`
    MATCH (p:Person {name: $name})-[:KNOWS]-(friend:Person)
    WHERE friend.age < $age
    RETURN friend
    `, { name: 'Alice', age: 40 },
    { database: 'neo4j' }
  )

  // Loop through results and do something with them
  for(let person of result.records) {
    // `person.friend` is an object of type `Node`
    console.log(person.get('friend'))
  }

  // Summary information
  console.log(
    `The query \`${result.summary.query.text}\` ` +
    `returned ${result.records.length} records ` +
    `in ${result.summary.resultAvailableAfter} ms.`
  )

  await driver.close()
})();

术语表

LTS

长期支持版本是保证支持数年的版本。Neo4j 4.4 是 LTS 版本,Neo4j 5 也将有一个 LTS 版本。

Aura

Aura 是 Neo4j 的全托管云服务。它提供免费和付费计划。

Cypher

Cypher 是 Neo4j 的图查询语言,可让您从数据库中检索数据。它类似于 SQL,但适用于图。

APOC

Cypher 上的优秀过程 (APOC) 是一个函数库(包含许多函数),这些函数本身无法在 Cypher 中轻松表达。

Bolt

Bolt 是 Neo4j 实例和驱动程序之间用于交互的协议。它默认监听端口 7687。

ACID

原子性 (Atomicity)、一致性 (Consistency)、隔离性 (Isolation)、持久性 (Durability) (ACID) 是保证数据库事务可靠处理的属性。符合 ACID 的数据库管理系统可确保即使发生故障,数据库中的数据也能保持准确和一致。

最终一致性

如果数据库保证所有集群成员在某个时间点都将存储最新版本的数据,那么它是最终一致的。

因果一致性

如果集群的每个成员都以相同的顺序看到读写查询,那么数据库就是因果一致的。这比最终一致性更强。

NULL

null 标记不是一种类型,而是表示值缺失的占位符。欲了解更多信息,请参阅Cypher → 使用 null

事务

事务是一个工作单元,它要么被完整地提交,要么在失败时被回滚。一个例子是银行转账:它涉及多个步骤,但所有步骤都必须成功或被撤销,以避免资金从一个账户中扣除但未添加到另一个账户的情况。

背压

背压是阻碍数据流动的力。它确保客户端不会被数据淹没,即数据传输速度不会快于客户端的处理速度。

事务函数

事务函数是由 executeReadexecuteWrite 调用执行的回调。如果服务器发生故障,驱动程序会自动重新执行该回调。

驱动程序

一个 Driver 对象包含与 Neo4j 数据库建立连接所需的详细信息。

© . All rights reserved.