

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 openCypher 访问 Neptune 图形
<a name="access-graph-opencypher"></a>

Neptune 支持使用 openCypher 构建图形应用程序，openCypher 是目前使用图形数据库的开发人员最常用的查询语言之一。开发人员、业务分析师和数据科学家都喜欢 openCypher 的受 SQL 启发的语法，因为它提供了一种熟悉的结构来为图形应用程序编写查询。

**openCypher** 是一种用于属性图的声明式查询语言，最初由 Neo4j 开发，然后于 2015 年开源，并在 Apache 2 开源许可证下为 [openCypher](http://www.opencypher.org/) 项目做出了贡献。其语法在 [Cypher 查询语言参考版本 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf) 中介绍。

有关 openCypher 规范在 Neptune 支持方面的限制和差异，请参阅[Amazon Neptune 中的 openCypher 规范合规性](feature-opencypher-compliance.md)。

**注意**  
Cypher 查询语言的当前 Neo4j 实现在某些方面与 openCypher 规范有所不同。如果您要将当前的 Neo4j Cypher 代码迁移到 Neptune，请参阅[Neptune 与 Neo4j 的兼容性](migration-compatibility.md)和[重写 Cypher 查询以在 Neptune 上的 openCypher 中运行](migration-opencypher-rewrites.md)以获取帮助。

从引擎版本 1.1.1.0 开始，openCypher 可在 Neptune 中用于生产用途。

## Gremlin 与 openCypher：相似之处和不同之处
<a name="access-graph-opencypher-overview-with-gremlin"></a>

Gremlin 和 openCypher 都是属性图查询语言，它们在很多方面互补。

Gremlin 旨在吸引程序员并无缝融入代码。因此，从设计上讲，Gremlin 势在必行，而对于具有 SQL 或 SPARQL 经验的人来说，可能会更熟悉 openCypher 的声明式语法。对于在 Jupyter 笔记本中使用 Python 的数据科学家来说，Gremlin 似乎更自然，而对于具有一些 SQL 背景的企业用户来说，openCypher 可能看起来更直观。

好消息是，在 Neptune 中，**您不必**在 Gremlin 和 openCypher 之间进行选择。无论使用这两种语言中的哪一种来输入数据，任何一种语言的查询都可以在同一个图形上运行。您可能会发现在某些情况下使用 Gremlin 更方便，而在其它情况下使用 openCypher 更方便，这取决于您正在做的事情。

Gremlin 使用命令式语法，这允许您通过一系列步骤控制如何在图形中移动，每个步骤都接收数据流，对其执行某种操作（使用筛选条件、映射等），然后将结果输出到下一个步骤。Gremlin 查询通常采用 `g.V()` 形式，后跟其它步骤。

在 openCypher 中，您使用一种受 SQL 启发的声明式语法，此语法使用主旨语法（比如 `()-[]->()`）指定要在图形中查找的节点和关系的模式。openCypher 查询通常以 `MATCH` 子句开头，后跟其它子句，如 `WHERE`、`WITH` 和 `RETURN`。

# 开始使用 openCypher
<a name="access-graph-opencypher-overview-getting-started"></a>

无论如何加载，您都可以使用 openCypher 在 Neptune 中查询属性图数据，但不能使用 openCypher 来查询以 RDF 形式加载的数据。

[Neptune 批量加载程序](bulk-load.md)接受采用 [Gremlin 的 CSV 格式](bulk-load-tutorial-format-gremlin.md)和 [openCypher 的 CSV 格式](bulk-load-tutorial-format-opencypher.md)的属性图数据。当然，你也可以使用 Gremlin and/or OpenCypher 查询将属性数据添加到图表中。

有许多在线教程可用于学习 Cypher 查询语言。在这里，一些 openCypher 查询的简短示例可以帮助您了解这种语言，但是到目前为止，开始使用 openCypher 查询 Neptune 图形的最好、最简单的方法是在 [Neptune Workbench](graph-notebooks.md) 中使用 openCypher 笔记本。该工作台是开源的，托管 GitHub 在。[https://github.com/aws-samples/amazon-neptune-samples](https://github.com/aws-samples/amazon-neptune-samples/)

[你可以在 Neptune GitHub 图形笔记本存储库中找到 OpenCypher 笔记本。](https://github.com/aws/graph-notebook/tree/main/src/graph_notebook/notebooks)特别是，请查看 openCypher 的 [Air-routes visualization](https://github.com/aws/graph-notebook/blob/main/src/graph_notebook/notebooks/02-Visualization/Air-Routes-openCypher.ipynb) 和 [English Premier Teams](https://github.com/aws/graph-notebook/blob/main/src/graph_notebook/notebooks/02-Visualization/EPL-openCypher.ipynb) 笔记本。

OpenCypher 处理的数据采用一系列无序地图的形式。 key/value 完善、操作和增强这些映射的主要方法是使用子句来执行诸如模式匹配、插入、更新和删除 key/value 对映射之类的任务。

openCypher 中有几个子句用于在图形中查找数据模式，其中 `MATCH` 最常用。`MATCH` 允许您指定要在图形中查找的节点、关系和筛选条件的模式。例如：
+ **获取所有节点**

  ```
  MATCH (n) RETURN n
  ```
+ **查找连接的节点**

  ```
  MATCH (n)-[r]->(d) RETURN n, r, d
  ```
+ **查找路径**

  ```
  MATCH p=(n)-[r]->(d) RETURN p
  ```
+ **获取所有带有标签的节点**

  ```
  MATCH (n:airport) RETURN n
  ```

请注意，上面的第一个查询返回图形中的每单个节点，接下来的两个查询返回每个具有关系的节点，但通常不建议这样做！几乎在所有情况下，您都希望缩小返回的数据的范围，这可以通过指定节点或关系标签和属性来实现，如第四个示例所示。

您可以在 Neptune [github 示例存储库](https://github.com/aws-samples/amazon-neptune-samples/tree/master/opencypher/Cheatsheet.md)中找到 openCypher 语法的便捷备忘单。

# Neptune openCypher 状态 servlet 和状态端点
<a name="access-graph-opencypher-status"></a>

openCypher 状态端点提供对服务器上当前正在运行或等待运行的查询的相关信息的访问。它还允许您取消这些查询。端点为：

```
https://(the server):(the port number)/openCypher/status
```

您可以使用 HTTP `GET` 和 `POST` 方法从服务器获取当前状态或取消查询。您也可以使用 `DELETE` 方法取消正在运行或等待的查询。

## 状态请求的参数
<a name="access-graph-opencypher-status-parameters"></a>

**状态查询参数**
+ **`includeWaiting`**（`true` 或 `false`）- 当设置为 `true` 且其它参数不存在时，会导致返回等待的查询和正在运行的查询的状态信息。
+ **`cancelQuery`** – 仅与 `GET` 和 `POST` 方法结合使用，以表示这是取消请求。`DELETE` 方法不需要此参数。

  不使用 `cancelQuery` 参数的值，但如果 `cancelQuery` 存在，则需要使用 `queryId` 参数来确定要取消哪个查询。
+ **`queryId`** – 包含特定查询的 ID。

  与 `GET` 或 `POST` 方法结合使用且 `cancelQuery` 参数不存在时，`queryId` 会导致返回其标识的特定查询的状态信息。如果 `cancelQuery` 参数存在，则会取消 `queryId` 标识的特定查询。

  与 `DELETE` 方法结合使用时，`queryId` 始终表示要取消的特定查询。
+ **`silent`** – 仅在取消查询时使用。如果设置为 `true`，则导致取消以静默方式进行。

## 状态请求响应字段
<a name="access-graph-opencypher-status-response-fields"></a>

**如果未提供特定查询的 ID，则为状态响应字段**
+ **acceptedQueryCount**— 已接受但尚未完成的查询数量，包括队列中的查询。
+ **runningQueryCount**— 当前正在运行的 OpenCypher 查询的数量。
+ **queries** – 当前 openCypher 查询的列表。

**特定查询的状态响应字段**
+ **queryId** – 查询的 GUID id。Neptune 为每个查询自动分配该 ID 值，或者您也可以分配自己的 ID（请参阅[将自定义 ID 注入到 Neptune Gremlin 或 SPARQL 查询中](features-query-id.md)）。
+ **queryString** – 提交的查询。在超过 1024 个字符时，将截断为此长度。
+ **queryEvalStats**— 此查询的统计信息：
  + **waited** – 表示查询等待了多长时间，以毫秒为单位。
  + **elapsed** – 到目前为止，查询已运行的毫秒数。
  + **cancelled** – `True` 表示查询已取消，或 `False` 表示尚未取消。

## 状态请求和响应示例
<a name="access-graph-opencypher-status-samples"></a>
+ **请求所有查询的状态，包括正在等待的查询：**

  ```
  curl https://server:port/openCypher/status \
    --data-urlencode "includeWaiting=true"
  ```

  *响应：*

  ```
  {
    "acceptedQueryCount" : 0,
    "runningQueryCount" : 0,
    "queries" : [ ]
  }
  ```
+ **请求正在运行的查询的状态，**不** 包括正在等待的查询：**

  ```
  curl https://server:port/openCypher/status
  ```

  *响应：*

  ```
  {
    "acceptedQueryCount" : 0,
    "runningQueryCount" : 0,
    "queries" : [ ]
  }
  ```
+ **请求单个查询的状态：**

  ```
  curl https://server:port/openCypher/status \
   --data-urlencode "queryId=eadc6eea-698b-4a2f-8554-5270ab17ebee"
  ```

  *响应：*

  ```
  {
    "queryId" : "eadc6eea-698b-4a2f-8554-5270ab17ebee",
    "queryString" : "MATCH (n1)-[:knows]->(n2), (n2)-[:knows]->(n3), (n3)-[:knows]->(n4), (n4)-[:knows]->(n5), (n5)-[:knows]->(n6), (n6)-[:knows]->(n7), (n7)-[:knows]->(n8), (n8)-[:knows]->(n9), (n9)-[:knows]->(n10) RETURN COUNT(n1);",
    "queryEvalStats" : {
      "waited" : 0,
      "elapsed" : 23463,
      "cancelled" : false
    }
  }
  ```
+ **请求取消查询**

  1. 使用 `POST`：

  ```
  curl -X POST https://server:port/openCypher/status \
    --data-urlencode "cancelQuery" \
    --data-urlencode "queryId=f43ce17b-db01-4d37-a074-c76d1c26d7a9"
  ```

  *响应：*

  ```
  {
    "status" : "200 OK",
    "payload" : true
  }
  ```

  2. 使用 `GET`：

  ```
  curl -X GET https://server:port/openCypher/status \
    --data-urlencode "cancelQuery" \
    --data-urlencode "queryId=588af350-cfde-4222-bee6-b9cedc87180d"
  ```

  *响应：*

  ```
  {
    "status" : "200 OK",
    "payload" : true
  }
  ```

  3. 使用 `DELETE`：

  ```
  curl -X DELETE \
    -s "https://server:port/openCypher/status?queryId=b9a516d1-d25c-4301-bb80-10b2743ecf0e"
  ```

  *响应：*

  ```
  {
    "status" : "200 OK",
    "payload" : true
  }
  ```

# 亚马逊 Neptune OpenCypher HTTPS 终端节点
<a name="access-graph-opencypher-queries"></a>

**Topics**
+ [

## OpenCypher 在 HTTPS 终端节点上读取和写入查询
](#access-graph-opencypher-queries-read-write)
+ [

## 默认 OpenCypher JSON 结果格式
](#access-graph-opencypher-queries-results-simple-JSON)
+ [

## 用于多 OpenCypher 部分响应的可选 HTTP 尾随标头
](#optional-http-trailing-headers)

**注意**  
Neptune 目前不支持 HTTP/2 来处理 REST API 请求。客户端在连接到端点时必须使用 HTTP/1.1。

## OpenCypher 在 HTTPS 终端节点上读取和写入查询
<a name="access-graph-opencypher-queries-read-write"></a>

 OpenCypher HTTPS 终端节点支持同时使用和`POST`方法进行读取`GET`和更新查询。不支持 `DELETE` 和 `PUT` 方法。

以下说明将引导您使用`curl`命令和 HTTPS 连接到 OpenCypher 终端节点。必须从与您的 Neptune 数据库实例位于同一虚拟私有云 (VPC) 中的 Amazon EC2 实例中按照这些说明操作。

语法如下：

```
HTTPS://(the server):(the port number)/openCypher
```

以下是示例读取查询，一个使用 `POST`，一个使用 `GET`：

1. 使用 `POST`：

```
curl HTTPS://server:port/openCypher \
  -d "query=MATCH (n1) RETURN n1;"
```

2. 使用 `GET`（查询字符串采用 URL 编码）：

```
curl -X GET \
  "HTTPS://server:port/openCypher?query=MATCH%20(n1)%20RETURN%20n1"
```

以下是示例 write/update 查询，一个使用`POST`，一个使用`GET`：

1. 使用 `POST`：

```
curl HTTPS://server:port/openCypher \
  -d "query=CREATE (n:Person { age: 25 })"
```

2. 使用 `GET`（查询字符串采用 URL 编码）：

```
curl -X GET \
  "HTTPS://server:port/openCypher?query=CREATE%20(n%3APerson%20%7B%20age%3A%2025%20%7D)"
```

## 默认 OpenCypher JSON 结果格式
<a name="access-graph-opencypher-queries-results-simple-JSON"></a>

默认情况下或通过将请求标头显式设置为 `Accept: application/json`，返回以下 JSON 格式。这种格式旨在使用大多数库的原生语言特征轻松解析为对象。

返回的 JSON 文档包含一个字段 `results`，其中包含查询返回值。以下示例显示了常用值的 JSON 格式。

**值响应示例：**

```
{
  "results": [
    {
      "count(a)": 121
    }
  ]
}
```

**节点响应示例：**

```
{
  "results": [
    {
      "a": {
        "~id": "22",
        "~entityType": "node",
        "~labels": [
          "airport"
        ],
        "~properties": {
          "desc": "Seattle-Tacoma",
          "lon": -122.30899810791,
          "runways": 3,
          "type": "airport",
          "country": "US",
          "region": "US-WA",
          "lat": 47.4490013122559,
          "elev": 432,
          "city": "Seattle",
          "icao": "KSEA",
          "code": "SEA",
          "longest": 11901
        }
      }
    }
  ]
}
```

**关系响应示例：**

```
{
  "results": [
    {
      "r": {
        "~id": "7389",
        "~entityType": "relationship",
        "~start": "22",
        "~end": "151",
        "~type": "route",
        "~properties": {
          "dist": 956
        }
      }
    }
  ]
}
```

**路径响应示例：**

```
{
  "results": [
    {
      "p": [
        {
          "~id": "22",
          "~entityType": "node",
          "~labels": [
            "airport"
          ],
          "~properties": {
            "desc": "Seattle-Tacoma",
            "lon": -122.30899810791,
            "runways": 3,
            "type": "airport",
            "country": "US",
            "region": "US-WA",
            "lat": 47.4490013122559,
            "elev": 432,
            "city": "Seattle",
            "icao": "KSEA",
            "code": "SEA",
            "longest": 11901
          }
        },
        {
          "~id": "7389",
          "~entityType": "relationship",
          "~start": "22",
          "~end": "151",
          "~type": "route",
          "~properties": {
            "dist": 956
          }
        },
        {
          "~id": "151",
          "~entityType": "node",
          "~labels": [
            "airport"
          ],
          "~properties": {
            "desc": "Ontario International Airport",
            "lon": -117.600997924805,
            "runways": 2,
            "type": "airport",
            "country": "US",
            "region": "US-CA",
            "lat": 34.0559997558594,
            "elev": 944,
            "city": "Ontario",
            "icao": "KONT",
            "code": "ONT",
            "longest": 12198
          }
        }
      ]
    }
  ]
}
```

## 用于多 OpenCypher 部分响应的可选 HTTP 尾随标头
<a name="optional-http-trailing-headers"></a>

 该功能从 Neptune 引擎发行版本 [1.4.5.0](https://docs.aws.amazon.com/releases/release-1.4.5.0.xml) 开始推出。

 对 OpenCypher 查询和更新的HTTP响应通常以多个区块的形式返回。如果在发送初始响应块（HTTP 状态码为 200）之后发生失败，诊断问题可能会变得很困难。默认情况下，Neptune 通过在消息正文中附加错误消息来报告此类失败，消息正文可能会因响应的流式传输性质而被损坏。

**使用尾随标头**  
 为了改进错误检测和诊断，您可以通过在请求中包含传输编码（TE）尾随标头（te: trailers）来启用尾随标头。这样做会导致 Neptune 在响应块的尾随标头中加入两个新的标头字段：
+  `X-Neptune-Status` – 包含响应代码后跟一个短名称。例如，如果成功，则尾随标头将是：`X-Neptune-Status: 200 OK`。如果失败，响应代码将是 Neptune 引擎错误代码，例如 `X-Neptune-Status: 500 TimeLimitExceededException`。
+  `X-Neptune-Detail` – 对于成功的请求，为空。如果出现错误，则它包含 JSON 错误消息。由于 HTTP 标头值中只允许使用 ASCII 字符，因此 JSON 字符串是经过 URL 编码的。错误消息还会附加到响应消息正文中。

 有关更多信息，请参阅[有关 TE 请求标头的 MDN 页面](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE)。

**OpenCypher 尾随标题用法示例**  
 此示例演示了尾随标头如何帮助诊断超出其时间限制的查询：

```
curl --raw 'https://your-neptune-endpoint:port/openCypher' \
-H 'TE: trailers' \
-d 'query=MATCH(n) RETURN n.firstName'
 
 
Output:
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< trailer: X-Neptune-Status, X-Neptune-Detail
< content-type: application/json;charset=UTF-8
< 
< 
{
  "results": [{
      "n.firstName": "Hossein"
    }, {
      "n.firstName": "Jan"
    }, {
      "n.firstName": "Miguel"
    }, {
      "n.firstName": "Eric"
    }, 
{"detailedMessage":"Operation terminated (deadline exceeded)",
"code":"TimeLimitExceededException",
"requestId":"a7e9d2aa-fbb7-486e-8447-2ef2a8544080",
"message":"Operation terminated (deadline exceeded)"}
0
X-Neptune-Status: 500 TimeLimitExceededException
X-Neptune-Detail: %7B%22detailedMessage%22%3A%22Operation+terminated+%28deadline+exceeded%29%22%2C%22code%22%3A%22TimeLimitExceededException%22%2C%22requestId%22%3A%22a7e9d2aa-fbb7-486e-8447-2ef2a8544080%22%2C%22message%22%3A%22Operation+terminated+%28deadline+exceeded%29%22%7D
```

**响应详解：**  
 前面的示例显示了带有尾随标头的 OpenCypher 响应如何帮助诊断查询失败。在这里，我们可以看到四个连续部分：(1) 初始标头，状态为 200 OK，表示直播已开始；(2) 失败前成功流式传输的部分（损坏）JSON 结果；（3）显示超时的附加错误消息；以及（4）包含最终状态 (500 TimeLimitExceededException) 和详细错误信息的尾随标头。

# 使用 AWS SDK 运行 OpenCypher 查询
<a name="access-graph-opencypher-sdk"></a>

借助 AWS SDK，你可以使用自己选择的编程语言对你的 Neptune 图运行 OpenCypher 查询。Neptune 数据 API SDK（服务名称`neptunedata`）提供了提交 OpenCyp [ExecuteOpenCypherQuery](https://docs.aws.amazon.com/neptune/latest/data-api/API_ExecuteOpenCypherQuery.html)her 查询的操作。

您必须从与您的 Neptune 数据库集群位于同一虚拟私有云 (VPC) 中的 Amazon EC2 实例，或者从与您的集群终端节点有网络连接的位置运行这些示例。

可以在下面找到每种 SDK 语言的`neptunedata`服务 API 参考文档的直接链接：


| 编程语言 | neptunedata API 参考 | 
| --- | --- | 
| C\$1\$1 | [https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-neptunedata/html/annotated.html](https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-neptunedata/html/annotated.html) | 
| Go | [https://docs.aws.amazon.com/sdk-for-go/api/service/neptunedata/](https://docs.aws.amazon.com/sdk-for-go/api/service/neptunedata/) | 
| Java | [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/neptunedata/package-summary.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/neptunedata/package-summary.html) | 
| JavaScript | [https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-neptunedata/](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-neptunedata/) | 
| Kotlin | [https://sdk.amazonaws.com/kotlin/api/latest/neptunedata/index.html](https://sdk.amazonaws.com/kotlin/api/latest/neptunedata/index.html) | 
| .NET | [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Neptunedata/NNeptunedata.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Neptunedata/NNeptunedata.html) | 
| PHP | [https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-Aws.Neptunedata.html](https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-Aws.Neptunedata.html) | 
| Python | [https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptunedata.html](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptunedata.html) | 
| Ruby | [https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Neptunedata.html](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Neptunedata.html) | 
| Rust | [https://crates.io/crates/aws-sdk-neptunedata](https://crates.io/crates/aws-sdk-neptunedata) | 
| CLI | [https://docs.aws.amazon.com/cli/latest/reference/neptunedata/](https://docs.aws.amazon.com/cli/latest/reference/neptunedata/) | 

## OpenCypher S AWS DK 示例
<a name="access-graph-opencypher-sdk-examples"></a>

以下示例说明如何设置`neptunedata`客户端、运行 OpenCypher 查询和打印结果。将*YOUR\$1NEPTUNE\$1HOST*和*YOUR\$1NEPTUNE\$1PORT*替换为您的 Neptune 数据库集群的终端节点和端口。

**客户端超时和重试配置**  
SDK 客户端超时控制*客户端*等待响应的时间。它不能控制查询在服务器上运行多长时间。如果客户端在服务器完成之前超时，则在客户端无法检索结果的情况下，查询可能会继续在 Neptune 上运行。  
我们建议将客户端读取超时设置为`0`（无超时），或者设置为比 Neptune 数据库集群上服务器端 nept [une\$1query\$1timeout](parameters.md#parameters-db-cluster-parameters-neptune_query_timeout) 设置至少长几秒钟的值。这让 Neptune 可以控制查询何时超时。  
我们还建议将最大重试尝试次数设置为`1`（不允许重试）。如果 SDK 重试服务器上仍在运行的查询，则可能会导致重复操作。这对于变异查询尤其重要，因为在变异查询中，重试可能会导致意想不到的重复写入。

------
#### [ Python ]

1. 按照[安装说明安装](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html) Boto3。

1. 创建名为的文件`openCypherExample.py`并粘贴以下代码：

   ```
   import boto3
   import json
   from botocore.config import Config
   
   # Disable the client-side read timeout and retries so that
   # Neptune's server-side neptune_query_timeout controls query duration.
   client = boto3.client(
       'neptunedata',
       endpoint_url=f'https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT',
       config=Config(read_timeout=None, retries={'total_max_attempts': 1})
   )
   
   response = client.execute_open_cypher_query(
       openCypherQuery='MATCH (n) RETURN n LIMIT 1'
   )
   
   print(json.dumps(response['results'], indent=2))
   ```

1. 运行示例：`python openCypherExample.py`

------
#### [ Java ]

1. 按照[安装说明](https://docs.aws.amazon.com//sdk-for-java/latest/developer-guide/setup.html)设置适用于 Java 的 AWS SDK。

1. 使用以下代码设置`NeptunedataClient`、运行 OpenCypher 查询并打印结果：

   ```
   import java.net.URI;
   import java.time.Duration;
   import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
   import software.amazon.awssdk.core.retry.RetryPolicy;
   import software.amazon.awssdk.services.neptunedata.NeptunedataClient;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteOpenCypherQueryRequest;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteOpenCypherQueryResponse;
   
   // Disable the client-side timeout and retries so that
   // Neptune's server-side neptune_query_timeout controls query duration.
   NeptunedataClient client = NeptunedataClient.builder()
       .endpointOverride(URI.create("https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT"))
       .overrideConfiguration(ClientOverrideConfiguration.builder()
           .apiCallTimeout(Duration.ZERO)
           .retryPolicy(RetryPolicy.none())
           .build())
       .build();
   
   ExecuteOpenCypherQueryRequest request = ExecuteOpenCypherQueryRequest.builder()
       .openCypherQuery("MATCH (n) RETURN n LIMIT 1")
       .build();
   
   ExecuteOpenCypherQueryResponse response = client.executeOpenCypherQuery(request);
   
   System.out.println(response.results().toString());
   ```

------
#### [ JavaScript ]

1. 按照[安装说明](https://docs.aws.amazon.com//sdk-for-javascript/v3/developer-guide/getting-started-nodejs.html)为设置 S AWS DK JavaScript。安装 neptunedata 客户端软件包:. `npm install @aws-sdk/client-neptunedata` 

1. 创建名为的文件`openCypherExample.js`并粘贴以下代码：

   ```
   import { NeptunedataClient, ExecuteOpenCypherQueryCommand } from "@aws-sdk/client-neptunedata";
   import { NodeHttpHandler } from "@smithy/node-http-handler";
   
   const config = {
       endpoint: "https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT",
       // Disable the client-side request timeout so that
       // Neptune's server-side neptune_query_timeout controls query duration.
       requestHandler: new NodeHttpHandler({
           requestTimeout: 0
       }),
       maxAttempts: 1
   };
   
   const client = new NeptunedataClient(config);
   
   const input = {
       openCypherQuery: "MATCH (n) RETURN n LIMIT 1"
   };
   
   const command = new ExecuteOpenCypherQueryCommand(input);
   const response = await client.send(command);
   
   console.log(JSON.stringify(response, null, 2));
   ```

1. 运行示例：`node openCypherExample.js`

------

# 使用 Bolt 协议向 Neptune 进行 openCypher 查询
<a name="access-graph-opencypher-bolt"></a>

[Bolt](https://boltprotocol.org/) [是一个面向语句的 client/server 协议，最初由Neo4j开发，并根据知识共享3.0署名许可进行许可。ShareAlike](https://creativecommons.org/licenses/by-sa/3.0/)它由客户端驱动，这意味着客户端始终发起消息交换。

要使用 Neo4j 的 Bolt 驱动程序连接到 Neptune，只需使用 `bolt` URI 方案将 URI 和端口号替换为集群端点即可。如果您有单个 Neptune 实例在运行，请使用 read\$1write 端点。如果有多个实例在运行，则建议使用两个驱动程序，一个用于写入器，另一个用于所有只读副本。如果您只有两个默认端点，则 read\$1write 和 read\$1only 驱动程序就足够了，但是如果您还有自定义端点，可以考虑为每个端点创建一个驱动程序实例。

**注意**  
尽管 Bolt 规范规定 Bolt 可以使用 TCP 或 Bolt 进行连接 WebSockets，但 Neptune 仅支持 Bolt 的 TCP 连接。

对于所有实例大小（t3.medium 和 t4g.medium 除外），Neptune 允许多达 1000 个并发 Bolt 连接。在 t3.medium 和 t4g.medium 实例上，仅允许 512 个连接。

有关使用 Bolt 驱动程序的各种语言的 openCypher 查询的示例，请参阅 Neo4j [驱动程序和语言指南](https://neo4j.com/developer/language-guides/)文档。

**重要**  
适用于 Python、.NET 和 Golang 的 Neo4j Bolt 驱动程序最初不支持 Sign AWS ature v4 身份验证令牌的自动续订。 JavaScript这意味着在签名过期后（通常在 5 分钟内），驱动程序无法进行身份验证，随后的请求会失败。下面的 Python JavaScript、.NET 和 Go 示例都受到此问题的影响。  
有关更多信息，请参阅 [Neo4j Python 驱动程序问题 \$1834](https://github.com/neo4j/neo4j-python-driver/issues/834)、[Neo4j .NET 问题 \$1664](https://github.com/neo4j/neo4j-dotnet-driver/issues/664)、n [eo4j JavaScript 驱动程序问题 \$1993](https://github.com/neo4j/neo4j-javascript-driver/issues/993) 和 ne [o4j](https://github.com/neo4j/neo4j-go-driver/issues/429) GoLang 驱动程序问题 \$1429。  
从驱动程序版本 5.8.0 开始，已为 Go 驱动程序发布了新的预览版重新身份验证 API（请参阅 [v5.8.0 - 需要有关重新身份验证的反馈](https://github.com/neo4j/neo4j-go-driver/discussions/482)）。

## 使用 Bolt 以及 Java 连接到 Neptune
<a name="access-graph-opencypher-bolt-java"></a>

您可以从 Maven [MVN 存储库](https://mvnrepository.com/artifact/org.neo4j.driver/neo4j-java-driver)中下载任何您想使用的版本的驱动程序，也可以将此依赖项添加到您的项目中：

```
<dependency>
  <groupId>org.neo4j.driver</groupId>
  <artifactId>neo4j-java-driver</artifactId>
  <version>4.3.3</version>
</dependency>
```

然后，要使用这些 Bolt 驱动程序之一在 Java 中连接到 Neptune，请使用如下代码在集群中为该 primary/writer 实例创建一个驱动程序实例：

```
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;

final Driver driver =
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
    AuthTokens.none(),
    Config.builder().withEncryption()
                    .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                    .build());
```

如果您有一个或多个读取器副本，则同样可以使用如下代码为它们创建驱动程序实例：

```
final Driver read_only_driver =              // (without connection timeout)
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
      Config.builder().withEncryption()
                      .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                      .build());
```

或者，对于超时：

```
final Driver read_only_timeout_driver =      // (with connection timeout)
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
    Config.builder().withConnectionTimeout(30, TimeUnit.SECONDS)
                    .withEncryption()
                    .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                    .build());
```

如果您有自定义端点，则可能还值得为每个端点创建一个驱动程序实例。

## 使用 Bolt 的 Python openCypher 查询示例
<a name="access-graph-opencypher-bolt-python"></a>

下面介绍如何使用 Bolt 在 Python 中进行 openCypher 查询：

```
python -m pip install neo4j
```

```
from neo4j import GraphDatabase
uri = "bolt://(your cluster endpoint URL):(your cluster port)"
driver = GraphDatabase.driver(uri, auth=("username", "password"), encrypted=True)
```

请注意，`auth` 参数会被忽略。

## 使用 Bolt 的 .NET openCypher 查询示例
<a name="access-graph-opencypher-bolt-dotnet"></a>

要使用 Bolt 在.NET 中进行 OpenCypher 查询，第一步是使用安装 Neo4j 驱动程序。 NuHet要进行同步调用，请使用 `.Simple` 版本，如下所示：

```
Install-Package Neo4j.Driver.Simple-4.3.0
```

```
using Neo4j.Driver;

namespace hello
{
  // This example creates a node and reads a node in a Neptune
  // Cluster where IAM Authentication is not enabled.
  public class HelloWorldExample : IDisposable
  {
    private bool _disposed = false;
    private readonly IDriver _driver;
    private static string url = "bolt://(your cluster endpoint URL):(your cluster port)";
    private static string createNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'";
    private static string readNodeQuery = "MATCH(n:Greeting) RETURN n.message";

    ~HelloWorldExample() => Dispose(false);

    public HelloWorldExample(string uri)
    {
      _driver = GraphDatabase.Driver(uri, AuthTokens.None, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted));
    }

    public void createNode()
    {
      // Open a session
      using (var session = _driver.Session())
      {
         // Run the query in a write transaction
        var greeting = session.WriteTransaction(tx =>
        {
          var result = tx.Run(createNodeQuery);
          // Consume the result
          return result.Consume();
        });

        // The output will look like this:
        //   ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample".....
        Console.WriteLine(greeting);
      }
    }

    public void retrieveNode()
    {
      // Open a session
      using (var session = _driver.Session())
      {
        // Run the query in a read transaction
        var greeting = session.ReadTransaction(tx =>
        {
          var result = tx.Run(readNodeQuery);
          // Consume the result. Read the single node
          // created in a previous step.
          return result.Single()[0].As<string>();
        });
        // The output will look like this:
        //   HelloWorldExample
        Console.WriteLine(greeting);
      }
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (_disposed)
        return;
      if (disposing)
      {
        _driver?.Dispose();
      }
      _disposed = true;
    }

    public static void Main()
    {
      using (var apiCaller = new HelloWorldExample(url))
      {
        apiCaller.createNode();
        apiCaller.retrieveNode();
      }
    }
  }
}
```

## 结合使用 Bolt 和 IAM 身份验证的 Java openCypher 查询示例
<a name="access-graph-opencypher-bolt-java-iam-auth"></a>

下面的 Java 代码显示如何结合使用 Bolt 和 IAM 身份验证在 Java 中进行 openCypher 查询。 JavaDoc 评论描述了它的用法。一旦驱动程序实例可用，您就可以使用它发出多个经过身份验证的请求。

```
package software.amazon.neptune.bolt;

import com.amazonaws.DefaultRequest;
import com.amazonaws.Request;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.http.HttpMethodName;
import com.google.gson.Gson;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.neo4j.driver.internal.security.InternalAuthToken;
import org.neo4j.driver.internal.value.StringValue;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static com.amazonaws.auth.internal.SignerConstants.AUTHORIZATION;
import static com.amazonaws.auth.internal.SignerConstants.HOST;
import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_DATE;
import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SECURITY_TOKEN;

/**
 * Use this class instead of `AuthTokens.basic` when working with an IAM
 * auth-enabled server. It works the same as `AuthTokens.basic` when using
 * static credentials, and avoids making requests with an expired signature
 * when using temporary credentials. Internally, it generates a new signature
 * on every invocation (this may change in a future implementation).
 *
 * Note that authentication happens only the first time for a pooled connection.
 *
 * Typical usage:
 *
 * NeptuneAuthToken authToken = NeptuneAuthToken.builder()
 *     .credentialsProvider(credentialsProvider)
 *     .region("aws region")
 *     .url("cluster endpoint url")
 *     .build();
 *
 * Driver driver = GraphDatabase.driver(
 *     authToken.getUrl(),
 *     authToken,
 *     config
 * );
 */

public class NeptuneAuthToken extends InternalAuthToken {
  private static final String SCHEME = "basic";
  private static final String REALM = "realm";
  private static final String SERVICE_NAME = "neptune-db";
  private static final String HTTP_METHOD_HDR = "HttpMethod";
  private static final String DUMMY_USERNAME = "username";
  @NonNull
  private final String region;
  @NonNull
  @Getter
  private final String url;
  @NonNull
  private final AWSCredentialsProvider credentialsProvider;
  private final Gson gson = new Gson();

  @Builder
  private NeptuneAuthToken(
      @NonNull final String region,
      @NonNull final String url,
      @NonNull final AWSCredentialsProvider credentialsProvider
  ) {
      // The superclass caches the result of toMap(), which we don't want
      super(Collections.emptyMap());
      this.region = region;
      this.url = url;
      this.credentialsProvider = credentialsProvider;
  }

  @Override
  public Map<String, Value> toMap() {
    final Map<String, Value> map = new HashMap<>();
    map.put(SCHEME_KEY, Values.value(SCHEME));
    map.put(PRINCIPAL_KEY, Values.value(DUMMY_USERNAME));
    map.put(CREDENTIALS_KEY, new StringValue(getSignedHeader()));
    map.put(REALM_KEY, Values.value(REALM));

    return map;
  }

  private String getSignedHeader() {
    final Request<Void> request = new DefaultRequest<>(SERVICE_NAME);
    request.setHttpMethod(HttpMethodName.GET);
    request.setEndpoint(URI.create(url));
    // Comment out the following line if you're using an engine version older than 1.2.0.0
    request.setResourcePath("/opencypher");

    final AWS4Signer signer = new AWS4Signer();
    signer.setRegionName(region);
    signer.setServiceName(request.getServiceName());
    signer.sign(request, credentialsProvider.getCredentials());

    return getAuthInfoJson(request);
  }

  private String getAuthInfoJson(final Request<Void> request) {
    final Map<String, Object> obj = new HashMap<>();
    obj.put(AUTHORIZATION, request.getHeaders().get(AUTHORIZATION));
    obj.put(HTTP_METHOD_HDR, request.getHttpMethod());
    obj.put(X_AMZ_DATE, request.getHeaders().get(X_AMZ_DATE));
    obj.put(HOST, request.getHeaders().get(HOST));
    obj.put(X_AMZ_SECURITY_TOKEN, request.getHeaders().get(X_AMZ_SECURITY_TOKEN));

    return gson.toJson(obj);
  }
}
```

## 结合使用 Bolt 和 IAM 身份验证的 Python openCypher 查询示例
<a name="access-graph-opencypher-bolt-python-iam-auth"></a>

下面的 Python 类允许您结合使用 Bolt 和 IAM 身份验证在 Python 中进行 openCypher 查询：

```
import json

from neo4j import Auth
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
from botocore.auth import (
  SigV4Auth,
  _host_from_url,
)

SCHEME = "basic"
REALM = "realm"
SERVICE_NAME = "neptune-db"
DUMMY_USERNAME = "username"
HTTP_METHOD_HDR = "HttpMethod"
HTTP_METHOD = "GET"
AUTHORIZATION = "Authorization"
X_AMZ_DATE = "X-Amz-Date"
X_AMZ_SECURITY_TOKEN = "X-Amz-Security-Token"
HOST = "Host"


class NeptuneAuthToken(Auth):
  def __init__(
    self,
    credentials: Credentials,
    region: str,
    url: str,
    **parameters
  ):
    # Do NOT add "/opencypher" in the line below if you're using an engine version older than 1.2.0.0
    request = AWSRequest(method=HTTP_METHOD, url=url + "/opencypher")
    request.headers.add_header("Host", _host_from_url(request.url))
    sigv4 = SigV4Auth(credentials, SERVICE_NAME, region)
    sigv4.add_auth(request)

    auth_obj = {
      hdr: request.headers[hdr]
      for hdr in [AUTHORIZATION, X_AMZ_DATE, X_AMZ_SECURITY_TOKEN, HOST]
    }
    auth_obj[HTTP_METHOD_HDR] = request.method
    creds: str = json.dumps(auth_obj)
    super().__init__(SCHEME, DUMMY_USERNAME, creds, REALM, **parameters)
```

您可以使用该类创建驱动程序，如下所示：

```
  authToken = NeptuneAuthToken(creds, REGION, URL)
  driver = GraphDatabase.driver(URL, auth=authToken, encrypted=True)
```

## 结合使用 IAM 身份验证和 Bolt 的 Node.js 示例
<a name="access-graph-opencypher-bolt-nodejs-iam-auth"></a>

下面的 Node.js 代码使用 JavaScript 版本 3 的 AWS SDK 和 ES6 语法来创建对请求进行身份验证的驱动程序：

```
import neo4j from "neo4j-driver";
import { HttpRequest }  from "@smithy/protocol-http";
import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { SignatureV4 } from "@smithy/signature-v4";
import crypto from "@aws-crypto/sha256-js";
const { Sha256 } = crypto;
import assert from "node:assert";

const region = "us-west-2";
const serviceName = "neptune-db";
const host = "(your cluster endpoint URL)";
const port = 8182;
const protocol = "bolt";
const hostPort = host + ":" + port;
const url = protocol + "://" + hostPort;
const createQuery = "CREATE (n:Greeting {message: 'Hello'}) RETURN ID(n)";
const readQuery = "MATCH(n:Greeting) WHERE ID(n) = $id RETURN n.message";

async function signedHeader() {
  const req = new HttpRequest({
    method: "GET",
    protocol: protocol,
    hostname: host,
    port: port,
    // Comment out the following line if you're using an engine version older than 1.2.0.0
    path: "/opencypher",
    headers: {
      host: hostPort
    }
  });

  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region: region,
    service: serviceName,
    sha256: Sha256
  });

  return signer.sign(req, { unsignableHeaders: new Set(["x-amz-content-sha256"]) })
    .then((signedRequest) => {
      const authInfo = {
        "Authorization": signedRequest.headers["authorization"],
        "HttpMethod": signedRequest.method,
        "X-Amz-Date": signedRequest.headers["x-amz-date"],
        "Host": signedRequest.headers["host"],
        "X-Amz-Security-Token": signedRequest.headers["x-amz-security-token"]
      };
      return JSON.stringify(authInfo);
    });
}

async function createDriver() {
  let authToken = { scheme: "basic", realm: "realm", principal: "username", credentials: await signedHeader() };

  return neo4j.driver(url, authToken, {
      encrypted: "ENCRYPTION_ON",
      trust: "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES",
      maxConnectionPoolSize: 1,
      // logging: neo4j.logging.console("debug")
    }
  );
}

async function unmanagedTxn(driver) {
  const session = driver.session();
  const tx = session.beginTransaction();
  try {
    const created = await tx.run(createQuery);
    const matched = await tx.run(readQuery, { id: created.records[0].get(0) });
    const msg = matched.records[0].get("n.message");
    assert.equal(msg, "Hello");
    await tx.commit();
  } catch (err) {
    // The transaction will be rolled back, now handle the error.
    console.log(err);
  } finally {
    await session.close();
  }
}

const driver = await createDriver();
try {
  await unmanagedTxn(driver);
} catch (err) {
  console.log(err);
} finally {
  await driver.close();
}
```

## 结合使用 Bolt 和 IAM 身份验证的 .NET openCypher 查询示例
<a name="access-graph-opencypher-bolt-dotnet-iam-auth"></a>

要在 .NET 中启用 IAM 身份验证，您需要在建立连接时签署请求。以下示例显示了如何创建用于生成身份验证令牌的 `NeptuneAuthToken` 帮助程序：

```
using Amazon.Runtime;
using Amazon.Util;
using Neo4j.Driver;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Web;

namespace Hello
{
  /*
   * Use this class instead of `AuthTokens.None` when working with an IAM-auth-enabled server.
   *
   * Note that authentication happens only the first time for a pooled connection.
   *
   * Typical usage:
   *
   * var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host);
   * _driver = GraphDatabase.Driver(Url, authToken, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted));
   */

  public class NeptuneAuthToken
  {
    private const string ServiceName = "neptune-db";
    private const string Scheme = "basic";
    private const string Realm = "realm";
    private const string DummyUserName = "username";
    private const string Algorithm = "AWS4-HMAC-SHA256";
    private const string AWSRequest = "aws4_request";

    private readonly string _accessKey;
    private readonly string _secretKey;
    private readonly string _region;

    private readonly string _emptyPayloadHash;

    private readonly SHA256 _sha256;


    public NeptuneAuthToken(string awsKey = null, string secretKey = null, string region = null)
    {
      var awsCredentials = awsKey == null || secretKey == null
        ? FallbackCredentialsFactory.GetCredentials().GetCredentials()
        : null;

      _accessKey = awsKey ?? awsCredentials.AccessKey;
      _secretKey = secretKey ?? awsCredentials.SecretKey;
      _region = region ?? FallbackRegionFactory.GetRegionEndpoint().SystemName; //ex: us-east-1

      _sha256 = SHA256.Create();
      _emptyPayloadHash = Hash(Array.Empty<byte>());
    }

    public IAuthToken GetAuthToken(string url)
    {
      return AuthTokens.Custom(DummyUserName, GetCredentials(url), Realm, Scheme);
    }

    /******************** AWS SIGNING FUNCTIONS *********************/
    private string Hash(byte[] bytesToHash)
    {
      return ToHexString(_sha256.ComputeHash(bytesToHash));
    }

    private static byte[] HmacSHA256(byte[] key, string data)
    {
      return new HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(data));
    }

    private byte[] GetSignatureKey(string dateStamp)
    {
      var kSecret = Encoding.UTF8.GetBytes($"AWS4{_secretKey}");
      var kDate = HmacSHA256(kSecret, dateStamp);
      var kRegion = HmacSHA256(kDate, _region);
      var kService = HmacSHA256(kRegion, ServiceName);
      return HmacSHA256(kService, AWSRequest);
    }

    private static string ToHexString(byte[] array)
    {
      return Convert.ToHexString(array).ToLowerInvariant();
    }

    private string GetCredentials(string url)
    {
      var request = new HttpRequestMessage
      {
        Method = HttpMethod.Get,
        RequestUri = new Uri($"https://{url}/opencypher")
      };

      var signedrequest = Sign(request);

      var headers = new Dictionary<string, object>
      {
        [HeaderKeys.AuthorizationHeader] = signedrequest.Headers.GetValues(HeaderKeys.AuthorizationHeader).FirstOrDefault(),
        ["HttpMethod"] = HttpMethod.Get.ToString(),
        [HeaderKeys.XAmzDateHeader] = signedrequest.Headers.GetValues(HeaderKeys.XAmzDateHeader).FirstOrDefault(),
        // Host should be capitalized, not like in Amazon.Util.HeaderKeys.HostHeader
        ["Host"] = signedrequest.Headers.GetValues(HeaderKeys.HostHeader).FirstOrDefault(),
      };

      return JsonSerializer.Serialize(headers);
    }

    private HttpRequestMessage Sign(HttpRequestMessage request)
    {
      var now = DateTimeOffset.UtcNow;
      var amzdate = now.ToString("yyyyMMddTHHmmssZ");
      var datestamp = now.ToString("yyyyMMdd");

      if (request.Headers.Host == null)
      {
        request.Headers.Host = $"{request.RequestUri.Host}:{request.RequestUri.Port}";
      }

      request.Headers.Add(HeaderKeys.XAmzDateHeader, amzdate);

      var canonicalQueryParams = GetCanonicalQueryParams(request);

      var canonicalRequest = new StringBuilder();
      canonicalRequest.Append(request.Method + "\n");
      canonicalRequest.Append(request.RequestUri.AbsolutePath + "\n");
      canonicalRequest.Append(canonicalQueryParams + "\n");

      var signedHeadersList = new List<string>();
      foreach (var header in request.Headers.OrderBy(a => a.Key.ToLowerInvariant()))
      {
        canonicalRequest.Append(header.Key.ToLowerInvariant());
        canonicalRequest.Append(':');
        canonicalRequest.Append(string.Join(",", header.Value.Select(s => s.Trim())));
        canonicalRequest.Append('\n');
        signedHeadersList.Add(header.Key.ToLowerInvariant());
      }
      canonicalRequest.Append('\n');

      var signedHeaders = string.Join(";", signedHeadersList);
      canonicalRequest.Append(signedHeaders + "\n");
      canonicalRequest.Append(_emptyPayloadHash);

      var credentialScope = $"{datestamp}/{_region}/{ServiceName}/{AWSRequest}";
      var stringToSign = $"{Algorithm}\n{amzdate}\n{credentialScope}\n"
        + Hash(Encoding.UTF8.GetBytes(canonicalRequest.ToString()));

      var signing_key = GetSignatureKey(datestamp);
      var signature = ToHexString(HmacSHA256(signing_key, stringToSign));

      request.Headers.TryAddWithoutValidation(HeaderKeys.AuthorizationHeader,
        $"{Algorithm} Credential={_accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}");

      return request;
    }

    private static string GetCanonicalQueryParams(HttpRequestMessage request)
    {
      var querystring = HttpUtility.ParseQueryString(request.RequestUri.Query);

      // Query params must be escaped in upper case (i.e. "%2C", not "%2c").
      var queryParams = querystring.AllKeys.OrderBy(a => a)
        .Select(key => $"{key}={Uri.EscapeDataString(querystring[key])}");
      return string.Join("&", queryParams);
    }
  }
}
```

下面介绍了如何结合使用 Bolt 和 IAM 身份验证在 .NET 中进行 openCypher 查询。下面的示例使用 `NeptuneAuthToken` 帮助程序：

```
using Neo4j.Driver;

namespace Hello
{
  public class HelloWorldExample
  {
    private const string Host = "(your hostname):8182";
    private const string Url = $"bolt://{Host}";
    private const string CreateNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'";
    private const string ReadNodeQuery = "MATCH(n:Greeting) RETURN n.message";

    private const string AccessKey = "(your access key)";
    private const string SecretKey = "(your secret key)";
    private const string Region = "(your AWS region)"; // e.g. "us-west-2"

    private readonly IDriver _driver;

    public HelloWorldExample()
    {
      var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host);

      // Note that when the connection is reinitialized after max connection lifetime
      // has been reached, the signature token could have already been expired (usually 5 min)
      // You can face exceptions like:
      //   `Unexpected server exception 'Signature expired: XXXX is now earlier than YYYY (ZZZZ - 5 min.)`
      _driver = GraphDatabase.Driver(Url, authToken, o =>
                o.WithMaxConnectionLifetime(TimeSpan.FromMinutes(60)).WithEncryptionLevel(EncryptionLevel.Encrypted));
    }

    public async Task CreateNode()
    {
      // Open a session
      using (var session = _driver.AsyncSession())
      {
        // Run the query in a write transaction
        var greeting = await session.WriteTransactionAsync(async tx =>
        {
          var result = await tx.RunAsync(CreateNodeQuery);
          // Consume the result
          return await result.ConsumeAsync();
        });

        // The output will look like this:
        //   ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample".....
        Console.WriteLine(greeting.Query);
      }
    }

    public async Task RetrieveNode()
    {
      // Open a session
      using (var session = _driver.AsyncSession())
      {
        // Run the query in a read transaction
        var greeting = await session.ReadTransactionAsync(async tx =>
        {
          var result = await tx.RunAsync(ReadNodeQuery);
          var records = await result.ToListAsync();

          // Consume the result. Read the single node
          // created in a previous step.
          return records[0].Values.First().Value;
        });
        // The output will look like this:
        //   HelloWorldExample
        Console.WriteLine(greeting);
      }
    }
  }
}
```

此示例可以通过使用以下软件包在 `.NET 6` 或 `.NET 7` 上运行以下代码来启动：
+ **`Neo4j`**`.Driver=4.3.0`
+ **`AWSSDK`**`.Core=3.7.102.1`

```
namespace Hello
{
  class Program
  {
    static async Task Main()
    {
      var apiCaller = new HelloWorldExample();

      await apiCaller.CreateNode();
      await apiCaller.RetrieveNode();
    }
  }
}
```

## 结合使用 Bolt 和 IAM 身份验证的 Golang openCypher 查询示例
<a name="access-graph-opencypher-bolt-golang-iam-auth"></a>

下面的 Golang 软件包展示了如何结合使用 Bolt 和 IAM 身份验证以 Go 语言进行 openCypher 查询：

```
package main

import (
  "context"
  "encoding/json"
  "fmt"
  "github.com/aws/aws-sdk-go/aws/credentials"
  "github.com/aws/aws-sdk-go/aws/signer/v4"
  "github.com/neo4j/neo4j-go-driver/v5/neo4j"
  "log"
  "net/http"
  "os"
  "time"
)

const (
  ServiceName   = "neptune-db"
  DummyUsername = "username"
)

// Find node by id using Go driver
func findNode(ctx context.Context, region string, hostAndPort string, nodeId string) (string, error) {
  req, err := http.NewRequest(http.MethodGet, "https://"+hostAndPort+"/opencypher", nil)

  if err != nil {
    return "", fmt.Errorf("error creating request, %v", err)
  }

  // credentials must have been exported as environment variables
  signer := v4.NewSigner(credentials.NewEnvCredentials())
  _, err = signer.Sign(req, nil, ServiceName, region, time.Now())

  if err != nil {
    return "", fmt.Errorf("error signing request: %v", err)
  }

  hdrs := []string{"Authorization", "X-Amz-Date", "X-Amz-Security-Token"}
  hdrMap := make(map[string]string)
  for _, h := range hdrs {
    hdrMap[h] = req.Header.Get(h)
  }

  hdrMap["Host"] = req.Host
  hdrMap["HttpMethod"] = req.Method

  password, err := json.Marshal(hdrMap)
  if err != nil {
    return "", fmt.Errorf("error creating JSON, %v", err)
  }
  authToken := neo4j.BasicAuth(DummyUsername, string(password), "")
  // +s enables encryption with a full certificate check
  // Use +ssc to disable client side TLS verification
  driver, err := neo4j.NewDriverWithContext("bolt+s://"+hostAndPort+"/opencypher", authToken)
  if err != nil {
    return "", fmt.Errorf("error creating driver, %v", err)
  }

  defer driver.Close(ctx)

  if err := driver.VerifyConnectivity(ctx); err != nil {
    log.Fatalf("failed to verify connection, %v", err)
  }

  config := neo4j.SessionConfig{}

  session := driver.NewSession(ctx, config)
  defer session.Close(ctx)

  result, err := session.Run(
    ctx,
    fmt.Sprintf("MATCH (n) WHERE ID(n) = '%s' RETURN n", nodeId),
    map[string]any{},
  )
  if err != nil {
    return "", fmt.Errorf("error running query, %v", err)
  }

  if !result.Next(ctx) {
    return "", fmt.Errorf("node not found")
  }

  n, found := result.Record().Get("n")
  if !found {
    return "", fmt.Errorf("node not found")
  }

  return fmt.Sprintf("+%v\n", n), nil
}

func main() {
  if len(os.Args) < 3 {
    log.Fatal("Usage: go main.go (region) (host and port)")
  }
  region := os.Args[1]
  hostAndPort := os.Args[2]
  ctx := context.Background()

  res, err := findNode(ctx, region, hostAndPort, "72c2e8c1-7d5f-5f30-10ca-9d2bb8c4afbc")
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(res)
}
```

## Neptune 中的 Bolt 连接行为
<a name="access-graph-opencypher-bolt-connections"></a>

以下是有关 Neptune Bolt 连接的一些注意事项：
+ 由于 Bolt 连接是在 TCP 层创建的，因此您不能像使用 HTTP 端点那样在它们前面使用[应用程序负载均衡器](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html)。
+ Neptune 用于 Bolt 连接的端口是您的数据库集群的端口。
+ 根据传递给它的 Bolt 序言，Neptune 服务器会选择最合适的 Bolt 版本（1、2、3 或 4.0）。
+ 客户端在任何时间点可以打开的与 Neptune 服务器的最大连接数为 1000。
+ 如果客户端在查询后没有关闭连接，则该连接可用于执行下一个查询。
+ 但是，如果连接空闲了 20 分钟，服务器会自动将其关闭。
+ 如果未启用 IAM 身份验证，则可以使用 `AuthTokens.none()`，而不是提供虚拟用户名和密码。例如，在 Java 中：

  ```
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", AuthTokens.none(),
      Config.builder().withEncryption().withTrustStrategy(TrustStrategy.trustSystemCertificates()).build());
  ```
+ 启用 IAM 身份验证后，如果 Bolt 连接由于某种其它原因尚未关闭，则始终会在建立连接 10 天后的几分钟内断开连接。
+ 如果客户端在没有消耗先前查询的结果的情况下通过连接发送查询以供执行，则新查询将被丢弃。要改为丢弃之前的结果，客户端必须通过连接发送重置消息。
+ 对于给定连接，一次只能创建一个事务。
+ 如果在事务期间发生异常，Neptune 服务器会回滚事务并关闭连接。在这种情况下，驱动程序会为下一个查询创建一个新连接。
+ 请注意，会话不是线程安全的。多个并行操作必须使用多个单独的会话。

# openCypher 参数化查询示例
<a name="opencypher-parameterized-queries"></a>

Neptune 支持参数化的 openCypher 查询。这样，您就可以通过不同的参数多次使用相同的查询结构。由于查询结构不会变化，Neptune 可以缓存其抽象语法树 (AST)，而不必多次解析它。

## 使用 HTTPS 端点进行的 openCypher 参数化查询示例
<a name="opencypher-http-parameterized-queries"></a>

以下是在 Neptune openCypher HTTPS 端点上使用参数化查询的示例。查询为：

```
MATCH (n {name: $name, age: $age})
RETURN n
```

参数定义如下：

```
parameters={"name": "john", "age": 20}
```

使用 `GET`，您可以按如下所示提交参数化查询：

```
curl -k \
  "https://localhost:8182/openCypher?query=MATCH%20%28n%20%7Bname:\$name,age:\$age%7D%29%20RETURN%20n&parameters=%7B%22name%22:%22john%22,%22age%22:20%7D"
```

或者，您也可以使用 `POST`：

```
curl -k \
  https://localhost:8182/openCypher \
  -d "query=MATCH (n {name: \$name, age: \$age}) RETURN n" \
  -d "parameters={\"name\": \"john\", \"age\": 20}"
```

或者，使用 `DIRECT POST`：

```
curl -k \
   -H "Content-Type: application/opencypher" \
  "https://localhost:8182/openCypher?parameters=%7B%22name%22:%22john%22,%22age%22:20%7D" \
  -d "MATCH (n {name: \$name, age: \$age}) RETURN n"
```

## 使用 Bolt 进行 openCypher 参数化查询的示例
<a name="opencypher-bolt-parameterized-queries"></a>

以下是使用 Bolt 协议进行 openCypher 参数化查询的 Python 示例：

```
from neo4j import GraphDatabase
uri = "bolt://[neptune-endpoint-url]:8182"
driver = GraphDatabase.driver(uri, auth=("", ""))

def match_name_and_age(tx, name, age):
  # Parameterized Query
  tx.run("MATCH (n {name: $name, age: $age}) RETURN n", name=name, age=age)

with driver.session() as session:
  # Parameters
  session.read_transaction(match_name_and_age, "john", 20)

driver.close()
```

以下是使用 Bolt 协议进行 openCypher 参数化查询的 Java 示例：

```
Driver driver = GraphDatabase.driver("bolt+s://(your cluster endpoint URL):8182");
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("name", "john");
parameters.put("age", 20);
String queryString = "MATCH (n {name: $name, age: $age}) RETURN n";
Result result = driver.session().run(queryString, parameters);
```

# openCypher 数据模型
<a name="access-graph-opencypher-data-model"></a>

Neptune openCypher 引擎建立在与 Gremlin 相同的属性图模型之上。具体而言：
+ 每个节点都有一个或多个标签。如果您插入一个没有标签的节点，则会附加一个名为 `vertex` 的默认标签。如果您尝试删除节点的所有标签，则会引发错误。
+ 关系是一个正好只有一种关系类型的实体，它在两个节点之间形成单向连接（即*从*其中一个节点*到*另一个节点）。
+ 节点和关系都可以具有属性，但不是必须具有属性。Neptune 支持具有零个属性的节点和关系。
+ Neptune 不支持元属性，这些元属性也未包含在 openCypher 规范中。
+ 如果图形中的属性是使用 Gremlin 创建的，则它们可以是多值的。也就是说，节点或关系属性可以有一组不同的值，而不仅仅是一个值。Neptune 扩展了 openCypher 语义，以从容地处理多值属性。

[openCypher 数据格式](bulk-load-tutorial-format-opencypher.md)中记录了支持的数据类型。但是，我们目前不建议将 `Array` 属性值插入到 openCypher 图形中。尽管可以使用批量加载程序插入数组属性值，但当前 Neptune openCypher 版本将其视为一组多值属性，而不是单个列表值。

以下是此版本支持的数据类型列表：
+ `Bool`
+ `Byte`
+ `Short`
+ `Int` 
+ `Long`
+ `Float`（包括正负无穷大和 NaN，但不包括 INF）
+ `Double`（包括正负无穷大和 NaN，但不包括 INF）
+ `DateTime` 
+ `String`

# openCypher `explain` 特征
<a name="access-graph-opencypher-explain"></a>

openCypher `explain` 特征是 Amazon Neptune 中的一种自助式工具，可帮助您了解 Neptune 引擎所采用的执行方法。要调用 explain，您需要使用 `explain=mode` 向 openCypher [HTTPS](access-graph-opencypher-queries.md) 请求传递一个参数，其中 `mode` 值可以是以下值之一：

****
+ **`static`** – 在 `static` 模式下，`explain` 仅输出查询计划的静态结构。它不实际运行查询。
+ **`dynamic`** – 在 `dynamic` 模式下，`explain` 还会运行查询，并包括查询计划的动态方面。这些方面可能包括流经运算符的中间绑定的数量、传入绑定与传出绑定的比率以及每个运算符使用的总时间。
+ **`details`** – 在 `details` 模式下，`explain` 输出动态模式中显示的信息以及其它详细信息，例如实际的 openCypher 查询字符串和某个连接运算符之下模式的估计范围计数。

  

例如，使用 `POST`：

```
curl HTTPS://server:port/openCypher \
  -d "query=MATCH (n) RETURN n LIMIT 1;" \
  -d "explain=dynamic"
```

或者，使用 `GET`：

```
curl -X GET \
  "HTTPS://server:port/openCypher?query=MATCH%20(n)%20RETURN%20n%20LIMIT%201&explain=dynamic"
```

## Neptune 中 openCypher `explain` 的限制
<a name="access-graph-opencypher-explain-limitations"></a>

openCypher Explain 的当前版本存在以下限制：
+ Explain 计划目前仅适用于执行只读操作的查询。不支持执行任何类型的突变（例如 `CREATE`、`DELETE`、`MERGE`、`SET` 等）的查询。
+ 特定计划的运算符和输出可能会在将来版本中发生变化。

## openCypher `explain` 输出中的 DFE 运算符
<a name="access-graph-opencypher-dfe-operators"></a>

要使用 openCypher `explain` 特征提供的信息，您需要了解有关 [DFE 查询引擎](neptune-dfe-engine.md)工作原理的一些详细信息（DFE 是 Neptune 用来处理 openCypher 查询的引擎）。

DFE 引擎将每个查询转换到一个运算符管道中。从第一个运算符开始，中间解将通过这个运算符管道从一个运算符流到下一个运算符。Explain 表中的每一行表示一个结果（截至到评估点）。

可以出现在 DFE 查询计划中的运算符如下所示：

**DFEApply**— 对存储在指定变量中的值执行参数部分中指定的函数

**DFEBind关系**-将具有指定名称的变量绑定在一起

**DFEChunkLocalSubQuery**— 这是一种非阻塞操作，充当正在执行的子查询的封装器。

**DFEDistinct列**-根据指定的变量返回输入值的不同子集。

**DFEDistinct关系**-根据指定的变量返回输入解决方案的不同子集。

**DFEDrain**— 出现在子查询的末尾，用作该子查询的终止步骤。解的数量记录为 `Units In`。`Units Out` 始终为零。

**DFEForward值** — 将所有输入块直接复制为输出块，以传递给其下游运算符。

**DFEGroupByHashIndex**— 根据先前计算的哈希索引（使用运算）对输入解执行分组`DFEHashIndexBuild`运算。作为输出，给定输入通过包含每个输入解的组键的列进行扩展。

**DFEHashIndexBuild**— 在一组变量上建立哈希索引作为副作用。此哈希索引通常会在以后的操作中重复使用。有关此哈希索引可能用在何处，请参阅 `DFEHashIndexJoin` 或 `DFEGroupByHashIndex`。

**DFEHashIndexJoin**— 根据先前构建的哈希索引对传入的解决方案执行联接。有关此哈希索引可能在何处构建，请参阅 `DFEHashIndexBuild`。

DFEJoinE **x** ists — 采用左手和右手输入关系，并保留左侧关系中具有相应值的值，这些值由给定联接变量定义。

**** - 这是一种非阻塞操作，它充当子查询的包装器，允许重复运行子查询以在循环中使用。

**DFEMergeChunks** — 这是一种阻塞操作，它将来自其上游运算符的区块组合成单个解决方案块，然后传递给其下游运算符（相反）。`DFESplitChunks`

**DFEMinus**— 采用左手和右手输入关系，并保留左侧关系中在给定连接变量定义的右关系中没有相应值的值。如果两个关系中的变量没有重叠，则此运算符只返回左侧输入关系。

DFENotE **x** ists — 采用左手和右手输入关系，并保留左侧关系中在给定联接变量定义的右关系中没有相应值的值。如果两个关系中的变量没有重叠，则此运算符会返回空关系。

**DFEOptional联接**-执行左外联接（也称为可选联接）：左侧的解决方案如果右侧至少有一个加入伙伴，则按原样转发来自左侧的解决方案，而右侧没有加入伙伴的解决方案则按原样转发。这是一项阻止操作。

DFEPipelineJo **in** — 根据`pattern`参数定义的元组模式连接输入。

**DFEPipelineRangeCount**— 计算与给定模式匹配的解数，并返回包含计数值的单一单元解。

**DFEPipeline扫描**-扫描数据库中的给定`pattern`参数，无论是否对列使用给定过滤器。

**DFEProject**— 获取多个输入列并仅投影所需的列。

**DFEReduce**— 对指定变量执行指定的聚合函数。

DFERelationalJo **in** — 使用合并联接根据指定的模式键连接前一个运算符的输入。这是一项阻止操作。

**DFERouteChunks** — 从其单个传入边缘获取输入块，然后沿着其多个传出边缘路由这些块。

**DFESelect行**-此运算符有选择地从其左侧输入关系解中提取行，以转发到其下游运算符。根据运算符右侧输入关系中提供的行标识符选择的行。

**DFESerialize**— 将查询的最终结果序列化为 JSON 字符串序列化，将每个输入解决方案映射到相应的变量名。对于节点和边缘结果，这些结果将序列化为实体属性和元数据的映射。

**DFESort**— 获取输入关系并根据提供的排序键生成排序关系。

**DFESplitByGroup**— 将来自一个传入边的每个输入块拆分为较小的输出块，这些块对应于行组，行组由 IDs 来自另一个传入边的相应输入块的行标识。

**DFESplitChunks** — 将每个输入块拆分为较小的输出块（相反）。`DFEMergeChunks`

**DFEStreamingHashIndexBuild**— 直播版本的`DFEHashIndexBuild`.

**DFEStreamingGroupByHashIndex**— 直播版本的`DFEGroupByHashIndex`.

**DFESubquery**— 该运算符出现在所有计划的开头，它封装了计划中在 [DFE 引擎](neptune-dfe-engine.md)上运行的部分，这是 OpenCypher 的整个计划。

**DFESymmetricHashJoin**— 使用哈希联接根据指定的模式键连接前一个运算符的输入。这是一个非阻止操作。

**DFESync**— 此运算符是支持非阻塞计划的同步运算符。它从两个传入边缘获取解，然后将这些解转发到相应的下游边缘。出于同步目的，可以对其中一个边缘的输入进行内部缓冲。

**DFETee**— 这是一个分支运算符，它向多个运算符发送相同的解决方案集。

**DFETerm分辨率**-对其输入执行本地化或全局化操作，生成分别包含本地化或全球化标识符的列。

**** - 将输入列中的值列表作为单个元素展开到输出列。

**DFEUnion**— 采用两个或多个输入关系，并使用所需的输出架构生成这些关系的并集。

**SolutionInjection**— 显示在 explain 输出中的其他所有内容之前，在 Units Out 列中的值为 1。但是，它不起作用，实际上并没有向 DFE 引擎注入任何解。

**TermResolution**— 出现在计划末尾，将海王星引擎中的对象转换为 OpenCypher 对象。

## openCypher `explain` 输出中的列
<a name="access-graph-opencypher-explain-columns"></a>

Neptune 作为 openCypher explain 输出生成的查询计划信息包含每行具有一个运算符的表。此表包含以下各列：

**ID** – 计划中此运算符的数字 ID。

**Out \$11**（和 **Out \$12**）– 此运算符下游的运算符的 ID。最多可以有两个下游运算符。

**Name** – 此运算符的名称。

**Arguments** – 运算符的任何相关详细信息。这包括如输入架构、输出架构、模式（对于 `PipelineScan` 和 `PipelineJoin`）等。

**Mode** – 描述基本运算符行为的标签。此列一般为空白 (`-`)。一个例外是 `TermResolution`，其中模式可以是 `id2value_opencypher`，表示从 ID 到 openCypher 值的解。

**Units In** – 作为输入传递给该运算符的解的数量。没有上游运算符的运算符（例如 `DFEPipelineScan`、`SolutionInjections` 和未注入静态值的 `DFESubquery`）的值为零。

**Units Out** – 作为该运算符的输出而生成的解的数量。`DFEDrain` 是一种特殊情况，其中被排除的解的数量记录在 `Units In` 中，并且 `Units Out` 始终为零。

**Ratio** – `Units Out` 与 `Units In` 的比率。

**Time (ms)** – 此运算符消耗的 CPU 时间，以毫秒为单位。

## openCypher explain 输出的一个基本示例
<a name="access-graph-opencypher-explain-basic-example"></a>

以下是 openCypher `explain` 输出的基本示例。该查询是在航线数据集中针对机场代码为 `ATL` 的节点执行的单节点查找，它使用默认 ASCII 输出格式的 `details` 模式调用 `explain`：

```
curl -d "query=MATCH (n {code: 'ATL'}) RETURN n" -k https://localhost:8182/openCypher -d "explain=details"                                                                                                      ~
Query:
MATCH (n {code: 'ATL'}) RETURN n

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 1         │ 0.00  │ 4.00      ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?n]          │ id2value_opencypher │ 1        │ 1         │ 1.00  │ 2.00      ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan       │ pattern=Node(?n) with property 'code' as ?n_code2 and label 'ALL'                                            │ -    │ 0        │ 1         │ 0.00  │ 0.21      ║
║    │        │        │                       │ inlineFilters=[(?n_code2 IN ["ATL"^^xsd:string])]                                                            │      │          │           │       │           ║
║    │        │        │                       │ patternEstimate=1                                                                                            │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#9d84f97c-c3b0-459a-98d5-955a8726b159/graph_1 │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEProject            │ columns=[?n]                                                                                                 │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 1        │ 0         │ 0.00  │ 0.03      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#9d84f97c-c3b0-459a-98d5-955a8726b159/graph_1
╔════╤════════╤════════╤══════════════════════╤════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                  │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ outSchema=[?n, ?n_code2]                                   │ -    │ 0        │ 1         │ 0.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ 3      │ DFETee               │ -                                                          │ -    │ 1        │ 2         │ 2.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFEDistinctColumn    │ column=?n                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.20      ║
║    │        │        │                      │ ordered=false                                              │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 5      │ -      │ DFEHashIndexBuild    │ vars=[?n]                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEPipelineJoin      │ pattern=Node(?n) with property 'ALL' and label '?n_label1' │ -    │ 1        │ 1         │ 1.00  │ 0.25      ║
║    │        │        │                      │ patternEstimate=3506                                       │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ 7      │ DFESync              │ -                                                          │ -    │ 2        │ 2         │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 7  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 8  │ 9      │ -      │ DFEHashIndexJoin     │ -                                                          │ -    │ 2        │ 1         │ 0.50  │ 0.35      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 9  │ -      │ -      │ DFEDrain             │ -                                                          │ -    │ 1        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝
```

在顶层，`SolutionInjection` 出现在其它所有内容之前，其 Units Out 为 1。请注意，它实际上并没有注入任何解。您可以看到下一个运算符 `DFESubquery` 的 Units In 为 0。

在顶层的 `SolutionInjection` 之后是 `DFESubquery` 和 `TermResolution` 运算符。`DFESubquery` 封装查询执行计划中正在推送到 [DFE 引擎](neptune-dfe-engine.md)的部分（对于 openCypher 查询，整个查询计划由 DFE 执行）。查询计划中的所有运算符都嵌套在由 `DFESubquery` 引用的 `subQuery1` 内部。唯一的例外是`TermResolution`，它在内部实现 IDs 为完全序列化的 OpenCypher 对象。

所有下推到 DFE 引擎的运算符的名称都以 `DFE` 前缀开头。如上所述，整个 openCypher 查询计划由 DFE 执行，因此，除最后一个 `TermResolution` 运算符之外的所有运算符都以 `DFE` 开头。

在 `subQuery1` 内部，可以有零个或多个 `DFEChunkLocalSubQuery` 或 `DFELoopSubQuery` 运算符来封装在内存受限机制中执行的推送执行计划的一部分。这里的 `DFEChunkLocalSubQuery` 包含一个 `SolutionInjection`，它用作子查询的输入。要在输出中查找该子查询的表，请在 `Arguments` 列中搜索为 `DFEChunkLocalSubQuery` 或 `DFELoopSubQuery` 运算符指定的 `subQuery=graph URI`。

在 `subQuery1` 中，`ID` 为 0 的 `DFEPipelineScan` 扫描数据库以查找指定的 `pattern`。该模式在所有标签上扫描属性 `code` 保存为变量 `?n_code2` 的实体（可以通过将 `airport` 附加到 `n:airport` 来针对特定标签进行筛选）。`inlineFilters` 参数显示了针对等于 `ATL` 的 `code` 属性的筛选。

接下来，`DFEChunkLocalSubQuery` 运算符联接包含 `DFEPipelineJoin` 的子查询的中间结果。这可以确保 `?n` 实际上是一个节点，因为之前的 `DFEPipelineScan` 会扫描任何具有 `code` 属性的实体。

# 具有限制的关系查找的 `explain` 输出示例
<a name="access-graph-opencypher-explain-example-2"></a>

此查询查找两个类型为 `route` 的匿名节点之间的关系，最多返回 10。同样，`explain` 模式为 `details`，输出格式为默认 ASCII 格式。下面是 `explain` 输出：

此处，`DFEPipelineScan` 扫描从匿名节点 `?anon_node7` 开始并在另一个匿名节点 `?anon_node21` 处结束的边缘，关系类型另存为 `?p_type1`。对于 `?p_type1`（为 `el://route`）存在一个筛选条件（其中 `el` 代表边缘标签），它对应于查询字符串中的 `[p:route]`。

`DFEDrain` 收集限制为 10 的输出解，如其 `Arguments` 列所示。一旦达到限制或生成了所有解（以先发生者为准），`DFEDrain` 就会终止。

```
curl -d "query=MATCH ()-[p:route]->() RETURN p LIMIT 10" -k https://localhost:8182/openCypher -d "explain=details"                                                                                              ~
Query:
MATCH ()-[p:route]->() RETURN p LIMIT 10

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 10        │ 0.00  │ 5.00      ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?p]          │ id2value_opencypher │ 10       │ 10        │ 1.00  │ 1.00      ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═════════════════╤═══════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name            │ Arguments                                                 │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═════════════════╪═══════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan │ pattern=Edge((?anon_node7)-[?p:?p_type1]->(?anon_node21)) │ -    │ 0        │ 1000      │ 0.00  │ 0.66      ║
║    │        │        │                 │ inlineFilters=[(?p_type1 IN [<el://route>])]              │      │          │           │       │           ║
║    │        │        │                 │ patternEstimate=26219                                     │      │          │           │       │           ║
╟────┼────────┼────────┼─────────────────┼───────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEProject      │ columns=[?p]                                              │ -    │ 1000     │ 1000      │ 1.00  │ 0.14      ║
╟────┼────────┼────────┼─────────────────┼───────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ DFEDrain        │ limit=10                                                  │ -    │ 1000     │ 0         │ 0.00  │ 0.11      ║
╚════╧════════╧════════╧═════════════════╧═══════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝
```

# 值表达式函数的 `explain` 输出示例
<a name="access-graph-opencypher-explain-example-3"></a>

函数为：

```
MATCH (a) RETURN DISTINCT labels(a)
```

在下面的 `explain` 输出中，`DFEPipelineScan` (ID 0)扫描所有节点标签。这对应于 `MATCH (a`)。

`DFEChunkLocalSubquery` (ID 1) 汇总每个 `?a` 的 `?a` 的标签。这对应于 `labels(a)`。您可以通过 `DFEApply` 和 `DFEReduce` 看到这一点。

`BindRelation` (ID 2) 用于将列通用的 `?__gen_labelsOfa2` 重命名为 `?labels(a)`。

`DFEDistinctRelation` (ID 4) 仅检索不同的标签（多个 :airport 节点会给出重复的标签 (a): ["airport"]）。这对应于 `DISTINCT labels(a)`。

```
curl -d "query=MATCH (a) RETURN DISTINCT labels(a)" -k https://localhost:8182/openCypher -d "explain=details"                                                                                                    ~
Query:
MATCH (a) RETURN DISTINCT labels(a)

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 5         │ 0.00  │ 81.00     ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?labels(a)]  │ id2value_opencypher │ 5        │ 5         │ 1.00  │ 1.00      ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan       │ pattern=Node(?a) with property 'ALL' and label '?a_label1'                                                   │ -    │ 0        │ 3750      │ 0.00  │ 26.77     ║
║    │        │        │                       │ patternEstimate=3506                                                                                         │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#8b314f55-2cc7-456a-a48a-c76a0465cfab/graph_1 │ -    │ 3750     │ 3750      │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEBindRelation       │ inputVars=[?a, ?__gen_labelsOfa2, ?__gen_labelsOfa2]                                                         │ -    │ 3750     │ 3750      │ 1.00  │ 0.08      ║
║    │        │        │                       │ outputVars=[?a, ?__gen_labelsOfa2, ?labels(a)]                                                               │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ DFEProject            │ columns=[?labels(a)]                                                                                         │ -    │ 3750     │ 3750      │ 1.00  │ 0.05      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEDistinctRelation   │ -                                                                                                            │ -    │ 3750     │ 5         │ 0.00  │ 2.78      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 5        │ 0         │ 0.00  │ 0.03      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#8b314f55-2cc7-456a-a48a-c76a0465cfab/graph_1
╔════╤════════╤════════╤══════════════════════╤════════════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                  │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪════════════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ outSchema=[?a]                                             │ -        │ 0        │ 3750      │ 0.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ 3      │ DFETee               │ -                                                          │ -        │ 3750     │ 7500      │ 2.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFEProject           │ columns=[?a]                                               │ -        │ 3750     │ 3750      │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 17     │ -      │ DFEOptionalJoin      │ -                                                          │ -        │ 7500     │ 3750      │ 0.50  │ 0.44      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEDistinctRelation  │ -                                                          │ -        │ 3750     │ 3750      │ 1.00  │ 2.23      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ -      │ DFEDistinctColumn    │ column=?a                                                  │ -        │ 3750     │ 3750      │ 1.00  │ 1.50      ║
║    │        │        │                      │ ordered=false                                              │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ 7      │ -      │ DFEPipelineJoin      │ pattern=Node(?a) with property 'ALL' and label '?a_label3' │ -        │ 3750     │ 3750      │ 1.00  │ 10.58     ║
║    │        │        │                      │ patternEstimate=3506                                       │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 7  │ 8      │ 9      │ DFETee               │ -                                                          │ -        │ 3750     │ 7500      │ 2.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 8  │ 10     │ -      │ DFEBindRelation      │ inputVars=[?a_label3]                                      │ -        │ 3750     │ 3750      │ 1.00  │ 0.04      ║
║    │        │        │                      │ outputVars=[?100]                                          │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 9  │ 11     │ -      │ DFEBindRelation      │ inputVars=[?a, ?a_label3, ?100]                            │ -        │ 7500     │ 3750      │ 0.50  │ 0.07      ║
║    │        │        │                      │ outputVars=[?a, ?a_label3, ?100]                           │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 10 │ 9      │ -      │ DFETermResolution    │ column=?100                                                │ id2value │ 3750     │ 3750      │ 1.00  │ 7.60      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 11 │ 12     │ -      │ DFEBindRelation      │ inputVars=[?a, ?a_label3, ?100]                            │ -        │ 3750     │ 3750      │ 1.00  │ 0.06      ║
║    │        │        │                      │ outputVars=[?a, ?100, ?a_label3]                           │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 12 │ 13     │ -      │ DFEApply             │ functor=nodeLabel(?a_label3)                               │ -        │ 3750     │ 3750      │ 1.00  │ 0.55      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 13 │ 14     │ -      │ DFEProject           │ columns=[?a, ?a_label3_alias4]                             │ -        │ 3750     │ 3750      │ 1.00  │ 0.05      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 14 │ 15     │ -      │ DFEMergeChunks       │ -                                                          │ -        │ 3750     │ 3750      │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 15 │ 16     │ -      │ DFEReduce            │ functor=collect(?a_label3_alias4)                          │ -        │ 3750     │ 3750      │ 1.00  │ 6.37      ║
║    │        │        │                      │ segmentationKey=[?a]                                       │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 16 │ 3      │ -      │ DFEMergeChunks       │ -                                                          │ -        │ 3750     │ 3750      │ 1.00  │ 0.03      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 17 │ -      │ -      │ DFEDrain             │ -                                                          │ -        │ 3750     │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧════════════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

# 数学值表达式函数的 `explain` 输出示例
<a name="access-graph-opencypher-explain-example-4"></a>

在此示例中，`RETURN abs(-10)` 使用常量 `-10` 的绝对值执行简单求值。

`DFEChunkLocalSubQuery` (ID 1) 对存储在变量 `?100` 中的静态值 `-10` 执行解注入。

`DFEApply` (ID 2) 是对存储在变量 `?100` 中的静态值执行绝对值函数 `abs()` 的运算符。

以下是查询和生成的 `explain` 输出：

```
curl -d "query=RETURN abs(-10)" -k https://localhost:8182/openCypher  -d "explain=details"                                                                                                                       ~
Query:
RETURN abs(-10)

╔════╤════════╤════════╤═══════════════════╤═══════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments             │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪═══════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]        │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1    │ -                   │ 0        │ 1         │ 0.00  │ 4.00      ║
╟────┼────────┼────────┼───────────────────┼───────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?_internalVar1] │ id2value_opencypher │ 1        │ 1         │ 1.00  │ 1.00      ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection  │ outSchema=[]                                                                                                 │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#c4cc6148-cce3-4561-93c0-deb91f257356/graph_1 │ -    │ 1        │ 1         │ 1.00  │ 0.03      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEApply              │ functor=abs(?100)                                                                                            │ -    │ 1        │ 1         │ 1.00  │ 0.26      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ DFEBindRelation       │ inputVars=[?_internalVar2, ?_internalVar2]                                                                   │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
║    │        │        │                       │ outputVars=[?_internalVar2, ?_internalVar1]                                                                  │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEProject            │ columns=[?_internalVar1]                                                                                     │ -    │ 1        │ 1         │ 1.00  │ 0.06      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 1        │ 0         │ 0.00  │ 0.05      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝

subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#c4cc6148-cce3-4561-93c0-deb91f257356/graph_1
╔════╤════════╤════════╤══════════════════════╤═════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                           │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪═════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ solutions=[?100 -> [-10^^<LONG>]]   │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
║    │        │        │                      │ outSchema=[?100]                    │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 3      │ -      │ DFERelationalJoin    │ joinVars=[]                         │ -    │ 2        │ 1         │ 0.50  │ 0.18      ║
╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 1      │ -      │ DFESolutionInjection │ outSchema=[]                        │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ -      │ -      │ DFEDrain             │ -                                   │ -    │ 1        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧═════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝
```

# 可变长度路径 (VLP) 查询的 `explain` 输出示例
<a name="access-graph-opencypher-explain-example-5"></a>

这是处理可变长度路径查询的更复杂的查询计划的示例。为清楚起见，此示例仅显示部分 `explain` 输出。

在 `subQuery1` 中，`DFEPipelineScan` (ID 0) 和 `DFEChunkLocalSubQuery` (ID 1) 注入 `...graph_1` 子查询，负责使用 `YPO` 代码扫描节点。

在 `subQuery1` 中，`DFEChunkLocalSubQuery` (ID 2) 注入 `...graph_2` 子查询，负责使用 `LAX` 代码扫描节点。

在 `subQuery1` 中，`DFEChunkLocalSubQuery` (ID 3) 注入 `...graph3` 子查询，该子查询包含 `DFELoopSubQuery` (ID 17)，它转而注入 `...graph5` 子查询。此操作负责求解两个节点之间查询字符串中的 `-[*2]->` 可变长度模式。

```
curl -d "query=MATCH p=(a {code: 'YPO'})-[*2]->(b{code: 'LAX'}) return p" -k https://localhost:8182/openCypher -d "explain=details"                                                                                                                                                                                ~
Query:
MATCH p=(a {code: 'YPO'})-[*2]->(b{code: 'LAX'}) return p

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 0         │ 0.00  │ 84.00     ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?p]          │ id2value_opencypher │ 0        │ 0         │ 0.00  │ 0         ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan       │ pattern=Node(?a) with property 'code' as ?a_code7 and label 'ALL'                                            │ -    │ 0        │ 1         │ 0.00  │ 0.68      ║
║    │        │        │                       │ inlineFilters=[(?a_code7 IN ["YPO"^^xsd:string])]                                                            │      │          │           │       │           ║
║    │        │        │                       │ patternEstimate=1                                                                                            │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_1 │ -    │ 1        │ 1         │ 1.00  │ 0.03      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_2 │ -    │ 1        │ 1         │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_3 │ -    │ 1        │ 0         │ 0.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEBindRelation       │ inputVars=[?__gen_path6, ?anon_rel26, ?b_code8, ?b, ?a_code7, ?a, ?__gen_path6]                              │ -    │ 0        │ 0         │ 0.00  │ 0.10      ║
║    │        │        │                       │ outputVars=[?__gen_path6, ?anon_rel26, ?b_code8, ?b, ?a_code7, ?a, ?p]                                       │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ -      │ DFEProject            │ columns=[?p]                                                                                                 │ -    │ 0        │ 0         │ 0.00  │ 0.05      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 0        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_1
╔════╤════════╤════════╤══════════════════════╤════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                  │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ outSchema=[?a, ?a_code7]                                   │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ 3      │ DFETee               │ -                                                          │ -    │ 1        │ 2         │ 2.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFEDistinctColumn    │ column=?a                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.25      ║
║    │        │        │                      │ ordered=false                                              │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 5      │ -      │ DFEHashIndexBuild    │ vars=[?a]                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.05      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEPipelineJoin      │ pattern=Node(?a) with property 'ALL' and label '?a_label1' │ -    │ 1        │ 1         │ 1.00  │ 0.47      ║
║    │        │        │                      │ patternEstimate=3506                                       │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ 7      │ DFESync              │ -                                                          │ -    │ 2        │ 2         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 7  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 8  │ 9      │ -      │ DFEHashIndexJoin     │ -                                                          │ -    │ 2        │ 1         │ 0.50  │ 0.26      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 9  │ -      │ -      │ DFEDrain             │ -                                                          │ -    │ 1        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_2
╔════╤════════╤════════╤══════════════════════╤═══════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                         │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪═══════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan      │ pattern=Node(?b) with property 'code' as ?b_code8 and label 'ALL' │ -    │ 0        │ 1         │ 0.00  │ 0.38      ║
║    │        │        │                      │ inlineFilters=[(?b_code8 IN ["LAX"^^xsd:string])]                 │      │          │           │       │           ║
║    │        │        │                      │ patternEstimate=1                                                 │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEMergeChunks       │ -                                                                 │ -    │ 1        │ 1         │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFERelationalJoin    │ joinVars=[]                                                       │ -    │ 2        │ 1         │ 0.50  │ 0.19      ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 2      │ -      │ DFESolutionInjection │ outSchema=[?a, ?a_code7]                                          │ -    │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ -      │ -      │ DFEDrain             │ -                                                                 │ -    │ 1        │ 0         │ 0.00  │ 0.01      ║
╚════╧════════╧════════╧══════════════════════╧═══════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_3
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
...
║ 17 │ 18     │ -      │ DFELoopSubQuery       │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_5 │ -        │ 1        │ 2         │ 2.00  │ 0.31      ║
...
```

# Neptune openCypher 中的事务
<a name="access-graph-opencypher-transactions"></a>

Amazon Neptune 中的 openCypher 实现使用 [Neptune 定义的事务语义](transactions-neptune.md)。但是，如以下各节所述，Bolt 驱动程序提供的隔离级别对 Bolt 事务语义有一些特定的影响。

## 只读 Bolt 事务查询
<a name="access-graph-opencypher-transactions-ro"></a>

可以处理只读查询的方式多种多样，并可使用不同的事务模型和隔离级别，如下所示：

### 隐式只读事务查询
<a name="access-graph-opencypher-transactions-ro-implicit"></a>

以下是只读隐式事务的示例：

```
public void executeReadImplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // read query
  final String READ_QUERY = "MATCH (n) RETURN n limit 10";

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
          Config.builder().withEncryption()
                          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                          .build());

  // create the session config
  SessionConfig sessionConfig = SessionConfig.builder()
                                             .withFetchSize(1000)
                                             .withDefaultAccessMode(AccessMode.READ)
                                             .build();

  // run the query as access mode read
  driver.session(sessionConfig).readTransaction(new TransactionWork<String>()
    {
      final StringBuilder resultCollector = new StringBuilder();

      @Override
      public String execute(final Transaction tx)
      {
        // execute the query
        Result queryResult = tx.run(READ_QUERY);

        // Read the result
        for (Record record : queryResult.list())
        {
          for (String key : record.keys())
          {
            resultCollector.append(key)
                           .append(":")
                           .append(record.get(key).asNode().toString());
          }
        }
        return resultCollector.toString();
      }

    }
  );

  // close the driver.
  driver.close();
}
```

由于只读副本仅接受只读查询，因此，无论在会话配置中设置的访问模式如何，所有针对只读副本的查询都将作为读取隐式事务执行。Neptune 在 `SNAPSHOT` 隔离语义下将读取隐式事务计算为[只读查询](transactions-neptune.md#transactions-neptune-read-only)。

如果失败，默认情况下会重试读取隐式事务。

### 自动提交只读事务查询
<a name="access-graph-opencypher-transactions-ro-autocommit"></a>

以下是只读自动提交事务的示例：

```
public void executeAutoCommitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // read query
  final String READ_QUERY = "MATCH (n) RETURN n limit 10";

  // Create the session config.
  final SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.READ)
    .build();

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // result collector
  final StringBuilder resultCollector = new StringBuilder();

  // create a session
  final Session session = driver.session(sessionConfig);

  // run the query
  final Result queryResult = session.run(READ_QUERY);
  for (final Record record : queryResult.list())
  {
    for (String key : record.keys())
    {
      resultCollector.append(key)
                     .append(":")
                     .append(record.get(key).asNode().toString());
    }
  }

  // close the session
  session.close();

  // close the driver
  driver.close();
}
```

如果在会话配置中将访问模式设置为 `READ`，则 Neptune 会在 `SNAPSHOT` 隔离语义下将自动提交事务查询计算为[只读查询](transactions-neptune.md#transactions-neptune-read-only)。请注意，只读副本仅接受只读查询。

如果您未传入会话配置，则默认情况下会使用突变查询隔离来处理自动提交查询，因此传入将访问模式显式设置为 `READ` 的会话配置非常重要。

如果失败，则不会重试只读自动提交查询。

### 显式只读事务查询
<a name="access-graph-opencypher-transactions-ro-explicit"></a>

以下是显式只读事务的示例：

```
public void executeReadExplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // read query
  final String READ_QUERY = "MATCH (n) RETURN n limit 10";

  // Create the session config.
  final SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.READ)
    .build();

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // result collector
  final StringBuilder resultCollector = new StringBuilder();

  // create a session
  final Session session = driver.session(sessionConfig);

  // begin transaction
  final Transaction tx = session.beginTransaction();

  // run the query on transaction
  final List<Record> list = tx.run(READ_QUERY).list();

  // read the result
  for (final Record record : list)
  {
    for (String key : record.keys())
    {
      resultCollector
        .append(key)
        .append(":")
        .append(record.get(key).asNode().toString());
    }
  }

  // commit the transaction and for rollback we can use beginTransaction.rollback();
  tx.commit();

  // close the driver
  driver.close();
}
```

如果在会话配置中将访问模式设置为 `READ`，则 Neptune 会在 `SNAPSHOT` 隔离语义下将显式只读事务计算为[只读查询](transactions-neptune.md#transactions-neptune-read-only)。请注意，只读副本仅接受只读查询。

如果您未传入会话配置，则默认情况下会使用突变查询隔离来处理显式只读事务，因此传入将访问模式显式设置为 `READ` 的会话配置非常重要。

如果失败，默认情况下会重试只读显式查询。

## 突变 Bolt 事务查询
<a name="access-graph-opencypher-transactions-wr"></a>

与只读查询一样，可以通过多种方式处理突变查询，并可使用不同的事务模型和隔离级别，如下所示：

### 隐式突变事务查询
<a name="access-graph-opencypher-transactions-wr-implicit"></a>

以下是隐式突变事务的示例：

```
public void executeWriteImplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // create node with label as label and properties.
  final String WRITE_QUERY = "CREATE (n:label {name : 'foo'})";

  // Read the vertex created with label as label.
  final String READ_QUERY = "MATCH (n:label) RETURN n";

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // create the session config
  SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.WRITE)
    .build();

  final StringBuilder resultCollector = new StringBuilder();

  // run the query as access mode write
  driver.session(sessionConfig).writeTransaction(new TransactionWork<String>()
  {
    @Override
    public String execute(final Transaction tx)
    {
      // execute the write query and consume the result.
      tx.run(WRITE_QUERY).consume();

      // read the vertex written in the same transaction
      final List<Record> list = tx.run(READ_QUERY).list();

      // read the result
      for (final Record record : list)
      {
        for (String key : record.keys())
        {
          resultCollector
            .append(key)
            .append(":")
            .append(record.get(key).asNode().toString());
        }
      }
      return resultCollector.toString();
    }
  }); // at the end, the transaction is automatically committed.

  // close the driver.
  driver.close();
}
```

作为突变查询一部分进行的读取是在 `READ COMMITTED` 隔离下执行的，对 [Neptune 突变事务](transactions-neptune.md#transactions-neptune-mutation)具有通常的保证。

无论您是否专门传入会话配置，该事务始终被视为写入事务。

有关冲突，请参阅[使用锁定等待超时解决冲突](transactions-neptune.md#transactions-neptune-conflicts)。

### 自动提交突变事务查询
<a name="access-graph-opencypher-transactions-wr-autocommit"></a>

突变自动提交查询继承与突变隐式事务相同的行为。

如果未传入会话配置，默认情况下，该事务将被视为写入事务。

如果失败，则不会自动重试突变自动提交查询。

### 显式突变事务查询
<a name="access-graph-opencypher-transactions-wr-explicit"></a>

以下是显式突变事务的示例：

```
public void executeWriteExplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // create node with label as label and properties.
  final String WRITE_QUERY = "CREATE (n:label {name : 'foo'})";

  // Read the vertex created with label as label.
  final String READ_QUERY = "MATCH (n:label) RETURN n";

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // create the session config
  SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.WRITE)
    .build();

  final StringBuilder resultCollector = new StringBuilder();

  final Session session = driver.session(sessionConfig);

  // run the query as access mode write
  final Transaction tx = driver.session(sessionConfig).beginTransaction();

  // execute the write query and consume the result.
  tx.run(WRITE_QUERY).consume();

  // read the result from the previous write query in a same transaction.
  final List<Record> list = tx.run(READ_QUERY).list();

  // read the result
  for (final Record record : list)
  {
    for (String key : record.keys())
    {
      resultCollector
        .append(key)
        .append(":")
        .append(record.get(key).asNode().toString());
    }
  }

  // commit the transaction and for rollback we can use tx.rollback();
  tx.commit();

  // close the session
  session.close();

  // close the driver.
  driver.close();
}
```

显式突变查询继承与隐式突变事务相同的行为。

如果未传入会话配置，默认情况下，该事务将被视为写入事务。

有关冲突，请参阅[使用锁定等待超时解决冲突](transactions-neptune.md#transactions-neptune-conflicts)。

# openCypher 查询提示
<a name="opencypher-query-hints"></a>

**重要**  
 openCypher 查询提示仅在引擎发行版本 [1.3.2.0](https://docs.aws.amazon.com//neptune/latest/userguide/engine-releases-1.3.2.0.html) 及更高版本中可用。

 在 Amazon Neptune 中，您可以使用 `USING` 子句为 openCypher 查询指定查询提示。这些提示可让您控制优化和评估策略。

 查询提示的语法是：

```
USING {scope}:{hint} {value}
```

1.  `{scope}` 定义了提示适用于 `Query` 或 `Clause` 的范围。

    `Query` 的范围值表示查询提示适用于整个查询（查询级别）。

    `Clause` 的范围值表示查询提示适用于提示之前的子句（子句级别）。

1.  `{hint}` 是要应用的查询提示的名称。

1.  `{value}` 是 `{hint}` 的参数。

 这些值可以区分大小写。

 例如，要为查询启用查询计划缓存，请运行以下命令：

```
Using QUERY:PLANCACHE "enabled" 
MATCH (a:Person {firstName: "Erin", lastName: $lastName})
 RETURN a
```

**注意**  
 **目前，支持**查询范围查询**提示 **PLANCACHE、TIMEOUTMILL** **ISECO** NDS 和 Types。assumeConsistentData**支持的查询提示如下所示。

**Topics**
+ [

# openCypher 查询计划缓存提示
](opencypher-query-hints-qpc-hint.md)
+ [

# AssumeConsistentDataTypes 提示
](opencypher-query-hints-AssumeConsistentDataTypes.md)
+ [

# OpenCypher 查询超时提示
](opencypher-query-hints-timeout-hint.md)

# openCypher 查询计划缓存提示
<a name="opencypher-query-hints-qpc-hint"></a>

 查询级别的查询提示 `QUERY:PLANCACHE` 可以在每个查询（无论是否参数化）的基础上覆盖查询计划缓存行为。它需要与 `USING` 子句一起使用。该查询提示接受 `enabled` 或 `disabled` 作为值。有关查询计划缓存的更多信息，请参阅 [Amazon Neptune 中的查询计划缓存](access-graph-qpc.md)。

```
# Forcing plan to be cached or reused
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" MATCH(n) RETURN n LIMIT 1"
  
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
  -d "parameters={\"arg\": 123}"
  
# Forcing plan to be neither cached nor reused
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"disabled\" MATCH(n) RETURN n LIMIT 1"
```

# AssumeConsistentDataTypes 提示
<a name="opencypher-query-hints-AssumeConsistentDataTypes"></a>

 openCypher 遵循一种范式，即数值数据类型（例如 int、byte、short、long 等）的匹配是在类型提升语义下进行的。例如，在类型提升语义下，查找所有输入值为 10 且类型为 short 的属性时，它还会匹配值为 10 且类型为 long 的属性。在某些情况下，类型转换可能会产生开销，并导致查询计划的效率低于未执行类型转换时的效率。尤其是在数据中使用一致的数据类型时（例如，所有人的年龄都存储为 long 值时），在不影响查询结果的情况下进行类型提升会产生开销。

 为了在已知存储在数据库中的数值属性数据值类型一致的情况下进行优化，可以使用名为 `assumeConsistentDataTypes`（包含值 `true/false`，默认值为 `false`）的查询提示。当此查询提示的值为 `true` 时，引擎会假设唯一的属性值始终为 long 或 double，并将跳过类型提升语义。在查询中指定的数值被视为 long 值（对于非浮点值）和 double 值（对于浮点值）。

 如果数据始终使用单一数据类型（例如，所有年龄都存储为 `long`），则使用 `assumeConsistentDataTypes` 提示可以跳过对不同数值类型进行等效性检查的不必要步骤，从而优化查询。但是，如果同一属性的数据的数据类型不一致，则使用该提示可能会导致某些结果丢失，因为查询只会匹配提示假设的单个数据类型。

```
# Database loaded with following openCypher CSV's

# File 1
:ID,age:Int
n1,20
n2,25

# File 2
:ID,age:Long
n3,25


# Example (no hint)
MATCH (n:Person) 
WHERE n.age >= 25
RETURN n

# Result
n2
n3

Returns all person whose age is >= 25 and the values >= 25 can be with any of these datatypes
i.e. byte, short, int, long, double or float

-----------------------------------------------------------------------------------

# Example (with hint present)
USING QUERY:assumeConsistentDataTypes "true"
MATCH (n:Person)
WHERE n.age >= 25
RETURN n

# Result
n3

Returns only "n3" and not "n2". The reason is that even though the numerical value
matches (25), the datatype is "int" and is considered a non-match.
```

 也可以通过解释来验证差异。

 没有解释：

```
# Query
MATCH (n)
WHERE n.age = 20
RETURN n

# Explain Snippet
╔═════╤══════════╤══════════╤══════════════════════════════╤═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╤════════╤════════════╤══════════════╤═════════╤══════════════╗
║ ID │ Out #1 │ Out #2 │ Name                   │ Arguments                                                                                                                            │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠═════╪══════════╪══════════╪══════════════════════════════╪═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╪════════╪════════════╪══════════════╪═════════╪══════════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan (DFX)  │ pattern=Node(?n) with property 'age' as ?n_age2 and label 'ALL'                                                                      │ -    │ 0        │ 1         │ 0.00  │ 0.10      ║
║    │        │        │                        │ inlineFilters=[(?n_age2 IN ["20"^^xsd:byte, "20"^^xsd:int, "20"^^xsd:long, "20"^^xsd:short, "20.0"^^xsd:double, "20.0"^^xsd:float])] │      │          │           │       │           ║
║    │        │        │                        │ patternEstimate=1                                                                                                                    │      │          │           │       │           ║
╟─────┼──────────┼──────────┼──────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼────────────┼──────────────┼─────────┼──────────────╢

# The inFilters field contains all numeric types
```

 有提示：

```
# Query
MATCH (n)
WHERE n.age = 20
RETURN n

# Explain Snippet
╔═════╤══════════╤══════════╤══════════════════════════════╤═════════════════════════════════════════════════════════════════════════════════╤════════╤════════════╤══════════════╤═════════╤══════════════╗
║ ID │ Out #1 │ Out #2 │ Name                   │ Arguments                                                       │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠═════╪══════════╪══════════╪══════════════════════════════╪═════════════════════════════════════════════════════════════════════════════════╪════════╪════════════╪══════════════╪═════════╪══════════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan (DFX)  │ pattern=Node(?n) with property 'age' as ?n_age2 and label 'ALL' │ -    │ 0        │ 1         │ 0.00  │ 0.07      ║
║    │        │        │                        │ inlineFilters=[(?n_age2 IN ["20"^^xsd:long])]                   │      │          │           │       │           ║
║    │        │        │                        │ patternEstimate=1                                               │      │          │           │       │           ║
╟─────┼──────────┼──────────┼──────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼────────┼────────────┼──────────────┼─────────┼──────────────╢

# The inFilters field only contains long datatype
```

# OpenCypher 查询超时提示
<a name="opencypher-query-hints-timeout-hint"></a>

 可以通过查询级别的查询提示在每个查询的基础上配置查询超时行为。`QUERY:TIMEOUTMILLISECONDS`它必须与`USING`子句一起使用。查询提示接受非负长整数作为值。

```
# Using query-level timeout hint 

% curl https://<endpoint>:<port>/opencypher \
  -d "query=USING QUERY:TIMEOUTMILLISECONDS 100 MATCH(n) RETURN n LIMIT 1"
```

 查询超时行为将考虑集群级超时和查询级超时的最小值。请查看以下示例以了解查询超时行为。[有关集群级查询超时的更多信息，请参阅 neptune\$1query\$1timeout。](https://docs.aws.amazon.com/neptune/latest/userguide/parameters.html#parameters-db-cluster-parameters-neptune_query_timeout)

```
# Suppose `neptune_query_timeout` is 10000 ms and query-level timeout is set to 100 ms
# It will consider 100 ms as the final timeout 

% curl https://<endpoint>:<port>/opencypher \
  -d "query=USING QUERY:TIMEOUTMILLISECONDS 100 MATCH(n) RETURN n LIMIT 1"

# Suppose `neptune_query_timeout` is 100 ms and query-level timeout is set to 10000 ms
# It will still consider 100 ms as the final timeout 

% curl https://<endpoint>:<port>/opencypher \
  -d "query=USING QUERY:TIMEOUTMILLISECONDS 10000 MATCH(n) RETURN n LIMIT 1"
```

# Neptune openCypher 限制
<a name="access-graph-opencypher-limitations"></a>

Amazon Neptune 版本的 openCypher 仍然不支持[密码查询语言参考版本 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf) 中规定的所有内容，如[openCypher 规范合规性](feature-opencypher-compliance.md)中所述。预计未来的版本将解决其中的许多限制。

# Neptune openCypher 异常
<a name="access-graph-opencypher-exceptions"></a>

在 Amazon Neptune 上使用 openCypher 时，可能会出现各种异常。以下是您可能收到的来自 HTTPS 端点或 Bolt 驱动程序的常见异常（Bolt 驱动程序中的所有异常都报告为服务器状态异常）：


| HTTP 代码 | 错误消息 | 是否可检索？ | 补救措施 | 
| --- | --- | --- | --- | 
| 400 | *（语法错误，直接从 openCypher 解析器传播）* | 否 | 请更正查询语法，然后重试。 | 
| 500 | `Operation terminated (out of memory)` | 是 | 重新处理查询以添加其它筛选条件来减少所需的内存 | 
| 500 | 操作已终止（超过截止日期） | 是 | 增加数据库集群参数组中的查询超时，或者[重试请求](https://docs.aws.amazon.com/general/latest/gr/api-retries.html)。 | 
| 500 | 操作已终止（被用户取消） | 是 | 重试请求。 | 
| 500 | 数据库重置正在进行中。请在集群可用后重试查询。 | 是 | 重置完成后重试。 | 
| 500 | 由于并发操作冲突，操作失败（请重试）。事务目前正在回滚。 | 是 | 使用[指数回退和重试策略](best-practices-opencypher-retry-logic.md)重试。 | 
| 400 | *(operation name)* operation/feature 不支持的异常 | 否 | 不支持指定的操作。 | 
| 400 | 已尝试在只读副本上更新 openCypher | 否 | 将目标端点更改为写入器端点。 | 
| 400 | MalformedQueryException （Neptune 不显示内部解析器状态） | 否 | 请更正查询语法并重试。 | 
| 400 | 无法删除节点，因为它仍然具有关系。要删除此节点，必须先删除其关系。 | 否 | 不使用 `MATCH (n) DELETE n`，而是使用 `MATCH(n) DETACH DELETE(n)` | 
| 400 | 操作无效：正在尝试移除节点的最后一个标签。节点必须具有至少一个标签。 | 否 | Neptune 要求所有节点至少有一个标签，如果创建的节点没有显式标签，则会分配默认标签 `vertex`。更改查询 and/or 应用程序逻辑，以免删除最后一个标签。可以通过设置新标签然后删除旧标签，更新节点的单例标签。 | 
| 500 | 已违规请求的最大数量， ConfiguredQueueCapacity= \$1\$1 for connID = \$1\$1 | 是 | 目前，无论堆栈和协议如何，只能处理 8192 个并发请求。 | 
| 500 | 突破最大连接限制。 | 是 | 每个实例只允许 1000 个并发 Bolt 连接（对于 HTTP 没有限制）。 | 
| 400 | 应为 [节点、关系或路径之一]，而得到的是文本 | 否 | 请检查您传递的参数是否正确，查询语法是否正确，然后重试。 | 
| 400 | 属性值必须是简单的文本。或：需要 Set 属性的映射，但找不到。 | 否 | SET 子句只接受简单文本，不接受复合类型。 | 
| 400 | 找不到已传递的供删除的实体 | 否 | 检查数据库中是否存在您尝试删除的实体。 | 
| 400 | 用户无权访问数据库。 | 否 | 查看有关正在使用的 IAM 角色的策略。 | 
| 400 | 没有作为请求的一部分传递的令牌 | 否 | 在启用 IAM 的集群上，必须将经过正确签名的令牌作为查询请求的一部分传递。 | 
| 400 | 错误消息已传播。 | 否 | 使用请求编号与 Support 联系 AWS 。 | 
| 500 | 操作已终止（内部错误） | 是 | 使用请求编号与 Support 联系 AWS 。 | 

# Amazon Neptune 中的 openCypher 扩展
<a name="access-graph-opencypher-extensions"></a>

 Amazon Neptune 支持 openCypher 规范参考版本 9。有关详细信息，请参阅 Amazon Neptune 中的 [Amazon Neptune 中的 openCypher 规范合规性](feature-opencypher-compliance.md)。此外，Amazon Neptune 支持此处列出的功能。除非提及特定版本，否则这些功能可在 Neptune 数据库和 Neptune Analytics 中使用。

## 查询时 S3 数据访问权限
<a name="opencypher-compliance-neptune-read"></a>

在 Neptune 数据库 1.4.7.0 及更高版本中可用。

Neptune 支持在 OpenCypher 查询中直接从亚马逊 S3 读取 CSV 或 Parquet 数据的`neptune.read()`功能。与在查询之前导入数据的批量加载器不同，它在执行查询时`neptune.read()`访问 Amazon S3 数据。

有关完整的文档，请参阅[neptune.read ()](access-graph-opencypher-21-extensions-s3-read.md)。

## Neptune 特定的 `join()` 函数
<a name="opencypher-compliance-join-function"></a>

在 Neptune 数据库和 Neptune Analytics 中可用。

Neptune 实现了一个在 openCypher 规范中不存在的 `join()` 函数。它根据字符串文本列表和字符串分隔符创建字符串文本。此函数采用两个参数：
+ 第一个参数是字符串文本列表。
+ 第二个参数是分隔符字符串，可以包含零个、一个或多个字符。

示例：

```
join(["abc", "def", "ghi"], ", ")    // Returns "abc, def, ghi"
```

## Neptune 特定的 `removeKeyFromMap()` 函数
<a name="opencypher-compliance-removeKeyFromMap-function"></a>

在 Neptune 数据库和 Neptune Analytics 中可用。

Neptune 实现了一个在 openCypher 规范中不存在的 `removeKeyFromMap()` 函数。它从映射中移除指定的键并返回生成的新映射。

此函数采用两个参数：
+ 第一个参数是从中移除键的映射。
+ 第二个参数是从映射中移除的键。

当您想通过展开映射列表来设置节点或关系的值时，`removeKeyFromMap()` 函数特别有用。例如：

```
UNWIND [{`~id`: 'id1', name: 'john'}, {`~id`: 'id2', name: 'jim'}] as val
CREATE (n {`~id`: val.`~id`})
SET n = removeKeyFromMap(val, '~id')
```

## 节点和关系属性的自定义 ID 值
<a name="opencypher-compliance-custom-ids"></a>

在 Neptune 数据库 1.2.0.2 及更高版本和 Neptune Analytics 中可用。

从[引擎版本 1.2.0.2](engine-releases-1.2.0.2.md) 开始，Neptune 扩展了 openCypher 规范，以便您现在可以在 `CREATE`、`MERGE` 和 `MATCH` 子句中为节点和关系指定 `id` 值。这使您可以分配用户友好的字符串，而不是系统生成的字符串 UUIDs ，以识别节点和关系。

在 Neptune Analytics 中，自定义 ID 值不适用于边缘。

**警告**  
openCypher 规范的这一扩展不向后兼容，因为 `~id` 现在被视为保留的属性名称。如果您已经在数据和查询中将 `~id` 用作属性，则需要将现有属性迁移到新的属性键并移除旧的属性键。请参阅[如果您目前正在将 `~id` 用作属性，该怎么办](#opencypher-compliance-custom-ids-migrating)。

以下示例展示了如何创建具有自定义 ID 的节点和关系：

```
CREATE (n {`~id`: 'fromNode', name: 'john'})
  -[:knows {`~id`: 'john-knows->jim', since: 2020}]
  ->(m {`~id`: 'toNode', name: 'jim'})
```

如果您尝试创建已在使用的自定义 ID，Neptune 会引发 `DuplicateDataException` 错误。

以下是在 `MATCH` 子句中使用一个自定义 ID 的示例：

```
MATCH (n {`~id`: 'id1'})
RETURN n
```

以下是在子`MERGE`句中使用 custom IDs 的示例：

```
MATCH (n {name: 'john'}), (m {name: 'jim'})
MERGE (n)-[r {`~id`: 'john->jim'}]->(m)
RETURN r
```

### 如果您目前正在将 `~id` 用作属性，该怎么办
<a name="opencypher-compliance-custom-ids-migrating"></a>

在[引擎版本 1.2.0.2](engine-releases-1.2.0.2.md) 中，openCypher 子句中的 `~id` 键现在视为 `id` 而不是属性。这意味着，如果您有一个名为 `~id` 的属性，则无法对其进行访问。

如果您使用的是 `~id` 属性，那么在升级到引擎版本 `1.2.0.2` 或更高版本之前，您要做的就是先将现有 `~id` 属性迁移到新的属性键，然后移除 `~id` 属性。例如，下面的查询：
+ 为所有节点创建一个名为“newId”的新属性，
+ 将“\$1id”属性的值复制到“newId”属性中，
+ 并从数据中移除“\$1id”属性

```
MATCH (n)
WHERE exists(n.`~id`)
SET n.newId = n.`~id`
REMOVE n.`~id`
```

对于数据中具有 `~id` 属性的任何关系，都需要做同样的事情。

您还必须更改您正在使用的任何引用 `~id` 属性的查询。例如，此查询：

```
MATCH (n)
WHERE n.`~id` = 'some-value'
RETURN n
```

...会改成这样：

```
MATCH (n)
WHERE n.newId = 'some-value'
RETURN n
```

## 在 Neptune 中提供 CALL 子查询支持
<a name="call-subquery-support"></a>

 在 Neptune 数据库 1.4.1.0 及更高版本和 Neptune Analytics 中可用。

 Amazon Neptune 支持 `CALL` 子查询。`CALL` 子查询是主查询的一部分，对于 `CALL` 子查询的每个输入，它在隔离的范围中运行。

 例如，假设图形包含有关人员、他们的朋友和他们居住的城市的数据，我们可以使用 `CALL` 子查询来检索某人的每个朋友居住的两个最大的城市：

```
MATCH (person:Person)-[:knows]->(friend) 
CALL { 
  WITH friend 
  MATCH (friend)-[:lived_in]->(city) 
  RETURN city 
  ORDER BY city.population DESC
  LIMIT 2 
} 
RETURN person, friend, city
```

 在此示例中，`CALL { ... }` 中的查询部分是针对与前面的 MATCH 子句匹配的每个 `friend` 执行的。执行内部查询时，`ORDER` 和 `LIMIT` 子句是特定朋友居住的城市的本地子句，因此每个朋友可以获得（最多）两个城市。

 所有查询子句都可以在 `CALL` 子查询中使用。这也包括嵌套 `CALL` 子查询。第一个 `WITH` 子句和发出的变量存在一些限制，下文将对此进行说明。

### CALL 子查询中的变量范围
<a name="variable-scope-inside-call-subquery"></a>

 `CALL` 子查询之前的子句中使用的变量必须由初始 `WITH` 子句导入。与常规 `WITH` 子句不同，它只能包含一系列变量，但不允许使用别名，也不能与 `DISTINCT`、`ORDER BY`、`WHERE`、`SKIP` 或 `LIMIT` 一起使用。

### 从 CALL 子查询返回的变量
<a name="variables-returned-call-subquery"></a>

 `CALL` 子查询中发出的变量由最后的 `RETURN` 子句指定。请注意，发出的变量不能与 `CALL` 子查询之前的变量重叠。

### 限制
<a name="call-subquery-limitations"></a>

 目前，不支持 `CALL` 子查询中的更新。

## Neptune openCypher 函数
<a name="opencypher-compliance-new-functions"></a>

 在 Neptune 数据库 1.4.1.0 及更高版本和 Neptune Analytics 中可用。

**textIndexOf**

 `textIndexOf(text :: STRING, lookup :: STRING, from = 0 :: INTEGER?, to = -1 :: INTEGER?) :: (INTEGER?)` 

 返回从偏移 `from`（包含）到偏移 `to`（排除）的 `text` 范围内首次出现 `lookup` 的索引。如果 `to` 为 -1，则范围一直延续到 `text` 的末尾。索引从零开始，以 Unicode 标量值（非代理代码点）表示。

```
RETURN textIndexOf('Amazon Neptune', 'e')
{
  "results": [{
      "textIndexOf('Amazon Neptune', 'e')": 8
    }]
}
```

**collToSet**

 `collToSet(values :: LIST OF ANY?) :: (LIST? OF ANY?)` 

 返回仅包含原始列表中唯一元素的新列表。**保持**原始列表的顺序（例如 `[1, 6, 5, 1, 5]` 返回 `[1, 6, 5]`）。

```
RETURN collToSet([1, 6, 5, 1, 1, 5])
{
  "results": [{
      "collToSet([1, 6, 5, 1, 1, 5])": [1, 6, 5]
    }]
}
```

**collSubtract**

 `collSubtract(first :: LIST OF ANY?, second :: LIST OF ANY?) :: (LIST? OF ANY?)` 

 返回包含 `first` 中所有唯一元素且排除 `second` 中元素的新列表。

```
RETURN collSubtract([2, 5, 1, 0], [1, 5])
{
  "results": [{
      "collSubtract([2, 5, 1, 0], [1, 5])": [0, 2]
    }]
}
```

**collIntersection**

 `collIntersection(first :: LIST? OF ANY?, second :: LIST? OF ANY?) :: (LIST? OF ANY?)` 

 返回包含 `first` 和 `second` 交叉点中所有唯一元素的新列表。

```
RETURN collIntersection([2, 5, 1, 0], [1, 5])
{
  "results": [{
      "collIntersection([2, 5, 1, 0], [1, 5])": [1, 5]
    }]
}
```

## 排序函数
<a name="sorting-functions"></a>

 以下几节定义了对集合进行排序的函数。这些函数采用（在某些情况下是可选的）`config`映射参数，或者多个此类映射的列表，这些映射定义了排序键 and/or 的排序方向：

```
{ key: STRING, order: STRING }
```

 在此示例中，`key` 是映射或节点属性，其值用于排序。`order` 是“`asc`”或“`desc`”（不区分大小写），分别用于指定升序或降序排序。默认情况下，将按升序进行排序。

**collSort**

 `collSort(coll :: LIST OF ANY, config :: MAP?) :: (LIST? OF ANY?)` 

 返回包含 `coll` 输入列表中元素的新排序列表。

```
RETURN collSort([5, 3, 1], {order: 'asc'})
{
  "results": [{
      "collSort([5, 3, 1])": [1, 3, 5]
    }]
}
```

**collSortMaps**

 `collSortMaps(coll :: LIST OF MAP, config :: MAP) :: (LIST? OF ANY?)` 

 返回按指定 `key` 属性的值排序的映射列表。

```
RETURN collSortMaps([{name: 'Alice', age: 25}, {name: 'Bob', age: 35}, {name: 'Charlie', age: 18}], {key: 'age', order: 'desc'})
{
  "results": [{
      "x": [{
          "age": 35,
          "name": "Bob"
        }, {
          "age": 25,
          "name": "Alice"
        }, {
          "age": 18,
          "name": "Charlie"
        }]
    }]
}
```

**collSortMulti**

```
collSortMulti(coll :: LIST OF MAP?, 
configs = [] :: LIST OF MAP, 
limit = -1 :: INTEGER?, 
skip = 0 :: INTEGER?) :: (LIST? OF ANY?)
```

 返回按指定 `key` 属性的值排序的映射列表，可以选择应用限制和跳过。

```
RETURN collSortMulti([{name: 'Alice', age: 25}, {name: 'Bob', age: 35}, {name: 'Charlie', age: 18}], [{key: 'age', order: 'desc'}, {key:'name'}]) as x
{
  "results": [{
      "x": [{
          "age": 35,
          "name": "Bob"
        }, {
          "age": 25,
          "name": "Alice"
        }, {
          "age": 18,
          "name": "Charlie"
        }]
    }]
}
```

**collSortNodes**

 `collSortNodes(coll :: LIST OF NODE, config :: MAP) :: (LIST? OF NODE?)` 

 返回 `coll` 输入列表的排序版本，按节点元素各自 `key` 属性的值对节点元素进行排序。

```
create (n:person {name: 'Alice', age: 23}), (m:person {name: 'Eve', age: 21}), (o:person {name:'Bob', age:25})
{"results":[]}

match (n:person) with collect(n) as people return collSortNodes(people, {key: 'name', order: 'desc'})
{
  "results": [{
      "collSortNodes(people, 'name')": [{
          "~id": "e599240a-8c23-4337-8aa8-f603c8fb5488",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 21,
            "name": "Eve"
          }
        }, {
          "~id": "8a6ef785-59e3-4a0b-a0ff-389655a9c4e6",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 25,
            "name": "Bob"
          }
        }, {
          "~id": "466bc826-f47f-452c-8a27-6b7bdf7ae9b4",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 23,
            "name": "Alice"
          }
        }]
    }]
}

