数据类型和映射到 Cypher 类型

本节中的表格显示了 Cypher 数据类型和 Java 类型之间的映射。

无论其类型如何,查询结果中的所有值都嵌入在 Value 对象中。要提取并将它们强制转换为相应的 Java 类型,请使用 .as<type>()(例如 .asString()asInt() 等)。例如,如果来自数据库的 name 属性是一个字符串,则 record.get("name").asString() 将生成一个 String 对象的属性值。

另一方面,在提交查询参数时,您不需要使用驱动程序类型。驱动程序会自动序列化作为参数传递的原生 Java 类型的对象。

有关驱动程序将数据序列化成的值类型的完整列表,请参阅 API 文档

核心类型

Cypher 类型 驱动程序类型

NULL

NullValue

LIST

ListValue

MAP

MapValue

BOOLEAN

BooleanValue

INTEGER

IntegerValue

FLOAT

FloatValue

STRING

StringValue

ByteArray

BytesValue

时间类型

驱动程序提供了一组符合 ISO-8601 和 Cypher 的时间数据类型。亚秒值以纳秒精度测量。

驱动程序的类型依赖于 Java 的 time 类型。所有时间类型(除 DurationValue 外)实际上都是后台的 java.time 对象。这意味着

  • 如果您想查询数据库并使用时间类型,请实例化一个 java.time 对象并将其用作查询参数(即,您不需要关心驱动程序的类型)。

  • 如果您从数据库中检索时间对象(包括通过 Cypher 时间函数 之一),您将获得下面表格中显示的相应的驱动程序类型。驱动程序实现了将驱动程序时间类型转换为 Java 时间类型的方法(例如 .asZonedDateTime().asOffsetTime() 等)。

Cypher 类型 驱动程序类型

DATE

DateValue

ZONED TIME

TimeValue

LOCAL TIME

LocalTimeValue

ZONED DATETIME

DateTimeValue

LOCAL DATETIME

LocalDateTimeValue

DURATION

DurationValue

如何在查询中使用时间类型
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 = "<URI for Neo4j database>";
        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("neo4j").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空间参考标识符的缩写)是一个数字,用于识别空间类型要以其解释的坐标系。您可以将其视为每种空间类型的唯一标识符。

Cypher 类型 驱动程序类型 SRID

POINT(二维笛卡尔坐标系)

PointValue

7203

POINT(二维 WGS-84)

PointValue

4326

POINT(三维笛卡尔坐标系)

PointValue

9157

POINT(三维 WGS-84)

PointValue

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 = "<URI for Neo4j database>";
        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("neo4j").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 = "<URI for Neo4j database>";
        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("neo4j").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 类型 驱动程序类型

NODE

NodeValue

RELATIONSHIP

RelationshipValue

PATH

PathValue

NodeValue

表示图中的节点。

表 1. 节点对象上的基本方法
方法 返回

.labels()

节点标签,以列表形式。

.asMap()

节点属性,以映射形式。

.get("<propertyName>")

给定属性的值。

.elementId()

关系的字符串标识符。这应该谨慎使用,因为不保证 ID 值与单个事务范围之外的元素之间的映射。换句话说,使用 elementId 在不同事务中MATCH 元素是有风险的。

检索节点并显示其详细信息
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 = "<URI for Neo4j database>";
        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("neo4j").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

表示图中的关系。

表 2. 关系对象上的基本方法
方法 返回

.type()

关系类型。

.asMap()

关系属性,以映射形式。

.get("<propertyName>")

给定属性的值。

.startNodeElementId()

起始节点的 elementId

.endNodeElementId()

结束节点的 elementId

.elementId()

关系的字符串标识符。这应该谨慎使用,因为不保证 ID 值与单个事务范围之外的元素之间的映射。换句话说,使用 elementId 在不同事务中MATCH 元素是有风险的。

检索关系并显示其详细信息
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 = "<URI for Neo4j database>";
        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("neo4j").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

表示图中的路径。

驱动程序将路径分解为(可迭代的),由一个起始节点、一个关系和一个结束节点组成。可以通过 .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 = "<URI for Neo4j database>";
        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("neo4j").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("neo4j").build())
            .execute();
    }
}

有关完整文档,请参阅 API 文档 → PathValue

异常

驱动程序可能会引发许多不同的异常,请参阅 API 文档 → 异常。有关服务器可能返回的错误列表,请参阅 状态码

一些服务器错误被标记为安全重试,无需修改原始请求。此类错误的示例包括死锁、内存问题或连接问题。实现 RetryableException 的驱动程序异常就是这样,因为对导致它的操作的进一步尝试可能成功。这在 显式事务 中运行查询时特别有用,以便了解失败的查询是否值得重新运行。

词汇表

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 数据库连接所需的详细信息。