查询数据库
写入数据库
要创建一个名为Alice
的人的节点,请使用 Cypher 子句CREATE
Alice
的Person
节点let { records, summary } = await driver.executeQuery(
'CREATE (p:Person {name: $name})', (1)
{ name: 'Alice' }, (2)
{ database: 'neo4j' } (3)
)
console.log(
`Created ${summary.counters.updates().nodesCreated} nodes ` +
`in ${summary.resultAvailableAfter} ms.`
)
1 | Cypher 查询。 |
2 | 一个查询参数对象。 |
3 | 应该在哪个数据库上运行查询。 |
从数据库读取
要从数据库检索信息,请使用 Cypher 子句MATCH
Person
节点let { records, summary } = await driver.executeQuery(
'MATCH (p:Person) RETURN p.name AS name',
{},
{ database: 'neo4j' }
)
// Loop through users and do process 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`
)
更新数据库
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 子句MATCH
和CREATE
Alice
和Bob
之间创建一个: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 关系,并将其附加到名为Bob 的Person 节点 |
从数据库删除
要删除一个节点及其连接的所有关系,请使用 Cypher 子句DETACH DELETE
Alice
节点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 参数作为关键字参数或在字典中,如前面的示例所示。这是为了
-
性能优势:Neo4j 编译和缓存查询,但只有在查询结构不变时才能做到这一点;
-
安全原因:防止 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'
}
)
虽然在读取模式下执行写入查询可能会导致运行时错误,但您不应该依赖此来进行访问控制。两种模式之间的区别在于读取事务将被路由到集群的任何节点,而写入事务将被定向到主节点。换句话说,无法保证以读取模式提交的写入查询会被拒绝。 |
以不同的用户身份运行查询
您可以使用参数impersonatedUser
以不同用户的安全上下文执行查询,并指定要模拟的用户名称。为了使此操作生效,创建Driver
的用户需要具有适当的权限。模拟用户比创建新的Driver
对象更便宜。
await driver.executeQuery(
'MATCH (p:Person) RETURN p.name',
{},
{
impersonatedUser: 'somebodyElse',
database: 'neo4j'
}
)
模拟用户时,查询将在模拟用户的完整安全上下文中运行,而不是在经过身份验证的用户(即家目录数据库、权限等)的完整安全上下文中运行。
完整示例
const neo4j = require('neo4j-driver');
(async () => {
const URI = '<URI for Neo4j database>'
const USER = '<Username>'
const PASSWORD = '<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
-
原子性、一致性、隔离性、持久性(ACID)是保证数据库事务可靠处理的特性。符合 ACID 的 DBMS 确保数据库中的数据即使在发生故障的情况下也能保持准确性和一致性。
- 最终一致性
-
如果数据库保证所有集群成员在某个时间点存储数据的最新版本,则该数据库最终一致。
- 因果一致性
-
如果数据库保证所有集群成员以相同的顺序看到读写查询,则该数据库因果一致。这比最终一致性更强。
- NULL
-
空标记不是类型,而是值不存在的占位符。有关更多信息,请参见 Cypher → 使用
null
. - 事务
-
事务是工作单元,要么完整提交,要么在发生故障时回滚。例如银行转账:它涉及多个步骤,但这些步骤必须全部成功或被还原,以避免从一个账户中扣款而没有添加到另一个账户中。
- 背压
-
背压是一种阻碍数据流动的力量。它确保客户端不会因数据流速超过其处理能力而不堪重负。
- 事务函数
-
事务函数是由
executeRead
或executeWrite
调用执行的回调函数。如果服务器发生故障,驱动程序会自动重新执行回调函数。 - 驱动程序
-
一个
Driver
对象包含建立与 Neo4j 数据库连接所需的详细信息。