match (n:person) with collect(n) as people return collSortNodes(people, {key: 'age'})
{
  "results": [{
      "collSortNodes(people, '^age')": [{
          "~id": "e599240a-8c23-4337-8aa8-f603c8fb5488",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 21,
            "name": "Eve"
          }
        }, {
          "~id": "466bc826-f47f-452c-8a27-6b7bdf7ae9b4",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 23,
            "name": "Alice"
          }
        }, {
          "~id": "8a6ef785-59e3-4a0b-a0ff-389655a9c4e6",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 25,
            "name": "Bob"
          }
        }]
    }]
}
```

## 时间函数
<a name="temporal-functions"></a>

 时间函数在 Neptune 版本 [1.4.5.0](https://docs.aws.amazon.com/releases/release-1.4.5.0.xml) 及更高版本中可用。

### day
<a name="temporal-functions-day"></a>

 `day(temporal :: (datetime | date)) :: (LONG)` 

 返回 `datetime` 或 `date` 值中月份的 `day`。对于 `datetime`：在提取日期之前，根据输入将值标准化为 UTC。对于 `date`：根据时区提取日期。

 `datetime` 输入在 Neptune 数据库和 Neptune Analytics 中均可用：

```
RETURN day(datetime('2021-06-03T01:48:14Z'))
{
  "results": [{
      "day(datetime('2021-06-03T01:48:14Z'))": 3
    }]
}
```

 在此示例中，`datetime` 被标准化为 UTC，因此 \$108:00 向后移动到 6 月 2 日。

```
RETURN day(datetime('2021-06-03T00:00:00+08:00'))
{
  "results": [{
      "day(datetime('2021-06-03T00:00:00+08:00'))": 2
    }]
}
```

 `date` 输入仅在 Neptune Analytics 中可用：

```
RETURN day(date('2021-06-03Z'))
{
  "results": [{
      "day(date('2021-06-03Z'))": 3
    }]
}
```

 `date` 保留时区，保持在 6 月 3 日。

```
RETURN day(date('2021-06-03+08:00'))
{
  "results": [{
      "day(date('2021-06-03+08:00'))": 3
    }]
}
```

### month
<a name="temporal-functions-month"></a>

 `month(temporal :: (datetime | date)) :: (LONG)` 

 返回 `datetime` 或 `date` 值（1-12）中的月份。对于 `datetime`：在提取月份之前，根据输入将值标准化为 UTC。对于 `date`：根据时区提取月份。

 `datetime` 输入在 Neptune 数据库和 Neptune Analytics 中均可用：

```
RETURN month(datetime('2021-06-03T01:48:14Z'))
{
  "results": [{
      "month(datetime('2021-06-03T01:48:14Z'))": 6
    }]
}
```

 在此示例中，`datetime` 被标准化为 UTC，因此 \$108:00 向后移动到 5 月 31 日。

```
RETURN month(datetime('2021-06-01T00:00:00+08:00'))
{
  "results": [{
      "month(datetime('2021-06-01T00:00:00+08:00'))": 5
    }]
}
```

 `date` 输入仅在 Neptune Analytics 中可用：

```
RETURN month(date('2021-06-03Z'))
{
  "results": [{
      "month(date('2021-06-03Z'))": 6
    }]
}
```

 `date` 保留时区，保持在 6 月 1 日。

```
RETURN month(date('2021-06-01+08:00'))
{
  "results": [{
      "month(date('2021-06-01+08:00'))": 6
    }]
}
```

### year
<a name="temporal-functions-year"></a>

 `year(temporal :: (datetime | date)) :: (LONG)` 

 返回 `datetime` 或 `date` 值中的年份。对于 `datetime`：在提取年份之前，根据输入将值标准化为 UTC。对于 `date`：根据时区提取年份。

 `datetime` 输入在 Neptune 数据库和 Neptune Analytics 中均可用：

```
RETURN year(datetime('2021-06-03T01:48:14Z'))
{
  "results": [{
      "year(datetime('2021-06-03T01:48:14Z'))": 2021
    }]
}
```

 在此示例中，`datetime` 被标准化为 UTC，因此 \$108:00 向后移动到 2020 年 12 月 31 日。

```
RETURN year(datetime('2021-01-01T00:00:00+08:00'))
{
  "results": [{
      "year(datetime('2021-01-01T00:00:00+08:00'))": 2020
    }]
}
```

 `date` 输入仅在 Neptune Analytics 中可用：

```
RETURN year(date('2021-06-03Z'))
{
  "results": [{
      "year(date('2021-06-03Z'))": 2021
    }]
}
```

 `date` 保留时区，保持在 2021 年 6 月。

```
RETURN year(date('2021-01-01+08:00'))
{
  "results": [{
      "year(date('2021-01-01+08:00'))": 2021
    }]
}
```

### Neptune openCypher 函数
<a name="openCypher-functions"></a>

 在 Neptune 数据库 1.4.6.0 及更高版本和 Neptune Analytics 中可用。

#### reduce()
<a name="openCypher-functions-reduce"></a>

 reduce 通过将每个列表元素与运行总数或“累加器”组合来按顺序处理每个列表元素。它从初始值开始，在每次操作后更新累加器，并在下一次迭代中使用该更新的值。

 `for i in (0, ..., n) acc = acc X list[I], where X denotes any binary operator` 

 处理完所有元素后，它会返回最终的累加结果。

 典型的 reduce() 结构是：`reduce(accumulator = initial , variable IN list | expression)`

**类型规格：**  
 `- initial: starting value for the accumulator :: (Long | FLOAT | STRING | LIST? OF (STRING, LONG, FLOAT)) - list: the input list :: LIST OF T where T matches initial type - variable :: represents each element in the input list - expression :: Only supports '+' and '*' operator - return :: Same type as initial ` 

**限制：**  
 目前，`reduce()` 表达式仅支持：
+  数值乘法 
+  数值加法 
+  字符串连接 
+  列表连接 

 它们由 `+` 或 `*` 运算符表示。该表达式应为二进制表达式，如下所示：`expression pattern: accumulator + any variable or accumulator * any variable`

**溢出处理：**  
 Neptune 会在 `reduce()` 评估期间检测数值溢出问题，并根据数据类型做出不同的响应：

```
LONG (signed 64‑bit)
--------------------
• Valid range: –9 223 372 036 854 775 808 … 9 223 372 036 854 775 807  
• If any intermediate or final value falls outside this range,
  Neptune aborts the query with long overflow error message.
  
