使用 Neo4j 和 JavaScript 构建应用程序

Neo4j JavaScript 驱动程序是用于通过 JavaScript 应用程序与 Neo4j 实例交互的官方库。

Neo4j 的核心是 Cypher,它是一种用于与 Neo4j 数据库交互的查询语言。虽然本指南不要求您成为经验丰富的 Cypher 查询者,但如果您已经了解一些 Cypher,那么专注于 JavaScript 特定的部分会更容易。出于这个原因,虽然本指南也会在过程中提供对 Cypher 的简单介绍,但如果您是第一次接触图数据库建模和查询,请考虑查看 入门 → Cypher 以获取更详细的演练。然后,您可以在遵循本指南开发您的 JavaScript 应用程序时应用这些知识。

安装

使用 npm 安装 Neo4j Javascript 驱动程序

npm i neo4j-driver

连接到数据库

通过创建一个 Driver 对象并提供 URL 和身份验证令牌来连接到数据库。一旦您拥有 Driver 实例,请使用 .getServerInfo() 方法确保可以建立工作连接。

var neo4j = require('neo4j-driver');
(async () => {
  // URI examples: 'neo4j://localhost', 'neo4j+s://xxx.databases.neo4j.io'
  const URI = '<URI to Neo4j database>'
  const USER = '<Username>'
  const PASSWORD = '<Password>'
  let driver

  try {
    driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD))
    const serverInfo = await driver.getServerInfo()
    console.log('Connection established')
    console.log(serverInfo)
  } catch(err) {
    console.log(`Connection error\n${err}\nCause: ${err.cause}`)
  }
})();

查询数据库

使用 Driver.executeQuery() 方法执行 Cypher 语句。不要硬编码或连接参数:使用占位符并将参数指定为键值对。

// Get the name of all 42 year-olds
const { records, summary, keys } = await driver.executeQuery(
  'MATCH (p:Person {age: $age}) RETURN p.name AS name',
  { age: 42 },
  { database: 'neo4j' }
)

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

// Loop through results and do something with them
console.log('>> Results')
for(record of records) {
  console.log(record.get('name'))
}

运行自己的事务

对于更高级的用例,您可以运行 事务。使用 Session.executeRead()Session.executeWrite() 方法来运行托管的事务。

一个包含多个查询、客户端逻辑和潜在回滚的事务
const neo4j = require('neo4j-driver');

(async () => {
  const URI = '<URI for Neo4j database>'
  const USER = '<Username>'
  const PASSWORD = '<Password>'
  let driver, session
  let employeeThreshold = 10

  try {
    driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD))
    await driver.verifyConnectivity()
  } catch(err) {
    console.log(`-- Connection error --\n${err}\n-- Cause --\n${err.cause}`)
    await driver.close()
    return
  }

  session = driver.session({ database: 'neo4j' })
  for(let i=0; i<100; i++) {
    const name = `Neo-${i.toString()}`
    const orgId = await session.executeWrite(async tx => {
      let result, orgInfo

      // Create new Person node with given name, if not already existing
      await tx.run(`
        MERGE (p:Person {name: $name})
        RETURN p.name AS name
        `, { name: name }
      )

      // Obtain most recent organization ID and number of people linked to it
      result = await tx.run(`
        MATCH (o:Organization)
        RETURN o.id AS id, COUNT{(p:Person)-[r:WORKS_FOR]->(o)} AS employeesN
        ORDER BY o.createdDate DESC
        LIMIT 1
      `)
      if(result.records.length > 0) {
        orgInfo = result.records[0]
      }

      if(orgInfo != undefined && orgInfo['employeesN'] == 0) {
        throw new Error('Most recent organization is empty.')
        // Transaction will roll back -> not even Person is created!
      }

      // If org does not have too many employees, add this Person to that
      if(orgInfo != undefined && orgInfo['employeesN'] < employeeThreshold) {
        result = await tx.run(`
          MATCH (o:Organization {id: $orgId})
          MATCH (p:Person {name: $name})
          MERGE (p)-[r:WORKS_FOR]->(o)
          RETURN $orgId AS id
          `, { orgId: orgInfo['id'], name: name }
        )

      // Otherwise, create a new Organization and link Person to it
      } else {
        result = await tx.run(`
          MATCH (p:Person {name: $name})
          CREATE (o:Organization {id: randomuuid(), createdDate: datetime()})
          MERGE (p)-[r:WORKS_FOR]->(o)
          RETURN o.id AS id
          `, { name: name }
        )
      }

      // Return the Organization ID to which the new Person ends up in
      return result.records[0].get('id')
    })
    console.log(`User ${name} added to organization ${orgId}`)
  }
  await session.close()
  await driver.close()
})()

关闭连接和会话

在您完成 Driver 实例的使用后,请调用其 .close() 方法以释放其持有的任何资源。这同样适用于任何打开的会话。

const driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD))
let session = driver.session({ database: 'neo4j' })

// session/driver usage

session.close()
driver.close()

API 文档

有关驱动程序功能的详细信息,请查看 API 文档

词汇表

LTS

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

Aura

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

Cypher

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

APOC

Cypher 上的强大程序 (APOC) 是一个包含许多函数的库,这些函数难以用 Cypher 本身表达。

Bolt

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

ACID

原子性、一致性、隔离性、持久性 (ACID) 是保证数据库事务可靠处理的属性。符合 ACID 的 DBMS 确保即使在发生故障的情况下,数据库中的数据也保持准确和一致。

最终一致性

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

因果一致性

如果数据库保证所有集群成员以相同的顺序看到读写查询,则该数据库因果一致。这比最终一致性更强。

NULL

空标记不是一种类型,而是值不存在的占位符。有关更多信息,请参阅 Cypher → 使用 null

事务

事务是工作单元,要么全部提交,要么在失败时回滚。例如银行转账:它包含多个步骤,但必须全部成功或被回滚,以避免从一个帐户中扣款但没有添加到另一个帐户中。

反压

反压是反对数据流的力量。它确保客户端不会被超出其处理能力的数据淹没。

事务函数

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

驱动程序

Driver 对象保存与 Neo4j 数据库建立连接所需的详细信息。