数据类型及其到 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 = "{neo4j-database-uri}";
        final String dbUser = "{neo4j-username}";
        final String dbPassword = "{neo4j-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 (2D 笛卡尔坐标系)

PointValue

7203

POINT (2D WGS-84)

PointValue

4326

POINT (3D 笛卡尔坐标系)

PointValue

9157

POINT (3D 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 = "{neo4j-database-uri}";
        final String dbUser = "{neo4j-username}";
        final String dbPassword = "{neo4j-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 = "{neo4j-database-uri}";
        final String dbUser = "{neo4j-username}";
        final String dbPassword = "{neo4j-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 值和元素之间的映射。换句话说,在不同事务中使用 elementIdMATCH 元素存在风险。

检索节点并显示其详细信息
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 = "{neo4j-database-uri}";
        final String dbUser = "{neo4j-username}";
        final String dbPassword = "{neo4j-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 值和元素之间的映射。换句话说,在不同事务中使用 elementIdMATCH 元素存在风险。

检索关系并显示其详细信息
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 = "{neo4j-database-uri}";
        final String dbUser = "{neo4j-username}";
        final String dbPassword = "{neo4j-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 = "{neo4j-database-uri}";
        final String dbUser = "{neo4j-username}";
        final String dbPassword = "{neo4j-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

Awesome Procedures On Cypher (APOC) 是一个(许多)难以用 Cypher 本身表达的函数库。

Bolt

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

ACID

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

最终一致性

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

因果一致性

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

NULL

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

事务

事务是要么整体提交,要么在失败时回滚的工作单元。例如银行转账:它涉及多个步骤,但它们必须全部成功或被撤销,以避免钱从一个账户中扣除但未添加到另一个账户的情况发生。

反压

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

事务函数

事务函数是由 executeReadexecuteWrite 调用执行的回调。在服务器故障的情况下,驱动程序会自动重新执行该回调。

驱动程序

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

© . All rights reserved.