FLOAT (IEEE‑754 double)
-----------------------
• Largest finite value ≈ 1.79 × 10^308  
• Larger results overflow to INF
  Once `INF` is produced, it propagates through the remainder
  of the reduction.
```

**示例：**  
有关 reduce() 函数的信息，请参阅以下示例。

```
1. Long Addition:
RETURN reduce(sum = 0, n IN [1, 2, 3] | sum + n)
{
  "results": [{
      "reduce(sum = 0, n IN [1, 2, 3] | sum + n)": 6
    }]
}

2. String Concatenation:
RETURN reduce(str = "", x IN ["A", "B", "C"] | str + x) 
{
  "results": [{
      "reduce(str = "", x IN ["A", "B", "C"] | str + x)": "ABC"
    }]
}

3. List Combination:
RETURN reduce(lst = [], x IN [1, 2, 3] | lst + x)
{
  "results": [{
      "reduce(lst = [], x IN [1, 2, 3] | lst + x)": [1, 2, 3]
    }]
}

4. Float Addition:
RETURN reduce(total = 0.0, x IN [1.5, 2.5, 3.5] | total + x) 
{
  "results": [{
      "reduce(total = 0.0, x IN [1.5, 2.5, 3.5] | total + x)": 7.5
    }]
}

5. Long Multiplication:
RETURN reduce(product = 1, n IN [1, 2, 3] | product * n)
{
  "results": [{
      "reduce(product = 0, n IN [1, 2, 3] | product * n)": 6
    }]
}

