数据类型及其与 Cypher 类型的映射
本节中的表格显示了 Cypher 数据类型与 Java 类型之间的映射关系。
无论类型如何,查询结果中的所有值都嵌入在 Value 对象中。要提取并将其转换为相应的 Java 类型,请使用 .as<type>()(例如 .asString()、asInt() 等)。例如,如果来自数据库的 name 属性是一个字符串,record.get("name").asString() 将把该属性值作为 String 对象返回。
另一方面,在提交查询参数时,您无需使用驱动程序类型。驱动程序会自动序列化作为参数传递的 Java 原生类型对象。
有关驱动程序将数据序列化成的完整值类型列表,请参阅 API 文档。
时间类型
驱动程序提供了一套符合 ISO-8601 和 Cypher 标准的时间数据类型。亚秒级值以纳秒精度测量。
驱动程序的类型依赖于 Java 的 time 类型。除 DurationValue 外,所有时间类型底层实际上都是 java.time 对象。这意味着
-
如果您想使用时间类型查询数据库,只需实例化一个
java.time对象并将其用作查询参数(即您不需要关心驱动程序的类型)。 -
如果您从数据库检索时间对象(包括通过 Cypher 时间函数),您将获得下表中显示的相应驱动程序类型。驱动程序实现了将驱动程序时间类型转换为 Java 类型的方法(例如
.asZonedDateTime()、.asOffsetTime()等)。
| Cypher 类型 | 驱动程序类型 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
package demo;
import java.util.Map;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.QueryConfig;
public class App {
public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
driver.verifyConnectivity();
// Define a date, with timezone, and use it to set a relationship property
var friendsSince = ZonedDateTime.of(2016, 12, 16, 13, 59, 59, 9999999, ZoneId.of("Europe/Stockholm"));
var result = driver.executableQuery("""
MERGE (a:Person {name: $name})
MERGE (b:Person {name: $friend})
MERGE (a)-[friendship:KNOWS {since: $friendsSince}]->(b)
RETURN friendship.since AS date
""")
.withParameters(Map.of("name", "Alice", "friend", "Bob", "friendsSince", friendsSince))
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
var date = result.records().get(0).get("date");
System.out.println(date.getClass().getName()); // org.neo4j.driver.internal.value.DateTimeValue
System.out.println(date); // 2016-12-16T13:59:59.009999999+01:00[Europe/Stockholm]
var nativeDate = date.asZonedDateTime();
System.out.println(nativeDate.getClass().getName()); // java.time.ZonedDateTime
}
}
}
DurationValue
表示两个时间点之间的差值(以月、日、秒、纳秒表示)。
// import org.neo4j.driver.Values;
var duration = Values.isoDuration(1, 2, 3, 4); // months, days, seconds, nanoseconds
System.out.println(duration); // P1M2DT3.000000004S
有关完整文档,请参阅 API 文档 → DurationValue。
空间类型
Cypher 支持空间值(点),Neo4j 可以将这些点值存储为节点和关系的属性。
属性 SRID(Spatial Reference Identifier,空间参考标识符的缩写)是一个数字,用于标识解析空间类型时所使用的坐标系。您可以将其视为每个空间类型的唯一标识符。
| Cypher 类型 | 驱动程序类型 | SRID |
|---|---|---|
|
|
7203 |
|
|
4326 |
|
|
9157 |
|
|
4979 |
您可以通过 Values.point(srid, x, y[, z])(第三个坐标是可选的)创建点值。从数据库查询返回的点是 PointValue 类型,可以通过 .asPoint() 方法转换为 Point 对象。
Point 值package demo;
import java.util.Map;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.QueryConfig;
import org.neo4j.driver.Values;
public class App {
public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
driver.verifyConnectivity();
var result = driver.executableQuery("RETURN point({x: 2.3, y: 4.5, z: 2}) AS point")
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
var point = result.records().get(0).get("point");
System.out.println(point); // Point{srid=9157, x=2.3, y=4.5, z=2.0}
System.out.println(point.asPoint().x()); // 2.3
}
}
}
Point 值并将其用作属性值package demo;
import java.util.Map;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.QueryConfig;
import org.neo4j.driver.Values;
public class App {
public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
driver.verifyConnectivity();
var location = Values.point(4326, 67.28775180193841, 17.734163823312397); // 4326 = 2D geodetic point
var result = driver.executableQuery("CREATE (p:PlaceOfInterest {location: $location}) RETURN p")
.withParameters(Map.of("location", location))
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
var place = result.records().get(0).get("p").get("location");
System.out.println(place); // Point{srid=4326, x=67.28775180193841, y=17.734163823312397}
System.out.println(place.asPoint().y()); // 17.734163823312397
}
}
}
图类型
图类型仅作为结果传递,不可用作参数.
| Cypher 类型 | 驱动程序类型 |
|---|---|
|
|
|
|
|
NodeValue
表示图中的一个节点。
| 方法 | 返回 |
|---|---|
|
节点标签,作为列表返回。 |
|
节点属性,作为映射(Map)返回。 |
|
给定属性的值。 |
|
节点的字符串标识符。使用时需谨慎,因为不保证 id 值与单个事务范围之外的元素之间的映射关系。换句话说,使用 |
package demo;
import java.util.Map;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.QueryConfig;
public class App {
public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
driver.verifyConnectivity();
// Get a node from the database
var result = driver.executableQuery("MERGE (p:Person:Actor {name: $name, age: 59}) RETURN p")
.withParameters(Map.of("name", "Alice"))
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
// Extract node from result
var nodeVal = result.records().get(0).get("p");
var node = nodeVal.asNode(); // .asNode() -> type org.neo4j.driver.types.Node
System.out.printf("Labels: %s %n", node.labels());
System.out.printf("Properties: %s %n", node.asMap());
System.out.printf("Name property: %s %n", node.get("name"));
System.out.printf("Element ID: %s %n", node.elementId());
/*
Labels: [Person, Actor]
Properties: {name=Alice, age=59}
Name property: "Alice"
Element ID: 4:549a0567-2015-4bb6-a40c-8536bf7227b0:5
*/
}
}
}
有关完整文档,请参阅 API 文档 → NodeValue。
RelationshipValue
表示图中的一个关系。
| 方法 | 返回 |
|---|---|
|
关系类型。 |
|
关系属性,作为映射(Map)返回。 |
|
给定属性的值。 |
|
起始节点的 |
|
结束节点的 |
|
关系的字符串标识符。使用时需谨慎,因为不保证 id 值与单个事务范围之外的元素之间的映射关系。换句话说,使用 |
package demo;
import java.util.Map;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.QueryConfig;
public class App {
public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
driver.verifyConnectivity();
// Get a relationship from the database
var result = driver.executableQuery("""
MERGE (p:Person {name: $name})
MERGE (p)-[r:KNOWS {status: $status, since: date()}]->(friend:Person {name: $friendName})
RETURN r AS friendship
""")
.withParameters(Map.of("name", "Alice", "status", "BFF", "friendName", "Bob"))
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
// Extract relationship from result
var relationshipVal = result.records().get(0).get("friendship");
var relationship = relationshipVal.asRelationship(); // .asRelationship() -> type org.neo4j.driver.types.Relationship
System.out.printf("Type: %s %n", relationship.type());
System.out.printf("Properties: %s %n", relationship.asMap());
System.out.printf("Status property: %s %n", relationship.get("status"));
System.out.printf("Start node: %s %n", relationship.startNodeElementId());
System.out.printf("End node: %s %n", relationship.endNodeElementId());
System.out.printf("Element ID: %s %n", relationship.elementId());
/*
Type: KNOWS
Properties: {since=2024-01-12, status=BFF}
Status property: "BFF"
Start node: 4:549a0567-2015-4bb6-a40c-8536bf7227b0:0
End node: 4:549a0567-2015-4bb6-a40c-8536bf7227b0:6
Element ID: 5:549a0567-2015-4bb6-a40c-8536bf7227b0:1
*/
}
}
}
有关完整文档,请参阅 API 文档 → RelationshipValue。
PathValue
表示图中的一条路径。
驱动程序将路径分解为(可迭代的)段(segments),每个段由一个起始节点、一个关系和一个结束节点组成。可以通过 .start()、.relationship() 和 .end() 方法按顺序检索段实体。
package demo;
import java.util.Map;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.QueryConfig;
public class App {
public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
driver.verifyConnectivity();
// Create some :Person nodes linked by :KNOWS relationships
addFriend(driver, "Alice", "BFF", "Bob");
addFriend(driver, "Bob", "Fiends", "Sofia");
addFriend(driver, "Sofia", "Acquaintances", "Sofia");
// Follow :KNOWS relationships outgoing from Alice three times, return as path
var result = driver.executableQuery("""
MATCH path=(:Person {name: $name})-[:KNOWS*3]->(:Person)
RETURN path AS friendshipChain
""")
.withParameters(Map.of("name", "Alice"))
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
// Extract path from result
var pathVal = result.records().get(0).get("friendshipChain");
var path = pathVal.asPath(); // .asPath() -> type org.neo4j.driver.types.Path
System.out.println("-- Path breakdown --");
for (Path.Segment segment : path) {
System.out.printf(
"%s is friends with %s (%s).%n",
segment.start().get("name").asString(),
segment.end().get("name").asString(),
segment.relationship().get("status").asString());
}
/*
-- Path breakdown --
Alice is friends with Bob (BFF).
Bob is friends with Sofia (Fiends).
Sofia is friends with Sofia (Acquaintances).
*/
}
}
public static void addFriend(Driver driver, String name, String status, String friendName) {
driver.executableQuery("""
MERGE (p:Person {name: $name})
MERGE (p)-[r:KNOWS {status: $status, since: date()}]->(friend:Person {name: $friendName})
""")
.withParameters(Map.of("name", name, "status", status, "friendName", friendName))
.withConfig(QueryConfig.builder().withDatabase("<database-name>").build())
.execute();
}
}
有关完整文档,请参阅 API 文档 → PathValue。
向量类型6.0 版本引入
VectorValue 类型映射到 Cypher 类型 VECTOR。Vector 对象存储为包含同质值的连续内存块。
您可以通过 Values.vector() 方法之一,使用 byte、short、int、long、float、double 列表创建向量。
// import org.neo4j.driver.Values;
var vector = Values.vector(new float[8]);
driver.executableQuery("CREATE (:Value {vector: $vector})")
.withParameters(Map.of("vector", vector))
.execute();
在数据库中存储 VECTOR 对象需要服务器版本 >= 2025.10,企业版。 |
var result = driver.executableQuery("MATCH (v:Value) RETURN v.vector AS vector")
.execute();
var vector = result.records().get(0).get("vector");
System.out.printf("Retrieved vector: %s", vector);
// Retrieved vector: vector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 8, FLOAT32 NOT NULL)
UnsupportedType6.0 版本引入
UnsupportedTypeValue 类型用于 Cypher 查询返回但驱动程序无法识别的数据类型。当客户端版本相对于服务器版本过旧时会发生这种情况。
术语表
- LTS (长期支持版)
-
长期支持 (Long Term Support) 版本是保证在若干年内得到支持的版本。Neo4j 4.4 和 5.26 是 LTS 版本。
- Aura
-
Aura 是 Neo4j 的全托管云服务。它提供免费和付费计划。
- Cypher
-
Cypher 是 Neo4j 的图查询语言,允许您从数据库中检索数据。它就像 SQL,但专用于图数据库。
- APOC
-
Awesome Procedures On Cypher (APOC) 是一个包含(许多)函数的库,这些函数在 Cypher 本身中难以轻松实现。
- Bolt
-
Bolt 是用于 Neo4j 实例和驱动程序之间交互的协议。默认监听 7687 端口。
- ACID
-
原子性 (Atomicity)、一致性 (Consistency)、隔离性 (Isolation)、持久性 (Durability) (ACID) 是保证数据库事务可靠处理的属性。符合 ACID 的 DBMS 确保即使发生故障,数据库中的数据也能保持准确和一致。
- 最终一致性
-
如果一个数据库能保证所有集群成员在某个时间点都存储了数据的最新版本,则该数据库具有最终一致性。
- 因果一致性
-
如果读写查询被集群中的每个成员以相同的顺序看到,则数据库具有因果一致性。这比最终一致性更强。
- NULL
-
空标记不是一种类型,而是缺失值的占位符。更多信息,请参阅 Cypher → 使用
null。 - 事务
-
事务是一个工作单元,要么被提交,要么在失败时被回滚。例如银行转账:它涉及多个步骤,但它们必须全部成功或全部撤销,以避免钱从一个账户扣除却未存入另一个账户的情况。
- 背压
-
背压是对数据流的抵抗力。它确保客户端不会被过快发送的数据压垮,从而超出其处理能力。
- 书签
-
书签是代表数据库某种状态的标记。通过将一个或多个书签与查询一起传递,服务器将确保在所表示的状态建立之前,该查询不会被执行。
- 事务函数
-
事务函数是由
executeRead或executeWrite调用执行的回调。如果发生服务器故障,驱动程序会自动重新执行该回调。 - 驱动程序 (Driver)
-
Driver对象保存了与 Neo4j 数据库建立连接所需的详细信息。