6. Float Multiplication:
RETURN reduce(product = 1.0, n IN [1.5, 2.5, 3.5] | product * n)
{
  "results": [{
      "reduce(product = 1.0, n IN [1.5, 2.5, 3.5] | product * n)": 13.125
    }]
}

7. Long Overflow (Exception):
RETURN reduce(s = 9223372036854775807, x IN [2, 3] | s * x) AS result
{
"results": [{
    "reduce(s = 9223372036854775807, x IN [2, 3] | s * x) AS result": long overflow
    }]
}

8. Float Overflow:
RETURN reduce(s = 9.0e307, x IN [8.0e307, 1.0e307] | s + x) AS result
{
"results": [{
    "reduce(s = 9.0e307, x IN [8.0e307, 1.0e307] | s + x) AS result": INF
    }]
}
```

# neptune.read ()
<a name="access-graph-opencypher-21-extensions-s3-read"></a>

 Neptune 支持从亚马逊 S3 读取数据，然后使用这些数据运行 OpenCypher 查询（读取、插入、更新）的`CALL`过程`neptune.read`。该过程将文件中的每一行作为声明的结果变量行生成。它使用调用者的 IAM 凭证来访问 Amazon S3 中的数据。[管理 neptune.read () 的权限](access-graph-opencypher-21-extensions-s3-read-permissions.md)要设置权限，请参阅。Amazon S3 存储桶的区域必须与实例所在的区域相同。 AWS 目前，不支持跨区域读取。

 **语法** 

```
CALL neptune.read(
  {
    source: "string",
    format: "parquet/csv",
    concurrency: 10
  }
)
YIELD row
...
```

**输入**
+  **来源**（必填）-**单个**对象的 Amazon S3 URI。不支持多个对象的 Amazon S3 前缀。
+  **格式**（必填）-`parquet` 并`csv`受支持。
  +  有关所支持的 Parquet 格式的更多详细信息，请参阅[支持的镶木地板柱类型](access-graph-opencypher-21-extensions-s3-read-parquet.md#access-graph-opencypher-21-extensions-s3-read-parquet-column-types)。
  +  有关支持的 csv 格式的更多信息，请参阅[openCypher 数据的加载格式](bulk-load-tutorial-format-opencypher.md)。
+  **并发**（可选）-类型：0 或更大的整数。默认值：0。指定用于读取文件的线程数。如果该值为 0，则将使用资源允许的最大线程数。对于 Parquet，建议将其设置为多个行组。

**输出**

 neptune.read 返回：
+  **行**-类型:地图 
  +  文件中的每一行，其中键是列，值是每列中的数据。
  +  您可以像访问属性一样访问每列的数据 (`row.col`)。

## neptune.read 的最佳实践 ()
<a name="access-graph-opencypher-21-extensions-s3-read-best-practices"></a>

Neptune S3 读取操作可能占用大量内存。请使用非常适合生产工作负载的实例类型，如为 [Amazon Neptune 选择实例类型](instance-types.md)中所述。

`neptune.read()`请求的内存使用和性能会受到多种因素的影响，例如文件大小、列数、行数和文件格式。根据结构的不同，小文件（例如，100 MB 或更小的 CSV 文件、20MB 或以下的 Parquet 文件）可能在大多数适合生产的实例类型上可靠地运行，而较大的文件可能需要较小的实例类型无法提供的大量内存。

在测试此功能时，建议从小文件开始，然后逐渐扩展，以确保您的读取工作负载可以适应您的实例大小。如果您发现`neptune.read()`请求导致 out-of-memory异常或实例重启，请考虑将文件拆分为较小的块、降低文件复杂性或升级到更大的实例类型。

# 使用实木复合地板查询示例
<a name="access-graph-opencypher-21-extensions-s3-read-parquet"></a>

以下示例查询返回给定 Parquet 文件中的行数：

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "parquet"
  }
)
YIELD row
RETURN count(row)
```

您可以通过执行以下代码，使用中的`execute-open-cypher-query`操作 AWS CLI 来运行查询示例：

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'parquet'}) YIELD row RETURN count(row)" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

查询可以灵活地处理从 Parquet 文件中读取的行。例如，以下查询创建一个节点，其字段设置为在 Parquet 文件中找到的数据：

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "parquet"
  }
)
YIELD row
CREATE (n {someField: row.someCol}) 
RETURN n
```

**警告**  
使用像条款`MATCH(n)`之前那样的大型产生结果的条款不被视为良好做法。`CALL`这将导致查询长时间运行，这是因为来自先前子句的传入解决方案与 neptune.read 读取的行之间存在交叉积。建议使用 nept `CALL` une.read 开始查询。

## 支持的镶木地板柱类型
<a name="access-graph-opencypher-21-extensions-s3-read-parquet-column-types"></a>

**实木复合地板数据类型：**
+ NULL
+ BOOLEAN
+ FLOAT
+ DOUBLE
+ string
+ 有符号整数： UINT8、 UINT16、 UINT32、 UINT64
+ 地图：仅支持一级。不支持嵌套。
+ 列表：仅支持一级。不支持嵌套。

**海王星特定的数据类型：**

与 CSV 格式的属性列标题不同，Parquet 格式的属性列标题只需要具有属性名称，因此无需输入类型名称或基数。

但是，有些特殊的 Parquet 格式列类型需要在元数据中进行注释，包括 “任意类型”、“日期类型”、“日期时间” 类型和 “几何类型”。以下对象是包含这些特殊类型列的文件所需的元数据注释的示例：

```
"metadata": {
    "anyTypeColumns": ["UserCol1"],
    "dateTypeColumns": ["UserCol2"],
    "dateTimeTypeColumns": ["UserCol3"],
    "geometryTypeColumns": ["UserCol4"]
}
```

以下是与这些类型相关的预期有效载荷的详细信息：
+ 用户列中支持 “任意” 列类型。Any 类型是我们支持的所有其他类型的 “句法糖” 类型。如果用户列中有多种类型，则它非常有用。Any type 值的有效负载是一个 json 字符串列表，如下所示：`{"value": "10", "type": "Int"};{"value": "1.0", "type": "Float"}`，它在每个 json 字符串中都有一个值字段和一个类型字段。已设置 “任意” 列的基数值，这意味着该列可以接受多个值。
  + Neptune 在 Any 类型中支持以下类型：布尔型（或布尔型）、字节、短整型、长整型、、、、、 UnsignedByte UnsignedShort、浮点型 UnsignedInt UnsignedLong、双精度、日期、日期时间、日期时间、字符串和几何。
  + 任何类型都不支持向量类型。
  + 嵌套不支持任何类型。例如 `{"value": {"value": "10", "type": "Int"}, "type": "Any"}`。
+ 用户列中支持日期和日期时间类型的列。这些列的有效负载必须以符合 XSD 格式或以下格式之一的字符串形式提供：
  + yyyy-MM-dd
  + yyyy-mm-ddthh: mm
  + yyyy-mm-ddthh: mm: ss
  + yyyy-mm-ddthh: mm: ssz
  + yyyy-mm-ddthh: mm: ss.sssz
  + yyyy-mm-ddthh: mm: ss [\$1\$1-] 嗯嗯
  + yyyy-mm-ddthh: mm: ss.sss [\$1\$1-] 嗯嗯
+ 用户列支持几何列类型。这些列的有效载荷只能包含 Point 类型的几何基元，这些基元以众所周知文本 (WKT) 格式的字符串形式提供。例如，点 (30 10) 将是有效的几何值。

## 镶木地板输出示例
<a name="sample-parquet-output"></a>

给定这样的 Parquet 文件：

```
<s3 path>

Parquet Type:
    int8     int16       int32             int64              float      double    string
+--------+---------+-------------+----------------------+------------+------------+----------+
|   Byte |   Short |       Int   |                Long  |     Float  |    Double  | String   |
|--------+---------+-------------+----------------------+------------+------------+----------|
|   -128 |  -32768 | -2147483648 | -9223372036854775808 |    1.23456 |    1.23457 | first    |
|    127 |   32767 |  2147483647 |  9223372036854775807 |  nan       |  nan       | second   |
|      0 |       0 |           0 |                    0 | -inf       | -inf       | third    |
|      0 |       0 |           0 |                    0 |  inf       |  inf       | fourth   |
+--------+---------+-------------+----------------------+------------+------------+----------+
```

以下是 neptune.read 使用以下查询返回的输出示例：

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'parquet'}) YIELD row RETURN row" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

```
{
 "results": [{
 "row": {
 "Float": 1.23456,
 "Byte": -128,
 "Int": -2147483648,
 "Long": -9223372036854775808,
 "String": "first",
 "Short": -32768,
 "Double": 1.2345678899999999
 }
 }, {
 "row": {
 "Float": "NaN",
 "Byte": 127,
 "Int": 2147483647,
 "Long": 9223372036854775807,
 "String": "second",
 "Short": 32767,
 "Double": "NaN"
 }
 }, {
 "row": {
 "Float": "-INF",
 "Byte": 0,
 "Int": 0,
 "Long": 0,
 "String": "third",
 "Short": 0,
 "Double": "-INF"
 }
 }, {
 "row": {
 "Float": "INF",
 "Byte": 0,
 "Int": 0,
 "Long": 0,
 "String": "fourth",
 "Short": 0,
 "Double": "INF"
 }
 }]
}
```

当前，无法为来自 Parquet 文件的数据字段设置节点或边缘标签。建议您将查询分成多个查询，每个查询对应一个标签/类型。

```
CALL neptune.read({source: '<s3 path>', format: 'parquet'})
 YIELD row 
WHERE row.`~label` = 'airport'
CREATE (n:airport)

CALL neptune.read({source: '<s3 path>', format: 'parquet'})
YIELD row 
WHERE row.`~label` = 'country'
CREATE (n:country)
```

# 使用 CSV 查询示例
<a name="access-graph-opencypher-21-extensions-s3-read-csv"></a>

在此示例中，查询返回给定 CSV 文件中的行数：

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "csv"
  }
)
YIELD row
RETURN count(row)
```

您可以通过执行以下代码，使用中的 execute-open-cypher-query操作 AWS CLI 来运行查询示例：

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'csv'}) YIELD row RETURN count(row)" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

查询可以灵活地处理从 CSV 文件中读取的行。例如，以下查询创建了一个节点，其字段设置为 CSV 文件中的数据：

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "csv"
  }
)
YIELD row
CREATE (n {someField: row.someCol}) 
RETURN n
```

**警告**  
在 CALL 子句之前使用像 MATCH (n) 这样的大型结果生成子句不被视为良好做法。由于来自先前子句的传入解决方案与 neptune.read 读取的行之间存在交叉积，这将导致查询长时间运行。建议使用 CALL neptune.read 开始查询。

## 属性列标题
<a name="property-column-headers"></a>

可通过使用以下语法指定属性的列 (`:`)。类型名称不区分大小写。如果属性名称中出现冒号，则必须在其前面加上反斜杠:。`\:`

```
propertyname:type
```

**注意**  
不允许在列标题中使用空格、逗号、回车和换行符，因此属性名称不得包含这些字符。
您可以通过将 `[]` 添加到类型来指定数组列：  

  ```
                          propertyname:type[]
  ```
边缘属性只能有一个值，如果指定了数组类型或指定了另一个值，则会导致错误。以下示例显示了名为 age 且类型为 Int 的属性的列标题：  

  ```
  age:Int
  ```

文件中的每行都需要在该位置具有整数或保留为空。允许使用字符串数组，但是数组中的字符串不能包含分号 (`;`) 字符，除非使用反斜杠 () 对其进行转义。`\;`

## 支持的 CSV 列类型
<a name="supported-csv-column-types"></a>
+ **BOOL（或布尔值）**-允许的值：真、假。指示 Boolean 字段。除 true 之外的任何值都将被视为假。
+ **FLOAT**-范围：32 位 IEEE 754 浮点数，包括 Infinity、INF、-Infinity、-INF 和 NaN ()。not-a-number
+ **双精**度-范围：64 位 IEEE 754 浮点数，包括 Infinity、INF、-Infinity、-INF 和 NaN ()。not-a-number
+ **字符串**- 
  + 引号是可选的。如果逗号、换行符和回车符包含在由双引号 (“) 包围的字符串中，则会自动对其进行转义。示例：“你好，世界”。
  + 要在带引号的字符串中包含引号，可以连续使用两个引号来转义引号：示例：“Hello “" World ""””。
  + 允许使用字符串数组，但是数组中的字符串不能包含分号 (;) 字符，除非使用反斜杠 (\$1;) 对其进行转义。
  + 如果要使用引号将数组内的字符串括起来，则必须使用一组引号将整个数组括起来。示例：“字符串一；字符串 2；字符串 3”。
+ **日期、日期时间**-日期时间值可以采用 XSD 格式或以下格式之一提供：
  + yyyy-MM-dd
  + yyyy-mm-ddthh: mm
  + yyyy-mm-ddthh: mm: ss
  + yyyy-mm-ddthh: mm: ssz
  + yyyy-mm-ddthh: mm: ss.sssz
  + yyyy-mm-ddthh: mm: ss [\$1\$1-] 嗯嗯
  + yyyy-mm-ddthh: mm: ss.sss [\$1\$1-] 嗯嗯
+ 有@@ **符号整数**- 
  + 字节：-128 到 127
  + 空头：-32768 至 32767
  + Int：-2^31 到 2^31-1
  + 长：-2^63 到 2^63-1

**海王星特有的柱子类型：**
+ 用户列中支持 “任意” 列类型。Any 类型是我们支持的所有其他类型的 “句法糖” 类型。如果用户列中有多种类型，则它非常有用。Any type 值的有效负载是一个 json 字符串列表，如下所示：`{"value": "10", "type": "Int"};{"value": "1.0", "type": "Float"}`，它在每个 json 字符串中都有一个值字段和一个类型字段。“任意” 类型的列标题是属性名称：any。已设置 “任意” 列的基数值，这意味着该列可以接受多个值。
  + Neptune 在 Any 类型中支持以下类型：布尔型（或布尔型）、字节、短整型、长整型、、、、、 UnsignedByte UnsignedShort、浮点型 UnsignedInt UnsignedLong、双精度、日期、日期时间、日期时间、字符串和几何。
  + 任何类型都不支持向量类型。
  + 嵌套不支持任何类型。例如 `{"value": {"value": "10", "type": "Int"}, "type": "Any"}`。
+ 用户列支持几何列类型。这些列的有效载荷只能包含 Point 类型的几何基元，这些基元以众所周知文本 (WKT) 格式的字符串形式提供。例如，点 (30 10) 将是有效的几何值。

## CSV 输出示例
<a name="sample-csv-output"></a>

鉴于以下 CSV 文件：

```
<s3 path>
colA:byte,colB:short,colC:int,colD:long,colE:float,colF:double,colG:string
-128,-32768,-2147483648,-9223372036854775808,1.23456,1.23457,first
127,32767,2147483647,9223372036854775807,nan,nan,second
0,0,0,0,-inf,-inf,third
0,0,0,0,inf,inf,fourth
```

此示例显示了 neptune.read 使用以下查询返回的输出：

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'csv'}) YIELD row RETURN row" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

```
{
  "results": [{
      "row": {
        "colD": -9223372036854775808,
        "colC": -2147483648,
        "colE": 1.23456,
        "colB": -32768,
        "colF": 1.2345699999999999,
        "colG": "first",
        "colA": -128
      }
    }, {
      "row": {
        "colD": 9223372036854775807,
        "colC": 2147483647,
        "colE": "NaN",
        "colB": 32767,
        "colF": "NaN",
        "colG": "second",
        "colA": 127
      }
    }, {
      "row": {
        "colD": 0,
        "colC": 0,
        "colE": "-INF",
        "colB": 0,
        "colF": "-INF",
        "colG": "third",
        "colA": 0
      }
    }, {
      "row": {
        "colD": 0,
        "colC": 0,
        "colE": "INF",
        "colB": 0,
        "colF": "INF",
        "colG": "fourth",
        "colA": 0
      }
    }]
}
```

目前，无法为来自 CSV 文件的数据字段设置节点或边缘标签。建议您将查询分成多个查询，每个查询对应一个标签/类型。

```
CALL neptune.read({source: '<s3 path>', format: 'csv'})
 YIELD row 
WHERE row.`~label` = 'airport'
CREATE (n:airport)

CALL neptune.read({source: '<s3 path>', format: 'csv'})
YIELD row 
WHERE row.`~label` = 'country'
CREATE (n:country)
```

# 管理 neptune.read () 的权限
<a name="access-graph-opencypher-21-extensions-s3-read-permissions"></a>

## 所需的 IAM 策略
<a name="access-graph-opencypher-21-extensions-s3-read-permissions-iam"></a>

要执行使用的 OpenCypher 查询`neptune.read()`，您必须具有访问您的 Neptune 数据库中数据的相应权限。只读查询需要执行`ReadDataViaQuery`操作。修改数据的查询`WriteDataViaQuery`需要插入或删除`DeleteDataViaQuery`。以下示例允许在指定集群上执行所有三个操作。

此外，您需要访问包含您的数据文件的 S3 存储桶的权限。Neptunes3Access 策略声明授予所需的 S3 权限：
+ **`s3:ListBucket`**：验证存储桶是否存在并列出内容所必需的。
+ **`s3:GetObject`**：需要访问指定对象，以便可以读取其内容以集成到 OpenCypher 查询中。

如果您的 S3 存储桶使用服务器端加密 AWS KMS，则还必须授予 KMS 权限。Neptunes3 KMSAccess 策略声明允许 Neptune 在访问加密的 S3 对象时解密数据并生成数据密钥。该条件将 KMS 操作限制为来自您所在地区的 S3 和 RDS 服务的请求。
+ **`kms:Decrypt`**：需要对加密对象进行解密，以便 Neptune 可以读取其数据。
+ **`kms:GenerateDataKey`**：用于检索要读取的对象的 S3 API 也是必需的。

```
{
  "Sid": "NeptuneQueryAccess",
  "Effect": "Allow",
  "Action": [
      "neptune-db:ReadDataViaQuery",
      "neptune-db:WriteDataViaQuery",
      "neptune-db:DeleteDataViaQuery"
  ],
  "Resource": "arn:aws:neptune-db:<REGION>:<AWS_ACCOUNT_ID>:<CLUSTER_RESOURCE_ID>/*"
},
{
  "Sid": "NeptuneS3Access",
  "Effect": "Allow",
  "Action": [
      "s3:ListBucket",
      "s3:GetObject"
  ],
  "Resource": [
      "arn:aws:s3:::neptune-read-bucket",
      "arn:aws:s3:::neptune-read-bucket/*"
  ]
},
{
  "Sid": "NeptuneS3KMSAccess",
  "Effect": "Allow",
  "Action": [
      "kms:Decrypt",
      "kms:GenerateDataKey"
  ],
  "Resource": "arn:aws:kms:<REGION>:<AWS_ACCOUNT_ID>:key/<KEY_ID>",
  "Condition": {
      "StringEquals": {
        "kms:ViaService": [
            "s3.<REGION>.amazonaws.com",
            "rds.<REGION>.amazonaws.com"
        ]
      }
  }
}
```

## 重要先决条件
<a name="access-graph-opencypher-21-extensions-s3-read-permissions-prerequisites"></a>

这些权限和先决条件可确保将 S3 数据安全可靠地集成到 OpenCypher 查询中，同时保持适当的访问控制和数据保护措施。
+ **IAM 身份验证**：仅启用了 IAM 身份验证的 Neptune 集群支持此功能。有关如何创建和连接支持 IAM 身份验证的[集群的详细说明，请参阅保护您的 Amazon Neptune 数据库](security.md)。
+ **VPC 终端节点**：
  + Amazon S3 需要网关类型 VPC 端点，才能支持 Neptune 与 Amazon S3 进行通信。
  + 要在查询中使用自定义 AWS KMS 加密，需要接口类型的 VPC 终端节点，以允许 AWS KMS Neptune 与之通信。 AWS KMS
  + 有关如何配置此终端节点的详细说明，请参阅[创建 Amazon S3 VPC 终端节点](bulk-load-tutorial-IAM.md)。

# 空间数据
<a name="access-graph-opencypher-22-spatial-data"></a>

Amazon Neptune 现在支持空间查询，允许您在图表中存储和分析几何数据。虽然空间要素通常用于地理位置（例如地图上的坐标），但它适用于任何位置和邻近度很重要的二维数据。使用此功能回答诸如 “哪些商店距离该客户 5 英里以内？” 之类的问题 、“查找与该服务区相交的所有配送路线” 或 “此平面图中的哪些组件与 HVAC 区域重叠？” Neptune 使用适用于点、多边形和其他几何形状的行业标准空间类型函数来实现空间支持。您可以将空间数据存储为节点和边的属性，然后使用空间函数计算距离、检查点是否落在边界内或查找重叠区域，所有这些都在 OpenCypher 查询中完成。

**常见用例**：
+ **地理应用**：基于位置的建议、地理围栏、路线规划和区域分析
+ **设施和空间管理**：平面图布局、设备布局和区域覆盖范围
+ **网络拓扑**：物理基础设施映射、覆盖区域和服务边界
+ **设计和 CAD**：二维设计中的组件定位、碰撞检测和空间关系
+ **游戏开发**：角色定位、碰撞检测和 area-of-effect计算

与其他数据库一样，Amazon Neptune 中的空间类型实现遵循了 ISO/IEC 13249-3:2016 指令。[空间函数](access-graph-opencypher-22-spatial-functions.md)它们以 OpenCypher 查询语言提供。

## 坐标系
<a name="access-graph-opencypher-22-spatial-data-coordinate-system"></a>

Neptune 有一个用于整个数据库的空间参考标识符 (SRID)。坐标系的同质性减少了用户在查询中的错误并提高了数据库性能。第一个版本 (1.4.7.0) 支持笛卡尔坐标系，也称为 SRID 0。

SRID 0 的 Neptune 实现与经度和纬度值兼容。用于`ST_DistanceSpheroid`根据 WGS84 /SRID 4326 计算距离。

当前的实现支持存储三维坐标。空间函数目前仅支持使用 x 轴和 y 轴（二维）坐标。可用空间函数目前不支持 z 轴坐标。

## 存储位置数据
<a name="storing-spatial-data"></a>

使用 Geometry 属性类型存储节点和边上的位置数据。使用知名文本 (WKT) 格式创建几何值，这是将地理形状表示为文本的标准方法。例如，要存储点位置，请执行以下操作：

```
CREATE (n:airport {code: 'ATL', location: ST_GeomFromText('POINT (-84.4281 33.6367)')})
```

使用地理坐标时，第一个参数 (x) 代表经度，第二个参数 (y) 代表纬度。这遵循空间数据库中使用的标准坐标顺序和 ISO 19125 标准。

**注意**  
 Neptune 现在支持一种名为 “几何” 的新数据类型。可以使用`ST_GeomFromText`函数从 WKT 字符串创建节点或边的几何属性。  
Neptune 会自动将点数据存储在专门的空间索引中，以提高空间类型函数的性能。例如，专门的空间索引可以加速`ST_Contains`用于查找多边形内的点。  
[维基百科页面，其中包含几何图形的知名文本表示](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)

## 批量加载空间数据
<a name="loading-spatial-data-bulk"></a>

批量加载数据时，请在 CSV 标题中指定几何类型。Neptune 将解析 WKT 字符串并创建相应的几何属性：

```
:ID,:LABEL,code:String,city:String,location:Geometry
21,airport,ATL,Atlanta,POINT (-84.42810059 33.63669968)
32,airport,ANC,Anchorage,POINT (-149.9960022 61.17440033)
43,airport,AUS,Austin,POINT (-97.66989899 30.19449997)
```

有关完整的 CSV 格式详细信息，请参阅 [OpenCypher 批量加载](bulk-load-tutorial-format-opencypher.md)格式。

## 查询空间数据
<a name="querying-spatial-data"></a>

以下查询示例使用[航空路线数据集](https://github.com/krlawrence/graph/tree/main/sample-data)来演示如何在 Neptune 中使用空间函数。

如果您的数据具有单独的纬度和经度属性而不是几何属性，则可以在查询时将其转换为点。查找距离给定位置最近的 10 个机场：

```
MATCH (a:airport)
WITH a, ST_GeomFromText('POINT (' + a.lon + ' ' + a.lat + ')') AS airportLocation
WITH a, airportLocation, ST_Distance(ST_GeomFromText('POINT (-84.4281 33.6367)'), airportLocation) AS distance
WHERE distance IS NOT NULL
RETURN a.code, a.city, distance
ORDER BY distance ASC
LIMIT 10
```

如果您已经将位置存储为，`ST_Point`则可以直接使用这些位置值：

1. 设置 属性

   ```
   MATCH (a:airport)
   SET a.location = ST_GeomFromText('POINT (' + a.lon + ' ' + a.lat + ')')
   ```

1. 使用 ST\$1Distance 进行查询：

   ```
   MATCH (a:airport)
   WHERE a.location IS NOT NULL
   WITH a, ST_Distance(ST_GeomFromText('POINT (-84.4281 33.6367)'), a.location) AS distance
   RETURN a.code, a.city, distance
   ORDER BY distance ASC
   LIMIT 10
   ```

### 使用 Bolt 驱动器
<a name="querying-spatial-data-bolt"></a>

大多数查询方法以 WKT 字符串的形式返回 Geometry 值，这些值是人类可读的。如果您使用的是 Bolt 驱动程序，为了提高效率，几何值将以 WKB（众所周知的二进制）格式返回。在应用程序中将 WKB 转换为几何对象：

```
try (Session session = driver.session()) {
    Result result = session.run("MATCH (n:airport {code: 'ATL'}) RETURN n.location as geom");
    
    Record record = result.single();
    byte[] wkbBytes = record.get("geom").asByteArray();
    
    // Convert WKB to Geometry object using JTS library
    WKBReader wkbReader = new WKBReader();
    Geometry geom = wkbReader.read(wkbBytes);
}
```

# 空间函数
<a name="access-graph-opencypher-22-spatial-functions"></a>

Neptune OpenCypher 中提供了以下用于处理几何数据类型的空间函数：
+ [ST\$1Point](access-graph-opencypher-22-spatial-functions-st-point.md)
+ [ST\$1 GeomFromText](access-graph-opencypher-22-spatial-functions-st-geomfromtext.md)
+ [ST\$1 AsText](access-graph-opencypher-22-spatial-functions-st-astext.md)
+ [ST\$1 GeometryType](access-graph-opencypher-22-spatial-functions-st-geometrytype.md)
+ [ST\$1Equals](access-graph-opencypher-22-spatial-functions-st-equals.md)
+ [ST\$1Contains](access-graph-opencypher-22-spatial-functions-st-contains.md)
+ [ST\$1Intersects](access-graph-opencypher-22-spatial-functions-st-intersect.md)
+ [ST\$1Distance](access-graph-opencypher-22-spatial-functions-st-distance.md)
+ [ST\$1 DistanceSpheroid](access-graph-opencypher-22-spatial-functions-st-distancespheroid.md)
+ [ST\$1Envelope](access-graph-opencypher-22-spatial-functions-st-envelope.md)
+ [ST\$1Buffer](access-graph-opencypher-22-spatial-functions-st-buffer.md)

# ST\$1Point
<a name="access-graph-opencypher-22-spatial-functions-st-point"></a>

ST\$1Point 从输入坐标值中返回一个点。

**语法**

```
ST_Point(x, y, z)
```

**参数**
+ `x`-表示第一个坐标的双精度数据类型的值。
+ `y`-表示第二个坐标的双精度数据类型的值。
+ `z`-（可选）

**坐标顺序**

使用地理坐标时，第一个参数 (`x`) 代表**经度**，第二个参数 (`y`) 代表**纬度**。这遵循空间数据库中使用的标准坐标顺序和 ISO 19125 标准。

```
// Correct: longitude first, latitude second
ST_Point(-84.4281, 33.6367)  // Atlanta airport

// Incorrect: latitude first, longitude second
ST_Point(33.6367, -84.4281)  // This will return NaN in distance calculations
```

**有效的坐标范围**

对于地理数据，请确保坐标在有效范围内：
+ 经度 (`x`)：-180 到 180
+ 纬度 (`y`)：-90 到 90

当与诸如这样的距离计算函数一起使用时，超出这些范围的坐标将返回`NaN`（不是数字）`ST_DistanceSpheroid`。

**返回类型**

子类型点的几何

如果 x 或 y 为 null，则返回 null。

**示例**

以下内容根据输入坐标构造点几何。

```
RETURN ST_Point(5.0, 7.0); 
POINT(5 7)
```

# ST\$1 GeomFromText
<a name="access-graph-opencypher-22-spatial-functions-st-geomfromtext"></a>

ST\$1 根据输入几何体的众所周知的文本 (WKT) 表示形式GeomFromText 构造几何对象。

**语法**

```
ST_GeomFromText(wkt_string)
```

**参数**
+ `wkt_string`-数据类型为 STRING 的值，它是几何图形的 WKT 表示形式。

**返回类型**

GEOMETRY

如果 wkt\$1string 为 null，则返回 null。

如果 wkt\$1string 无效，则返回 a BadRequestException 。

**示例**

```
RETURN ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))')             
POLYGON((0 0,0 1,1 1,1 0,0 0))
```

# ST\$1 AsText
<a name="access-graph-opencypher-22-spatial-functions-st-astext"></a>

ST\$1 AsText 返回输入几何图形的已知文本 (WKT) 表示形式。

**语法**

```
ST_AsText(geo)
```

**参数**
+ `geo`-数据类型为 GEOMETRY 的值，或者计算结果为 GEOMETRY 的表达式。

**返回类型**

string

如果 geo 为 null，则返回 null。

如果输入参数不是几何， BadRequestException 则返回 a。

如果结果大于 64 KB 的字符串，则会返回错误。

**示例**

```
RETURN ST_AsText(ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))'))             
POLYGON((0 0,0 1,1 1,1 0,0 0))
```

# ST\$1 GeometryType
<a name="access-graph-opencypher-22-spatial-functions-st-geometrytype"></a>

ST\$1 以字符串GeometryType 形式返回几何体的类型。

**语法**

```
ST_GeometryType(geom)
```

**参数**
+ `geom`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。

**返回类型**

string

如果 geom 为 null，则返回 null。

如果输入参数不是几何， BadRequestException 则返回 a。

**示例**

```
RETURN ST_GeometryType(ST_GeomFromText('LINESTRING(77.29 29.07,77.42 29.26,77.27 29.31,77.29 29.07)'));
ST_LineString
```

# ST\$1Equals
<a name="access-graph-opencypher-22-spatial-functions-st-equals"></a>

如果输入几何图形的二维投影在拓扑上相等，ST\$1Equals 将返回 true。如果几何具有相等的点集，则认为它们在拓扑上是相等的。在拓扑相等的几何中，顶点的顺序在保持这种相等性的同时可能会有所不同。

**语法**

```
ST_Equals(geom1, geom2)
```

**参数**
+ `geom1`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。
+ `geom2`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。此值将与 geom1进行比较以确定它是否等于 geom1。

**返回类型**

BOOLEAN

如果 geom1 或 geom2 为 null，则返回 null。

如果 geom1 或 geom2 不是几何图形，则返回 a。 BadRequestException 

**示例**

```
RETURN ST_Equals(
    ST_GeomFromText('POLYGON ((0 2,1 1,0 -1,0 2))'), 
    ST_GeomFromText('POLYGON((-1 3,2 1,0 -3,-1 3))'));
false
```

以下内容检查两个线串在几何上是否相等。

```
RETURN ST_Equals(
    ST_GeomFromText('LINESTRING (1 0, 10 0)'), 
    ST_GeomFromText('LINESTRING(1 0,5 0,10 0)'));
true
```

# ST\$1Contains
<a name="access-graph-opencypher-22-spatial-functions-st-contains"></a>

如果第一个输入几何体的 2D 投影包含第二个输入几何体的 2D 投影，则 ST\$1Contains 返回 true。如果 B 中的每个点都是 A 中的一个点，并且其内部有非空交集，则几何 A 包含几何 B。ST\$1Contains (A, B) 等同于 ST\$1within (B, A)。

**语法**

```
ST_Contains(geom1, geom2)
```

**参数**
+ `geom1`-GEOMETRY 类型的值或计算结果为 GEOMETRY 类型的表达式。
+ `geom2`-GEOMETRY 类型的值或计算结果为 GEOMETRY 类型的表达式。此值将与 geom1 进行比较以确定它是否包含在 geom1 中。

**返回类型**

BOOLEAN

如果 geom1 或 geom2 为 null，则返回 null。

如果输入参数不是几何， BadRequestException 则返回 a。

**示例**

```
RETURN ST_Contains(
    ST_GeomFromText('POLYGON((0 2,1 1,0 -1,0 2))'), 
    ST_GeomFromText('POLYGON((-1 3,2 1,0 -3,-1 3))'));
false
```

# ST\$1Intersects
<a name="access-graph-opencypher-22-spatial-functions-st-intersect"></a>

如果两个输入几何体的 2D 投影至少有一个共同点，则 ST\$1Intersects 返回 true。

**语法**

```
ST_Intersects(geom1, geom2)
```

**参数**
+ `geom1`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。
+ `geom2`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。

**返回类型**

BOOLEAN

如果 geom1 或 geom2 为 null，则返回 null。

如果输入参数不是几何， BadRequestException 则返回 a。

**示例**

```
RETURN ST_Intersects(
    ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(2 2,2 5,5 5,5 2,2 2))'), 
    ST_GeomFromText('MULTIPOINT((4 4),(6 6))'));
true
```

# ST\$1Distance
<a name="access-graph-opencypher-22-spatial-functions-st-distance"></a>

对于输入几何体，ST\$1Distance 返回两个输入几何体值的 2D 投影之间的最小欧氏距离。

**语法**

```
ST_Distance(geo1, geo2)
```

**参数**
+ `geo1`-数据类型为 GEOMETRY 的值，或者计算结果为 GEOMETRY 类型的表达式。
+ `geo2`-数据类型为 GEOMETRY 的值，或者计算结果为 GEOMETRY 的表达式。

**返回类型**

双精度，单位与输入几何图形相同。

如果 geo1 或 geo2 为空，则返回空值。

如果输入参数不是几何， BadRequestException 则返回 a。

**示例**

```
RETURN ST_Distance(
    ST_GeomFromText('POLYGON((0 2,1 1,0 -1,0 2))'), 
    ST_GeomFromText('POLYGON((-1 -3,-2 -1,0 -3,-1 -3))'));
1.4142135623731
```

# ST\$1 DistanceSpheroid
<a name="access-graph-opencypher-22-spatial-functions-st-distancespheroid"></a>

返回两个 lon/lat 几何体之间的最小距离（以米为单位）。球体是 WGS84 /SRID 4326。

**语法**

```
ST_DistanceSpheroid(geom1, geom2);
```

**参数**
+ `geom1`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。
+ `geom2`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。

**返回类型**

FLOAT

如果 geom 为 null，则返回 null。

**示例**

```
RETURN ST_DistanceSpheroid(
    ST_GeomFromText('POINT(-110 42)'),
    ST_GeomFromText('POINT(-118 38)'))
814278.77
```

# ST\$1Envelope
<a name="access-graph-opencypher-22-spatial-functions-st-envelope"></a>

ST\$1Envelope 返回输入几何体的最小边界框，如下所示：
+ 如果输入几何为空，则返回的几何体将为 POINT EMPTY。
+ 如果输入几何体的最小边界框退化为一个点，则返回的几何体是一个点。
+ 如果前面都不成立，则该函数返回一个 counter-clockwise-oriented多边形，其顶点是最小边界框的角落。

对于所有非空输入，该函数在输入几何体的 2D 投影上运行。

**语法**

```
ST_Envelope(geom)
```

**参数**
+ `geom`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。

**返回类型**

GEOMETRY

如果 geom 为 null，则返回 null。

**示例**

```
RETURN ST_Envelope(ST_GeomFromText("POLYGON ((2 1, 4 3, 6 1, 5 5, 3 4, 2 1))"))
POLYGON ((2 1, 6 1, 6 5, 2 5, 2 1))
```

# ST\$1Buffer
<a name="access-graph-opencypher-22-spatial-functions-st-buffer"></a>

ST\$1Buffer 返回 2D 几何体，该几何表示与 xy 笛卡尔平面上投影的输入几何体之间的距离小于或等于输入距离的所有点。

**语法**

```
ST_Buffer(geom, distance, number_of_segments_per_quarter_circle)
```

**参数**
+ `geom`-数据类型为 GEOMETRY 的值或计算结果为 GEOMETRY 类型的表达式。
+ `distance`-数据类型为 DOUBLE PRECISION 的值，表示缓冲区的距离（或半径）。
+ `number_of_segments_per_quarter_circle`-数据类型为 INTEGER 的值（应大于或等于 0）。此值确定了围绕输入几何体的每个顶点大致四分之一圆的点数。负值默认为零。默认值为 8。

**返回类型**

GEOMETRY

ST\$1Buffer 函数返回 xy 笛卡尔平面中的二维 (2D) 几何体。

**示例**

```
RETURN ST_Buffer(ST_GeomFromText('LINESTRING (1 2,5 2,5 8)'), 2, 4);
POLYGON ((3 4, 3 8, 3.1522409349774265 8.76536686473018,
         3.585786437626905 9.414213562373096, 4.234633135269821 9.847759065022574,
         5 10, 5.765366864730179 9.847759065022574,
         6.414213562373095 9.414213562373096, 6.847759065022574 8.76536686473018,
         7 8, 7 2, 6.847759065022574 1.2346331352698203,
         6.414213562373095 0.5857864376269051, 5.765366864730179 0.1522409349774265,
         5 0, 1 0, 0.2346331352698193 0.152240934977427,
         -0.4142135623730954 0.5857864376269051,
         -0.8477590650225737 1.2346331352698208, -1 2.0000000000000004,
         -0.8477590650225735 2.7653668647301797,
         -0.4142135623730949 3.414213562373095,
         0.2346331352698206 3.8477590650225735, 1 4, 3 4))
```

以下内容返回近似于圆的输入点几何体的缓冲区。由于该命令指定 3 作为每个四分之一圆的区段数，因此该函数使用三个区段表示约四分之一圆。

```
RETURN ST_Buffer(ST_GeomFromText('POINT (1 1)'), 1.0, 8));
POLYGON ((2 1, 1.9807852804032304 0.8049096779838718,
     1.9238795325112867 0.6173165676349102, 1.8314696123025453 0.4444297669803978,
     1.7071067811865475 0.2928932188134525, 1.5555702330196022 0.1685303876974548,
     1.3826834323650898 0.0761204674887133, 1.1950903220161284 0.0192147195967696,
     1 0, 0.8049096779838718 0.0192147195967696, 0.6173165676349103 0.0761204674887133,
    0.444429766980398 0.1685303876974545, 0.2928932188134525 0.2928932188134524,
     0.1685303876974546 0.4444297669803978, 0.0761204674887133 0.6173165676349102,
     0.0192147195967696 0.8049096779838714, 0 0.9999999999999999,
     0.0192147195967696 1.1950903220161284, 0.0761204674887132 1.3826834323650896,
     0.1685303876974545 1.555570233019602, 0.2928932188134523 1.7071067811865475,
     0.4444297669803978 1.8314696123025453, 0.6173165676349097 1.9238795325112865,
     0.8049096779838714 1.9807852804032304, 0.9999999999999998 2,
     1.1950903220161284 1.9807852804032304, 1.38268343236509 1.9238795325112865,
     1.5555702330196017 1.8314696123025453, 1.7071067811865475 1.7071067811865477,
     1.8314696123025453 1.5555702330196022, 1.9238795325112865 1.3826834323650905,
     1.9807852804032304 1.1950903220161286, 2 1))
```