

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# openCypher를 사용하여 Neptune 그래프에 액세스
<a name="access-graph-opencypher"></a>

Neptune은 현재 그래프 데이터베이스를 사용하는 개발자들 사이에서 가장 많이 사용되는 쿼리 언어 중 하나인 openCypher를 사용한 그래프 애플리케이션 구축을 지원합니다. 개발자, 비즈니스 분석가, 데이터 과학자는 openCypher의 SQL에서 영감을 받은 구문을 선호합니다. 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으로 마이그레이션하는 경우 자세한 내용은 [Neo4j에 대한 Neptune의 호환성](migration-compatibility.md) 및 [Neptune의 OpenCypher에서 실행되도록 Cypher 쿼리를 재작성](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은 설계상 꼭 필요하지만, openCypher의 선언적 구문은 SQL 또는 SPARQL을 사용해 본 사람에게는 더 익숙하게 느껴질 수 있습니다. Jupyter Notebook에서 Python을 사용하는 데이터 과학자에게는 Gremlin이 더 익숙할 수 있지만, openCypher는 SQL에 대한 배경 지식이 있는 비즈니스 사용자에게 더 직관적일 수 있습니다.

좋은 소식은 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 및/또는 openCypher 쿼리를 사용하여 그래프에 속성 데이터를 추가할 수도 있습니다.

Cypher 쿼리 언어를 학습하는 데 사용할 수 있는 온라인 자습서가 많이 있습니다. 여기서는 openCypher 쿼리의 몇 가지 간단한 예제를 통해 언어에 대한 아이디어를 얻을 수 있지만, openCypher를 사용하여 Neptune 그래프를 쿼리하기 시작하는 가장 쉽고 좋은 방법은 [Neptune 워크벤치](graph-notebooks.md)의 openCypher 노트북을 사용하는 것입니다. 워크벤치는 오픈 소스로 제공되며, GitHub의 [https://github.com/aws-samples/amazon-neptune-samples](https://github.com/aws-samples/amazon-neptune-samples/)에서 호스팅됩니다.

openCypher 노트북은 GitHub [Neptune 그래프 노트북 리포지토리](https://github.com/aws/graph-notebook/tree/main/src/graph_notebook/notebooks)에서 찾을 수 있습니다. 그중에서도 [항공 노선 시각화 자료](https://github.com/aws/graph-notebook/blob/main/src/graph_notebook/notebooks/02-Visualization/Air-Routes-openCypher.ipynb)와 openCypher용 [영국 프리미어리그 팀](https://github.com/aws/graph-notebook/blob/main/src/graph_notebook/notebooks/02-Visualization/EPL-openCypher.ipynb) 노트북을 확인해 보세요.

openCypher에서 처리하는 데이터는 정렬되지 않은 일련의 키/값 맵의 형태를 취합니다. 이러한 맵을 수정, 조작 및 강화하는 주요 방법은 키/값 페어에서 패턴 매칭, 삽입, 업데이트, 삭제와 같은 작업을 수행하는 절을 사용하는 것입니다.

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 상태 서블릿 및 상태 엔드포인트
<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를 할당할 수 있습니다([Neptune Gremlin 또는 SPARQL 쿼리에 사용자 지정 ID 주입](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
  }
  ```

# Amazon Neptune OpenCypher HTTPS 엔드포인트
<a name="access-graph-opencypher-queries"></a>

**Topics**
+ [HTTPS 엔드포인트에서 쿼리를 읽고 쓸 수 있는 OpenCypher](#access-graph-opencypher-queries-read-write)
+ [기본 OpenCypher JSON 결과 형식](#access-graph-opencypher-queries-results-simple-JSON)
+ [멀티파트 OpenCypher 응답을 위한 선택적 HTTP 후행 헤더](#optional-http-trailing-headers)

**참고**  
Neptune은 현재 REST API 요청에 대해 HTTP/2를 지원하지 않습니다. 클라이언트는 엔드포인트에 연결할 때 HTTP/1.1을 사용해야 합니다.

## HTTPS 엔드포인트에서 쿼리를 읽고 쓸 수 있는 OpenCypher
<a name="access-graph-opencypher-queries-read-write"></a>

OpenCypher HTTPS 엔드포인트는 `GET` 및 `POST` 메서드를 모두 사용하는 읽기 및 업데이트 쿼리를 지원합니다. `DELETE` 및 `PUT` 메서드는 지원되지 않습니다.

다음은 `curl` 명령과 HTTPS를 사용하여 OpenCypher 엔드포인트에 연결하는 방법입니다. 사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(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"
```

다음은 `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>

다음 JSON 형식은 기본적으로 반환되거나 요청 헤더를 `Accept: application/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이 응답 청크의 후행 헤더에 2개의 새 헤더 필드를 포함하게 됩니다.
+  `X-Neptune-Status` – 응답 코드와 짧은 이름이 차례로 들어 있습니다. 예를 들어, 성공하면 후행 헤더는 `X-Neptune-Status: 200 OK`와 같습니다. 장애가 발생한 경우 응답 코드는 `X-Neptune-Status: 500 TimeLimitExceededException`과 같은 Neptune 엔진 오류 코드가 됩니다.
+  `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`)는 openCypher 쿼리를 제출하기 위한 [ExecuteOpenCypherQuery](https://docs.aws.amazon.com/neptune/latest/data-api/API_ExecuteOpenCypherQuery.html) 작업을 제공합니다. openCypher 

이러한 예제는 Neptune DB 클러스터와 동일한 Virtual Private Cloud(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 AWS SDK 예제
<a name="access-graph-opencypher-sdk-examples"></a>

다음 예제에서는 `neptunedata` 클라이언트를 설정하고, openCypher 쿼리를 실행하고, 결과를 인쇄하는 방법을 보여줍니다. *YOUR\$1NEPTUNE\$1HOST* 및 *YOUR\$1NEPTUNE\$1PORT*를 Neptune DB 클러스터의 엔드포인트 및 포트로 바꿉니다.

**클라이언트 측 제한 시간 및 재시도 구성**  
SDK 클라이언트 제한 시간은 *클라이언트*가 응답을 기다리는 시간을 제어합니다. 서버에서 쿼리가 실행되는 기간을 제어하지 않습니다. 서버가 완료되기 전에 클라이언트 시간이 초과되면 클라이언트가 결과를 검색할 방법이 없는 동안 Neptune에서 쿼리가 계속 실행될 수 있습니다.  
클라이언트 측 읽기 제한 시간을 `0` (제한 시간 없음) 또는 Neptune DB 클러스터의 서버 측 [neptune\$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) 따라 AWS SDK for Java를 설정합니다.

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) 따라 AWS SDK for 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/)는 Neo4j에서 처음 개발했으며, Creative Commons 3.0 [Attribution-ShareAlike](https://creativecommons.org/licenses/by-sa/3.0/) 라이선스에 따라 사용이 허가된 문 중심의 클라이언트/서버 프로토콜입니다. 클라이언트 중심적이므로, 메시지 교환은 클라이언트가 항상 시작합니다.

Neo4j의 Bolt 드라이버로 Neptune에 연결하려면 `bolt` URI 체계를 사용하여 URL과 포트 번호를 클러스터 엔드포인트로 바꾸면 됩니다. 단일 Neptune 인스턴스가 실행 중인 경우 read\$1write 엔드포인트를 사용하세요. 여러 인스턴스가 실행 중인 경우 라이터용 드라이버와 모든 읽기 전용 복제본용 드라이버를 2개 사용하는 것이 좋습니다. 기본 엔드포인트가 2개만 있는 경우에는 read\$1write와 read\$1only 드라이버로도 충분하지만, 사용자 지정 엔드포인트도 있는 경우에는 각 엔드포인트에 대한 드라이버 인스턴스를 생성하는 것이 좋습니다.

**참고**  
Bolt 사양에는 Bolt가 TCP 또는 WebSocket을 사용하여 연결할 수 있다고 명시되어 있지만, Neptune은 Bolt에 대한 TCP 연결만 지원합니다.

Neptune은 t3.medium 및 t4g.medium을 제외한 모든 인스턴스 크기에서 최대 1,000개의 동시 Bolt 연결을 허용합니다. t3.medium 및 t4g.medium 인스턴스에서는 512개의 연결만 허용됩니다.

Bolt 드라이버를 사용하는 다양한 언어의 openCypher 쿼리 예제는 Neo4j [드라이버 및 언어 안내서](https://neo4j.com/developer/language-guides/) 설명서를 참조하세요.

**중요**  
Python, .NET, JavaScript 및 Golang용 Neo4j Bolt 드라이버는 처음에 AWS Signature v4 인증 토큰의 자동 갱신을 지원하지 않았습니다. 즉, 서명이 만료된 후(보통 5분 후) 드라이버가 인증에 실패했고 후속 요청도 실패했습니다. 아래 Python, .NET, JavaScript, 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), [Neo4j JavaScript 드라이버 문제 \$1993](https://github.com/neo4j/neo4j-javascript-driver/issues/993), [Neo4j goLang 드라이버 문제 \$1429](https://github.com/neo4j/neo4j-go-driver/issues/429)를 참조하세요.  
드라이버 버전 5.8.0부터 Go 드라이버에 대한 새로운 미리 보기 재인증 API가 출시되었습니다([v5.8.0 - 재인증에 대한 피드백](https://github.com/neo4j/neo4j-go-driver/discussions/482) 참조).

## Java와 함께 Bolt를 사용하여 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에 연결하려면 다음과 같은 코드를 사용하여 클러스터의 기본/라이터 인스턴스용 드라이버 인스턴스를 생성하세요.

```
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>

.NET에서 Bolt를 사용하여 openCypher 쿼리를 만들려면 먼저 NuHet을 사용하여 Neo4j 드라이버를 설치해야 합니다. 동기 호출을 하려면 다음과 같은 `.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();
      }
    }
  }
}
```

## IAM 인증과 함께 Bolt를 사용하는 Java openCypher 쿼리 예제
<a name="access-graph-opencypher-bolt-java-iam-auth"></a>

아래 Java 코드는 IAM 인증과 함께 Bolt를 사용하여 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);
  }
}
```

## IAM 인증과 함께 Bolt를 사용하는 Python openCypher 쿼리 예제
<a name="access-graph-opencypher-bolt-python-iam-auth"></a>

아래의 Python 클래스를 사용하면 IAM 인증과 함께 Bolt를 사용하여 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 코드는 AWS SDK for JavaScript 버전 3 및 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();
}
```

## IAM 인증과 함께 Bolt를 사용하는 .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);
    }
  }
}
```

IAM 인증과 함께 Bolt를 사용하여 .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();
    }
  }
}
```

## IAM 인증과 함께 Bolt를 사용하는 Golang openCypher 쿼리 예제
<a name="access-graph-opencypher-bolt-golang-iam-auth"></a>

아래의 Golang 패키지는 IAM 인증과 함께 Bolt를 사용하여 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 엔드포인트와 마찬가지로 Bolt 연결 앞에 [Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html)를 사용할 수 없습니다.
+ Neptune이 Bolt 연결에 사용하는 포트는 DB 클러스터의 포트입니다.
+ 전달된 Bolt 서문을 기반으로 Neptune 서버는 가장 적합한 Bolt 버전(1, 2, 3 또는 4.0)을 선택합니다.
+ 클라이언트가 언제든지 열 수 있는 Neptune 서버에 대한 최대 연결 수는 1,000개입니다.
+ 클라이언트가 쿼리 후 연결을 끊지 않으면 해당 연결을 사용하여 다음 쿼리를 실행할 수 있습니다.
+ 하지만 연결이 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은 속성이 0인 노드와 관계를 지원합니다.
+ Neptune은 openCypher 사양에도 포함되어 있지 않은 metaproperties를 지원하지 않습니다.
+ 그래프의 속성은 Gremlin을 사용하여 생성한 경우 다중 값이 될 수 있습니다. 즉, 노드 또는 관계 속성에는 값이 하나만 있는 것이 아니라 여러 개의 다른 값이 있을 수 있습니다. Neptune은 다중 값 속성을 정상적으로 처리하도록 openCypher 시맨틱을 확장했습니다.

지원되는 데이터 유형은 [openCypher 데이터 형식](bulk-load-tutorial-format-opencypher.md)에 설명되어 있습니다. 그러나 현재로서는 openCypher 그래프에 `Array` 속성값을 삽입하지 않는 것이 좋습니다. 대량 로더를 사용하여 배열 속성값을 삽입할 수 있지만, 현재 Neptune openCypher 릴리스에서는 배열 속성값을 단일 목록 값이 아닌 다중 값 속성 세트로 취급합니다.

다음은 이번 릴리스에서 지원되는 데이터 유형 목록입니다.
+ `Bool`
+ `Byte`
+ `Short`
+ `Int` 
+ `Long`
+ `Float`(plus/minus Infinity/NaN 포함, INF 제외)
+ `Double`(plus/minus Infinity/NaN 포함, INF 제외)
+ `DateTime` 
+ `String`

# openCypher `explain` 기능
<a name="access-graph-opencypher-explain"></a>

openCypher `explain` 기능은 Neptune 엔진에 의해 수행되는 실행 접근 방식을 이해하는 데 도움이 되는 Amazon 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**   –   특정 변수에 저장된 값에 대해 인수 섹션에서 지정한 함수를 실행합니다.

**DFEBindRelation**   –   지정된 이름을 가진 변수를 함께 바인딩합니다.

**DFEChunkLocalSubQuery**   –   수행 중인 하위 쿼리에 대한 래퍼 역할을 하는 비차단 작업입니다.

**DFEDistinctColumn**   –   지정된 변수를 기반으로 입력 값의 고유한 하위 세트를 반환합니다.

**DFEDistinctRelation**   –   지정된 변수를 기반으로 입력 솔루션의 고유한 하위 세트를 반환합니다.

**DFEDrain**   –   하위 쿼리의 종료 단계 역할을 하기 위해 하위 쿼리의 끝에 나타납니다. 솔루션 수는 `Units In`으로 기록됩니다. `Units Out`은 항상 0입니다.

**DFEForwardValue**   –   모든 입력 청크를 출력 청크로 직접 복사하여 다운스트림 연산자에 전달합니다.

**DFEGroupByHashIndex**   –   이전에 계산된 해시 인덱스를 기반으로 입력 솔루션에 대해 그룹별 작업을 수행합니다(`DFEHashIndexBuild` 작업 사용). 주어진 입력은 각 입력 솔루션에 대한 그룹 키가 포함된 열로 확장되어 출력됩니다.

**DFEHashIndexBuild**   –   부작용으로 변수 집합에 대한 해시 인덱스를 작성합니다. 이 해시 인덱스는 일반적으로 이후 작업에서 재사용됩니다. 이 해시 인덱스를 사용할 수 있는 위치는 `DFEHashIndexJoin` 또는 `DFEGroupByHashIndex` 섹션을 참조하세요.

**DFEHashIndexJoin**   –   이전에 작성한 해시 인덱스를 기준으로 수신 솔루션에 대한 조인을 수행합니다. 이 해시 인덱스를 작성할 수 있는 위치는 `DFEHashIndexBuild` 섹션을 참조하세요.

**DFEJoinExists**   –   왼쪽 및 오른쪽 입력 관계식을 사용하고, 지정된 조인 변수에 정의된 대로 오른쪽 관계식에 해당 값이 있는 왼쪽 관계식의 값을 유지합니다.

****   –   이 작업은 하위 쿼리의 래퍼 역할을 하는 비차단 작업이므로, 반복 실행하여 루프에서 사용할 수 있습니다.

**DFEMergeChunks**   –   업스트림 연산자의 청크를 단일 솔루션 청크로 결합하여 다운스트림 연산자에 전달하는 차단 연산입니다(`DFESplitChunks`의 역연산).

**DFEMinus**   –   왼쪽 및 오른쪽 입력 관계식을 사용하고, 지정된 조인 변수에 정의된 대로 오른쪽 관계식에 해당 값이 없는 왼쪽 관계식의 값을 유지합니다. 두 관계식 모두에서 변수가 겹치지 않는 경우 이 연산자는 단순히 왼쪽 입력 관계식을 반환합니다.

**DFENotExists**   –   왼쪽 및 오른쪽 입력 관계식을 사용하고, 지정된 조인 변수에 정의된 대로 오른쪽 관계식에 해당 값이 없는 왼쪽 관계식의 값을 유지합니다. 두 관계식 모두에서 변수가 겹치지 않는 경우 이 연산자는 빈 관계식을 반환합니다.

**DFEOptionalJoin**   –   왼쪽 외부 조인(OPTIONAL 조인이라고도 함)을 수행합니다. 오른쪽에 조인 파트너가 하나 이상 있는 왼쪽의 솔루션은 조인되고, 오른쪽에 조인 파트너가 없는 왼쪽의 솔루션은 그대로 전달됩니다. 이는 차단 작업입니다.

**DFEPipelineJoin**   –   `pattern` 인수로 정의된 튜플 패턴에 대해 입력을 조인합니다.

**DFEPipelineRangeCount**   –   지정된 패턴과 일치하는 솔루션 수를 세고 개수 값이 포함된 단일 단항 솔루션을 반환합니다.

**DFEPipelineScan**   –   열에 지정된 필터를 사용하거나 사용하지 않고 데이터베이스에서 지정된 `pattern` 인수를 스캔합니다.

**DFEProject**   –   여러 입력 열을 가져와서 원하는 열만 투영합니다.

**DFEreduce**   –   지정된 변수에 대해 지정된 집계 함수를 수행합니다.

**DFERelationalJoin**   –   병합 조인을 사용하여 지정된 패턴 키를 기반으로 이전 연산자의 입력을 조인합니다. 이는 차단 작업입니다.

**DFERouteChunks**   –   단일 수신 엣지에서 입력 청크를 가져와 여러 개의 발신 엣지를 따라 해당 청크를 라우팅합니다.

**DFESelectRows**   –   이 연산자는 왼쪽 입력 관계식 솔루션에서 선택적으로 행을 가져와 다운스트림 연산자에 전달합니다. 연산자의 오른쪽 입력 관계식에 제공된 행 식별자를 기반으로 행이 선택됩니다.

**DFESerialize**   –   쿼리의 최종 결과를 JSON 문자열로 직렬화하여 각 입력 솔루션을 적절한 변수 이름에 매핑합니다. 노드 및 엣지 결과의 경우 이러한 결과는 엔터티 속성 및 메타데이터의 맵으로 직렬화됩니다.

**DFESort**   –   입력 관계식을 가져와 제공된 정렬 키를 기반으로 정렬된 관계식을 생성합니다.

**DFESplitByGroup**   –   한 수신 엣지의 각 단일 입력 청크를 다른 수신 엣지의 해당 입력 청크에서 행 ID로 식별되는 행 그룹에 해당하는 더 작은 출력 청크로 분할합니다.

**DFESplitChunks**   –   각 단일 입력 청크를 더 작은 출력 청크로 분할합니다(`DFEMergeChunks`의 역연산).

**DFEStreamingHashIndexBuild**   –   `DFEHashIndexBuild`의 스트리밍 버전입니다.

**DFEStreamingGroupByHashIndex**   –   `DFEGroupByHashIndex`의 스트리밍 버전입니다.

**DFE SubQuery**   –   이 연산자는 모든 계획의 시작 부분에 나타나며 [DFE 엔진](neptune-dfe-engine.md)에서 실행되는 계획의 일부, 즉 openCypher의 전체 계획을 캡슐화합니다.

**DFESymmetricHashJoin**   –   해시 조인을 사용하여 지정된 패턴 키를 기반으로 이전 연산자의 입력을 조인합니다. 이는 비차단 작업입니다.

**DFESync**   –   이 연산자는 비차단 계획을 지원하는 동기화 연산자입니다. 두 개의 수신 엣지에서 솔루션을 가져와 해당 다운스트림 엣지로 전달합니다. 동기화를 위해 이러한 엣지 중 하나를 따라 입력이 내부적으로 버퍼링될 수 있습니다.

**DFEtee**   –   동일한 솔루션 세트를 여러 연산자에 보내는 분기 연산자입니다.

**DFETermResolution**   –   입력에 대해 로컬화 또는 글로벌화 작업을 수행하여 로컬화된 식별자 또는 글로벌화된 식별자의 열을 각각 생성합니다.

****   –   입력 열의 값 목록을 개별 요소로 출력 열에 펼칩니다.

**DFEUnion**   –   둘 이상의 입력 관계식을 가져와서 원하는 출력 스키마를 사용하여 통합된 관계식을 생성합니다.

**SolutionInjection**   –   설명 출력의 다른 모든 항목보다 먼저 나타나며 Units Out 열의 값은 1입니다. 하지만 비운영 기능을 하며, 실제로 DFE 엔진에 솔루션을 주입하지 않습니다.

**TermResolution**   –   계획이 끝날 때 표시되며 Neptune 엔진의 객체를 openCypher 객체로 변환합니다.

## openCypher `explain` 출력의 열
<a name="access-graph-opencypher-explain-columns"></a>

Neptune이 openCypher Explain 출력으로 생성하는 쿼리 계획 정보에는 행당 하나의 연산자가 있는 표가 포함됩니다. 이 표에는 다음과 같은 열이 있습니다.

**ID**   –   계획에서 연산자의 숫자 ID입니다.

**Out \$11**(및 **Out \$12**)   –   연산자의 다운스트림에 있는 연산자의 ID입니다. 다운스트림 연산자는 최대 2개까지 있을 수 있습니다.

**Name**   –   연산자의 이름입니다.

**Arguments**   –   연산자에 대한 모든 관련 세부 정보입니다. 여기에는 입력 스키마, 출력 스키마, 패턴(`PipelineScan` 및 `PipelineJoin`용) 등이 포함됩니다.

**Mode**   –   연산자의 기본 동작을 설명하는 레이블입니다. 이 열은 대부분 비어 있습니다(`-`). 한 예외는 `TermResolution`으로, 모드는 `id2value_opencypher`이며 ID에서 openCypher 값까지의 해상도를 나타냅니다.

**Units In**   –   연산자에 입력으로 전달된 솔루션 수입니다. 업스트림 연산자가 없는 연산자(예: 정적 값이 삽입되지 않은 `DFEPipelineScan`, `SolutionInjections`, `DFESubquery` 등)는 값이 0입니다.

**Units Out**   –   연산자의 출력으로 생성된 솔루션 수입니다. `DFEDrain`은 배출되는 솔루션 수가 `Units In` 및 `Units Out`에 항상 0으로 기록되는 특수한 경우입니다.

**Ratio**   –   `Units Out` 대 `Units In`의 비율입니다.

**Time (ms)**   –   연산자가 소비한 CPU 시간(밀리초)입니다.

## openCypher Explain 출력의 기본 예제
<a name="access-graph-opencypher-explain-basic-example"></a>

다음은 openCypher `explain` 출력의 기본 예제입니다. 쿼리는 기본 ASCII 출력 형식의 `details` 모드를 사용하여 `explain`을 간접 호출하는 공항 코드 `ATL`이 있는 노드에 대한 항공 경로 데이터 세트의 단일 노드 조회입니다.

```
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`은 유닛 1개가 출력된 상태로 다른 모든 항목보다 먼저 나타납니다. 실제로 어떤 솔루션도 주입하지 않는다는 점에 유의하세요. 다음 연산자인 `DFESubquery`의 단위는 0이라는 것을 알 수 있습니다.

최상위 수준에서 `SolutionInjection` 이후는 `DFESubquery` 및 `TermResolution` 연산자입니다. `DFESubquery`는 [DFE 엔진](neptune-dfe-engine.md)으로 푸시되는 쿼리 실행 계획의 일부를 캡슐화합니다. openCypher 쿼리의 경우 전체 쿼리 계획이 DFE에 의해 실행됩니다. 쿼리 계획의 모든 연산자가 `DFESubquery`에서 참조하는 `subQuery1` 내에 중첩됩니다. 유일한 예외는 내부 ID를 완전히 직렬화된 openCypher 객체로 구체화하는 `TermResolution`입니다.

DFE 엔진으로 푸시되는 모든 연산자의 이름은 `DFE` 접두사로 시작합니다. 위에서 언급한 바와 같이 전체 openCypher 쿼리 계획은 DFE에 의해 실행되므로, 결과적으로 최종 `TermResolution` 연산자를 제외한 모든 연산자는 `DFE`로 시작됩니다.

`subQuery1` 내에는 메모리 경계 메커니즘에서 실행되는 푸시된 실행 계획의 일부를 캡슐화하는 `DFEChunkLocalSubQuery` 또는 `DFELoopSubQuery` 연산자가 0개 이상 있을 수 있습니다. 여기 `DFEChunkLocalSubQuery`에는 하위 쿼리에 대한 입력으로 사용되는 하나의 `SolutionInjection`이 포함되어 있습니다. 출력에서 해당 하위 쿼리의 테이블을 찾으려면 `DFEChunkLocalSubQuery` 또는 `DFELoopSubQuery` 연산자의 `Arguments` 열에 지정된 `subQuery=graph URI`를 검색합니다.

`subQuery1`에서 `ID`가 0인 `DFEPipelineScan`은 지정된 `pattern`에 대해 데이터베이스를 스캔합니다. 패턴은 속성 `code`가 변수 `?n_code2`로 저장된 엔터티를 모든 레이블에서 스캔합니다. `airport`를 `n:airport`에 추가하여 특정 레이블에서 필터링할 수 있습니다. `inlineFilters` 인수는 `ATL`과 동일한 `code` 속성에 대한 필터링을 보여줍니다.

그런 다음 `DFEChunkLocalSubQuery` 연산자는 `DFEPipelineJoin`이 포함된 하위 쿼리의 중간 결과에 조인합니다. 그러면 이전 `DFEPipelineScan`에서 `code` 속성을 가진 엔터티를 스캔하므로, `?n`이 실제로 노드임을 보장합니다.

# 제한이 있는 관계 조회에 대한 `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`은 `Arguments` 열과 같이 10으로 제한된 출력 솔루션을 수집합니다. `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 노드는 중복 레이블 ["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)는 정적 값 `-10`에 대한 솔루션 주입을 수행하며, 이 값은 변수 `?100`에 저장됩니다.

`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`에서 `...graph_1` 하위 쿼리를 삽입하는 `DFEPipelineScan`(ID 0) 및 `DFEChunkLocalSubQuery`(ID 1)는 `YPO` 코드가 있는 노드에 대한 스캔을 담당합니다.

`subQuery1`에서 `...graph_2` 하위 쿼리를 주입하는 `DFEChunkLocalSubQuery`(ID 2)는 `LAX` 코드가 있는 노드에 대한 스캔을 담당합니다.

`subQuery1`에서 `DFEChunkLocalSubQuery`(ID 3)는 `DFELoopSubQuery`(ID 17)를 포함하는 `...graph3` 하위 쿼리를 삽입하고 `...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();
}
```

변형 쿼리의 일부로 이루어진 읽기는 [Neptune 변형 트랜잭션](transactions-neptune.md#transactions-neptune-mutation)에 대한 일반적인 보장과 함께 `READ COMMITTED` 격리 상태에서 실행됩니다.

특별히 세션 구성을 전달했는지 여부에 관계없이 트랜잭션은 항상 쓰기 트랜잭션으로 처리됩니다.

충돌에 대해서는 [잠금-대기 제한 시간을 이용한 충돌 해결](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}`는 \$1scope\$1는 힌트가 적용되는 범위를 `Query` 또는 `Clause`으로 정의합니다.

    범위 값이 `Query`인 경우 쿼리 힌트가 전체 쿼리(쿼리 수준)에 적용됩니다.

    범위 값이 `Clause`인 경우 쿼리 힌트가 힌트 앞에 위치한 절(절 수준)에 적용됩니다.

1.  `{hint}`는 적용 중인 쿼리 힌트의 이름입니다.

1.  `{value}`는 `{hint}`의 인수입니다.

 값은 대/소문자를 구분하지 않을 수 있습니다.

 예를 들어 쿼리에 대해 쿼리 계획 캐시를 활성화하려면 

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

**참고**  
 현재 **쿼리** 범위 쿼리 힌트 **PLANCACHE**, **TIMEOUTMILLISECONDS** 및 **assumeConsistentDataTypes**가 지원됩니다. 지원되는 쿼리 힌트는 다음과 같습니다.

**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이고 유형이 짧은 모든 속성을 조회할 때 유형 승격 의미 체계에서 값이 10인 속성과도 일치합니다. 경우에 따라 형식 캐스팅은 오버헤드를 유발하여 형식 캐스팅이 수행되지 않은 경우보다 쿼리 계획이 덜 효율적일 수 있습니다. 특히 데이터에서 데이터 유형이 일관되게 사용되는 경우(예: 모든 사람의 연령이 긴 값으로 저장되는 경우) 유형 프로모션을 수행하면 쿼리 결과에 영향을 주지 않고 오버헤드가 발생합니다.

 데이터베이스에 저장된 숫자 속성 데이터 값의 데이터 형식이 일관된 것으로 알려진 경우 최적화를 허용하기 위해 `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에서는 [openCypher 사양 규정 준수](feature-opencypher-compliance.md)에서 자세히 설명하는 것처럼 여전히 [Query 쿼리 언어 참조, 버전 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf)에 나와 있는 모든 내용을 지원하지는 않습니다. 향후 릴리스에서는 이러한 제한 사항 중 상당수가 해결될 것으로 예상됩니다.

# 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 | 작업 종료(기한 초과) | 예 | DB 클러스터 파라미터 그룹에서 쿼리 제한 시간을 늘리거나 [요청을 재시도하세요](https://docs.aws.amazon.com/general/latest/gr/api-retries.html). | 
| 500 | 작업 종료(사용자에 의한 취소) | 예 | 요청을 다시 시도하세요. | 
| 500 | 데이터베이스 재설정이 진행 중입니다. 클러스터를 사용할 수 있게 되면 쿼리를 다시 시도하세요. | 예 | 재설정이 완료되면 다시 시도하세요. | 
| 500 | 동시 작업 충돌로 인해 작업이 실패했습니다. 다시 시도하세요. 트랜잭션이 현재 롤백 중입니다. | 예 | [지수 백오프 및 재시도 전략](best-practices-opencypher-retry-logic.md)을 사용하여 재시도하세요. | 
| 400 | *(작업 이름)* 작업/기능이 지원되지 않는 예외 | 아니요 | 지정한 작업을 지원하지 않습니다. | 
| 400 | 읽기 전용 복제본에서 openCypher 업데이트를 시도했습니다. | 아니요 | 대상 엔드포인트를 라이터 엔드포인트로 변경합니다. | 
| 400 | MalformedQueryException(Neptune은 내부 구문 분석기 상태를 표시하지 않음) | 아니요 | 쿼리 구문을 수정하고 다시 시도하세요. | 
| 400 | 노드에 아직 관계가 있으므로, 노드를 삭제할 수 없습니다. 이 노드를 삭제하려면 먼저 노드의 관계를 삭제해야 합니다. | 아니요 | `MATCH (n) DELETE n`을 사용하는 대신 `MATCH(n) DETACH DELETE(n)`를 사용하세요. | 
| 400 | 잘못된 작업: 노드의 마지막 레이블을 제거하려고 합니다. 노드에는 레이블이 하나 이상 있어야 합니다. | 아니요 | Neptune 사용 시 모든 노드에 적어도 하나의 레이블이 있어야 하며, 명시적 레이블 없이 노드를 생성하면 기본 레이블 `vertex`가 할당됩니다. 마지막 레이블이 삭제되지 않도록 쿼리 및/또는 애플리케이션 로직을 변경하세요. 새 레이블을 설정한 다음 기존 레이블을 제거하여 노드의 싱글톤 레이블을 업데이트할 수 있습니다. | 
| 500 | 최대 요청 수를 지키지 못했습니다(connId = \$1\$1의 경우 ConfiguredQueueCapacity=\$1\$1) | 예 | 스택과 프로토콜에 관계없이 현재는 8,192개의 동시 요청만 처리할 수 있습니다. | 
| 500 | 최대 연결 한도를 지키지 못했습니다. | 예 | 인스턴스당 1,000개의 동시 Bolt 연결만 허용됩니다(HTTP의 경우 제한 없음). | 
| 400 | [one of: Node, Relationship or Path]가 예상되고 리터럴을 받았습니다. | 아니요 | 올바른 인수, 올바른 쿼리 구문을 전달했는지 확인한 다음 다시 시도하세요. | 
| 400 | 속성값은 단순 리터럴이어야 합니다. Or: Expected Map for Set 속성이지만, 찾지 못했습니다. | 아니요 | SET 절은 복합 형식이 아닌 단순 리터럴만 허용합니다. | 
| 400 | 삭제를 위해 전달된 것으로 확인된 엔터티를 찾을 수 없습니다. | 아니요 | 삭제하려는 엔터티가 데이터베이스에 있는지 확인하세요. | 
| 400 | 사용자가 데이터베이스에 액세스할 수 없습니다. | 아니요 | 사용 중인 IAM 역할의 정책을 확인하세요. | 
| 400 | 요청의 일부로 전달된 토큰이 없습니다. | 아니요 | 올바르게 서명된 토큰은 IAM 지원 클러스터에서 쿼리 요청의 일부로 전달되어야 합니다. | 
| 400 | 오류 메시지가 전파됩니다. | 아니요 | 요청 ID를 사용하여 AWS Support에 문의하세요. | 
| 500 | 작업 종료(내부 오류) | 예 | 요청 ID를 사용하여 AWS Support에 문의하세요. | 

# 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 쿼리 내에서 Amazon S3에서 직접 CSV 또는 Parquet 데이터를 읽는 `neptune.read()` 함수를 지원합니다. 쿼리 전에 데이터를 가져오는 대량 로더와 달리는 쿼리 실행 시 Amazon S3 데이터에 `neptune.read()` 액세스합니다.

전체 설명서는 섹션을 참조하세요[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()` 함수를 구현합니다. 문자열 리터럴 목록과 문자열 구분 기호를 사용하여 문자열 리터럴을 생성합니다. 2가지 인수를 사용합니다.
+ 첫 번째 인수는 문자열 리터럴 목록입니다.
+ 두 번째 인수는 0자, 1자 또는 2자 이상의 문자로 구성될 수 있는 구분 기호 문자열입니다.

예제:

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

## Neptune 전용 `removeKeyFromMap()` 함수
<a name="opencypher-compliance-removeKeyFromMap-function"></a>

Neptune 데이터베이스 및 Neptune Analytics에서 사용할 수 있습니다.

Neptune은 openCypher 사양에 없는 `removeKeyFromMap()` 함수를 구현합니다. 맵에서 지정된 키를 제거하고 생성된 새 맵을 반환합니다.

함수는 2가지 인수를 사용합니다.
+ 첫 번째 인수는 키를 제거할 맵입니다.
+ 두 번째 인수는 맵에서 제거할 키입니다.

이 `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` 값을 지정할 수 있게 되었습니다. 이를 통해 시스템에서 생성한 UUID 대신 사용자에게 친숙한 문자열을 할당하여 노드와 관계를 식별할 수 있습니다.

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` 절에 사용자 지정 ID를 사용하는 예제입니다.

```
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 하위 쿼리 내에서 사용되는 `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?)` 

 `text` 범위에서 `from` 오프셋(포함)부터 `to` 오프셋(제외)까지의 범위 내에서 `lookup`의 첫 번째 발생 위치의 인덱스를 반환합니다. `to`가 -1인 경우 범위는 `text`의 끝까지 계속됩니다. 인덱싱은 0을 기준으로 하며 Unicode scalar 값(대체 코드 포인트가 아닌)으로 표현됩니다.

```
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?)` 

 `second`에서 요소를 제외하는 `first`의 모든 고유 요소가 포함된 새 목록을 반환합니다.

```
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` 맵 인수 또는 이러한 여러 맵 목록을 가져옵니다(일부 경우 선택 사항).

```
{ 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?)` 

 각 `key` 속성의 값을 기준으로 노드 요소를 정렬하여 `coll` 입력 목록의 정렬된 버전을 반환합니다.

```
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
    }]
}
```

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

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

 `datetime` 또는 `date` 값(1\$112)에서 월을 반환합니다. `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
    }]
}
```

### 년
<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은 Amazon S3에서 데이터를 `neptune.read` 읽은 다음 데이터를 사용하여 openCypher 쿼리(읽기, 삽입, 업데이트)를 실행하는 `CALL` 절차를 지원합니다. 프로시저는 파일의 각 행을 선언된 결과 변수 행으로 생성합니다. 호출자의 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
...
```

**입력**
+  **source**(필수) - **단일** 객체에 대한 Amazon S3 URI입니다. 여러 객체에 대한 Amazon S3 접두사는 지원되지 않습니다.
+  **format**(필수) - `parquet` 및 `csv`가 지원됩니다.
  +  지원되는 Parquet 형식에 대한 자세한 내용은에서 확인할 수 있습니다[지원되는 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는 다음을 반환합니다.
+  **행** - type:Map 
  +  파일의 각 행. 여기서 키는 열이고 값은 각 열에 있는 데이터입니다.
  +  속성 액세스()와 같은 각 열의 데이터에 액세스할 수 있습니다`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()` 요청의 메모리 사용량 및 성능은 파일 크기, 열 수, 행 수, 파일 형식과 같은 다양한 요인의 영향을 받습니다. 구조에 따라 작은 파일(예: CSV 파일 100MB 이하, Parquet 파일 20MB 이하)은 대부분의 프로덕션에 적합한 인스턴스 유형에서 안정적으로 작동할 수 있는 반면, 큰 파일은 작은 인스턴스 유형이 제공할 수 없는 상당한 메모리가 필요할 수 있습니다.

이 기능을 테스트할 때는 작은 파일로 시작하고 인스턴스 크기에 따라 읽기 워크로드를 수용할 수 있도록 점진적으로 확장하는 것이 좋습니다. out-of-memory 예외 또는 인스턴스 재시작으로 이어지는 `neptune.read()` 요청이 있는 경우 파일을 더 작은 청크로 분할하거나 파일 복잡성을 줄이거나 더 큰 인스턴스 유형으로 업그레이드하는 것이 좋습니다.

# parquet를 사용한 쿼리 예제
<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)
```

다음 코드를 실행 AWS CLI 하여에서 `execute-open-cypher-query` 작업을 사용하여 쿼리 예제를 실행할 수 있습니다.

```
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에서 읽은 행 간의 교차 제품 때문에 쿼리가 오래 실행됩니다. `CALL` neptune.read로 쿼리를 시작하는 것이 좋습니다.

## 지원되는 Parquet 열 유형
<a name="access-graph-opencypher-21-extensions-s3-read-parquet-column-types"></a>

**Parquet 데이터 형식:**
+ NULL
+ BOOLEAN
+ FLOAT
+ DOUBLE
+ STRING
+ 부호 있는 정수: UINT8, UINT16, UINT32, UINT64
+ MAP:는 단일 수준만 지원합니다. 중첩을 지원하지 않습니다.
+ 목록:는 한 수준만 지원합니다. 중첩을 지원하지 않습니다.

**Neptune별 데이터 형식:**

CSV 형식의 속성 열 헤더와 달리 Parquet 형식의 속성 열 헤더에는 속성 이름만 있으면 되므로 유형 이름이나 카디널리티가 필요하지 않습니다.

그러나 모든 유형, 날짜 유형, dateTime 유형 및 지오메트리 유형을 포함하여 메타데이터에 주석이 필요한 Parquet 형식의 일부 특수 열 유형이 있습니다. 다음 객체는 이러한 특수 유형의 열을 포함하는 파일에 필요한 메타데이터 주석의 예입니다.

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

다음은 이러한 유형과 관련된 예상 페이로드에 대한 세부 정보입니다.
+ 열 유형 사용자 열에서 모두 지원됩니다. 모든 유형은 지원하는 다른 모든 유형에 대한 "syntactic sugar" 유형입니다. 사용자 열에 여러 유형이 있는 경우 매우 유용합니다. 모든 유형 값의 페이로드는 다음과 같은 json 문자열 목록입니다. 각 개별 json 문자열에 `{"value": "10", "type": "Int"};{"value": "1.0", "type": "Float"}`값 필드와 유형 필드가 있습니다. 모든 열의 카디널리티 값이 설정됩니다. 즉, 열이 여러 값을 수락할 수 있습니다.
  + Neptune은 모든 유형에서 Bool(또는 Boolean), Byte, Short, Int, Long, UnsignedByte, UnsignedShort, UnsignedInt, UnsignedLong, Float, Double, Date, dateTime, String 및 Geometry 유형을 지원합니다.
  + 벡터 유형은 모든 유형에서 지원되지 않습니다.
  + 중첩 모든 유형은 지원되지 않습니다. 예를 들어 `{"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-]hhmm
  + yyyy-MM-ddTHH:mm:ss.SSS[\$1\$1-]hhmm
+ Geometry 열 유형은 사용자 열에서 지원됩니다. 이러한 열의 페이로드에는 WKT(Well-known text) 형식의 문자열로 제공되는 Point 유형의 지오메트리 프리미티브만 포함되어야 합니다. 예를 들어 POINT(30 10)는 유효한 Geometry 값입니다.

## 샘플 parquet 출력
<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)
```

다음 코드를 실행 AWS CLI 하여에서 execute-open-cypher-query 작업을 사용하여 쿼리 예제를 실행할 수 있습니다.

```
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[]
  ```
엣지 속성은 단일 값만 가질 수 있으며 배열 유형이 지정되거나 두 번째 값이 지정되면 오류가 발생합니다. 다음 예제에서는 Int 유형의 age 속성에 대한 열 헤더를 보여줍니다.  

  ```
  age:Int
  ```

파일의 각 행마다 해당 위치에 정수가 있거나 비어 있어야 합니다. 문자열 배열은 허용되지만 배열의 문자열은 백슬래시(`;`)를 사용하여 이스케이프되지 않는 한 세미콜론() 문자를 포함할 수 없습니다`\;`.

## 지원되는 CSV 열 유형
<a name="supported-csv-column-types"></a>
+ **BOOL(또는 BOOLEAN)** - 허용되는 값: true, false. 부울 필드를 나타냅니다. true 이외의 모든 값은 false로 처리됩니다.
+ **FLOAT** - 범위: Infinity, INF, -Infinity, -INF 및 NaN(not-a-number 포함한 32비트 IEEE 754 부동 소수점.
+ **DOUBLE** - 범위: Infinity, INF, -Infinity, -INF 및 NaN(not-a-number 포함한 64비트 IEEE 754 부동 소수점.
+ **문자열** - 
  + 인용 부호는 선택사항입니다. 쉼표, 줄 바꿈 및 캐리지 리턴 문자는 큰따옴표(")로 묶인 문자열에 포함된 경우 자동으로 이스케이프 처리됩니다. 예: "Hello, World".
  + 따옴표를 따옴표로 묶으려면 "Hello ""World"" 행의 두 개를 사용하여 따옴표를 이스케이프할 수 있습니다.
  + 문자열 배열은 허용되지만, 백슬래시(\$1;)를 사용하여 이스케이프되지 않는 한 배열의 문자열에는 세미콜론(;) 문자가 포함될 수 없습니다.
  + 어레이 안의 문자열을 인용 부호로 묶으려면 전체 어레이를 한 세트의 인용 부호로 묶어야 합니다. 예: "문자열 1, 문자열 2, 문자열 3".
+ **DATE, DATETIME** - 날짜/시간 값은 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-]hhmm
  + yyyy-MM-ddTHH:mm:ss.SSS[\$1\$1-]hhmm
+ **서명된 정수** - 
  + 바이트: -128\$1127
  + 요약: -32768\$132767
  + 정수: -2^31 \$1 2^31-1
  + Long: -2^63\$12^63-1

**Neptune별 열 유형:**
+ 열 유형 사용자 열에서 모두 지원됩니다. 모든 유형은 지원하는 다른 모든 유형에 대한 "syntactic sugar" 유형입니다. 사용자 열에 여러 유형이 있는 경우 매우 유용합니다. 모든 유형 값의 페이로드는 다음과 같은 json 문자열 목록입니다. `{"value": "10", "type": "Int"};{"value": "1.0", "type": "Float"}`각 개별 json 문자열에 값 필드와 유형 필드가 있습니다. 모든 유형의 열 헤더는 propertyname:Any입니다. Any 열의 카디널리티 값이 설정됩니다. 즉, 열이 여러 값을 수락할 수 있습니다.
  + Neptune은 모든 유형에서 Bool(또는 Boolean), Byte, Short, Int, Long, UnsignedByte, UnsignedShort, UnsignedInt, UnsignedLong, Float, Double, Date, dateTime, String 및 Geometry 유형을 지원합니다.
  + 벡터 유형은 모든 유형에서 지원되지 않습니다.
  + 중첩 모든 유형은 지원되지 않습니다. 예를 들어 `{"value": {"value": "10", "type": "Int"}, "type": "Any"}`입니다.
+ Geometry 열 유형은 사용자 열에서 지원됩니다. 이러한 열의 페이로드에는 WKT(Well-known text) 형식의 문자열로 제공되는 Point 유형의 Geometry 프리미티브만 포함되어야 합니다. 예를 들어 POINT(30 10)는 유효한 Geometry 값입니다.

## 샘플 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 데이터베이스의 데이터에 액세스할 수 있는 적절한 권한이 `neptune.read()`있어야 합니다. 읽기 전용 쿼리에는 `ReadDataViaQuery` 작업이 필요합니다. 데이터를 수정하는 쿼리는 `WriteDataViaQuery` 삽입 또는 `DeleteDataViaQuery` 삭제에 필요합니다. 아래 예제에서는 지정된 클러스터에서 세 가지 작업을 모두 부여합니다.

또한 데이터 파일이 포함된 S3 버킷에 액세스할 수 있는 권한이 필요합니다. NeptuneS3Access 정책 문은 필요한 S3 권한을 부여합니다.
+ **`s3:ListBucket`**: 버킷 존재를 확인하고 콘텐츠를 나열하는 데 필요합니다.
+ **`s3:GetObject`**: openCypher 쿼리에 통합하기 위해 콘텐츠를 읽을 수 있도록 지정된 객체에 액세스하는 데 필요합니다.

S3 버킷이와 함께 서버 측 암호화를 사용하는 AWS KMS경우 KMS 권한도 부여해야 합니다. NeptuneS3KMSAccess 정책 설명을 통해 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 엔드포인트: **)
  + Neptune이 Amazon S3와 통신할 수 있으려면 Amazon S3용 게이트웨이 유형 VPC 엔드포인트가 필요합니다.
  + 쿼리에서 사용자 지정 AWS KMS 암호화를 사용하려면 Neptune이 통신할 수 있도록 하려면에 대한 인터페이스 유형 VPC 엔드포인트 AWS KMS 가 필요합니다 AWS KMS.
  + 이 엔드포인트를 구성하는 방법에 대한 자세한 지침은 [ Amazon S3 VPC 엔드포인트 생성을 참조하세요](bulk-load-tutorial-IAM.md).

# 공간 데이터
<a name="access-graph-opencypher-22-spatial-data"></a>

Amazon Neptune은 이제 공간 쿼리를 지원하므로 그래프에 기하학적 데이터를 저장하고 분석할 수 있습니다. 지리적 위치(예: 맵의 좌표)에 일반적으로 사용되는 공간 기능은 위치와 근접성이 중요한 2차원 데이터와 함께 작동합니다. 이 기능을 사용하여 "어떤 스토어가이 고객으로부터 5마일 이내에 있습니까?", "이 서비스 영역과 교차하는 모든 전송 경로 찾기" 또는 "이 평면도의 어떤 구성 요소가 HVAC 영역과 겹치나요?"와 같은 질문에 답할 수 있습니다. Neptune은 점, 다각형 및 기타 기하학적 모양으로 작동하는 업계 표준 공간 유형 함수를 사용하여 공간 지원을 구현합니다. 공간 데이터를 노드 및 엣지의 속성으로 저장한 다음 공간 함수를 사용하여 거리를 계산하거나, 점이 경계 내에 있는지 확인하거나, 중첩 리전을 찾을 수 있습니다. 모두 openCypher 쿼리 내에 있습니다.

**일반적인 사용 사례**:
+ **지리적 애플리케이션**: 위치 기반 권장 사항, 지오펜싱, 경로 계획 및 지역 분석
+ **시설 및 공간 관리**: 평면도 레이아웃, 장비 배치 및 영역 적용 범위
+ **네트워크 토폴로지**: 물리적 인프라 매핑, 적용 범위 영역 및 서비스 경계
+ **설계 및 CAD**: 2D 설계의 구성 요소 위치 확인, 충돌 감지 및 공간 관계
+ **게임 개발**: 캐릭터 위치 확인, 충돌 감지 및 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을 기반으로 거리를 계산합니다.

현재 구현은 3차원 좌표 저장을 지원합니다. 공간 함수는 현재 x축 및 y축(2차원) 좌표 사용만 지원합니다. z축 좌표는 현재 사용 가능한 공간 함수에서 지원되지 않습니다.

## 위치 데이터 저장
<a name="storing-spatial-data"></a>

Geometry 속성 유형을 사용하여 노드 및 엣지에 위치 데이터를 저장합니다. 지리적 셰이프를 텍스트로 나타내는 표준 방법인 WKT(Well-Known Text) 형식에서 지오메트리 값을 생성합니다. 예를 들어 포인트 위치를 저장하려면

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

지리적 좌표로 작업할 때 첫 번째 인수(x)는 경도를 나타내고 두 번째 인수(y)는 위도를 나타냅니다. 이는 공간 데이터베이스에 사용되는 표준 좌표 순서와 ISO 19125 표준을 따릅니다.

**참고**  
 이제 Neptune은 "Geometry"라는 새로운 데이터 유형을 지원합니다. 노드 또는 엣지의 Geometry 속성은 `ST_GeomFromText` 함수를 사용하여 WKT 문자열에서 생성할 수 있습니다.  
Neptune은 공간 유형 함수의 성능을 개선하기 위해 특수 공간 인덱스에 포인트 데이터를 자동으로 저장합니다. 예를 들어 다각형 내에서 지점을 찾는 데 `ST_Contains` 사용되는는 특수 공간 인덱스에 의해 가속화됩니다.  
[ 지오메트리의 잘 알려진 텍스트 표현을 위한 Wikipedia 페이지 ](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>

다음 쿼리 예제에서는 [air-routes 데이터 세트를](https://github.com/krlawrence/graph/tree/main/sample-data) 사용하여 Neptune에서 공간 함수를 사용하는 방법을 보여줍니다.

데이터에 Geometry 속성 대신 별도의 위도 및 경도 속성이 있는 경우 쿼리 시 해당 속성을 지점으로 변환할 수 있습니다. 지정된 위치에 가장 가까운 공항 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>

대부분의 쿼리 메서드는 Geometry 값을 사람이 읽을 수 있는 WKT 문자열로 반환합니다. Bolt 드라이버를 사용하는 경우 효율성을 위해 Geometry 값이 WKB(Well-Known Binary) 형식으로 반환됩니다. WKB를 애플리케이션의 Geometry 객체로 변환합니다.

```
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\$1GeomFromText](access-graph-opencypher-22-spatial-functions-st-geomfromtext.md)
+ [ST\$1AsText](access-graph-opencypher-22-spatial-functions-st-astext.md)
+ [ST\$1GeometryType](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\$1DistanceSpheroid](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` - 첫 번째 좌표를 나타내는 데이터 형식 DOUBLE PRECISION의 값입니다.
+ `y` - 두 번째 좌표를 나타내는 데이터 형식 DOUBLE PRECISION의 값입니다.
+ `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\$1180
+ 위도(`y`): -90\$190

이러한 범위를 벗어나는 좌표는와 같은 거리 계산 함수와 함께 사용할 때 `NaN` (숫자가 아님)를 반환합니다`ST_DistanceSpheroid`.

**반환 타입**

하위 유형 POINT의 GEOMETRY

x 또는 y가 null이면 null이 반환됩니다.

**예시**

다음은 입력 좌표에서 점 지오메트리를 구성합니다.

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

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

ST\$1GeomFromText는 입력 지오메트리의 WKT(Well-Known Text) 표현으로부터 지오메트리 객체를 구성합니다.

**구문**

```
ST_GeomFromText(wkt_string)
```

**인수**
+ `wkt_string` - 지오메트리의 WKT 표현인 데이터 유형 STRING의 값입니다.

**반환 타입**

GEOMETRY

wkt\$1string이 null이면 null이 반환됩니다.

wkt\$1string이 유효하지 않으면 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\$1AsText
<a name="access-graph-opencypher-22-spatial-functions-st-astext"></a>

ST\$1AsText는 입력 지오메트리의 WKT(Well-Known Text) 표현을 반환합니다.

**구문**

```
ST_AsText(geo)
```

**인수**
+ `geo` - GEOMETRY 데이터 유형의 값 또는 GEOMETRY로 평가되는 표현식입니다.

**반환 타입**

STRING

geo가 null이면 null이 반환됩니다.

입력 파라미터가 Geometry가 아닌 경우 BadRequestException이 반환됩니다.

결과가 64-KB STRING보다 크면 오류가 반환됩니다.

**예시**

```
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\$1GeometryType
<a name="access-graph-opencypher-22-spatial-functions-st-geometrytype"></a>

ST\$1GeometryType은 지오메트리 유형을 문자열로 반환합니다.

**구문**

```
ST_GeometryType(geom)
```

**인수**
+ `geom` - GEOMETRY 데이터 형식의 값 또는 GEOMETRY 형식으로 평가되는 표현식입니다.

**반환 타입**

STRING

geom이 null이면 null이 반환됩니다.

입력 파라미터가 Geometry가 아닌 경우 BadRequestException이 반환됩니다.

**예시**

```
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는 입력 지오메트리의 2D 프로젝션이 토폴로지상 동일한 경우 true를 반환합니다. 지오메트리에 동일한 점 집합이 있는 경우 토폴로지상 동일한 것으로 간주됩니다. 토폴로지상 동일한 지오메트리에서 버텍스 순서는 이러한 평등을 유지하면서 다를 수 있습니다.

**구문**

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

**인수**
+ `geom1` - GEOMETRY 데이터 유형의 값 또는 GEOMETRY 유형으로 평가되는 표현식입니다.
+ `geom2` - GEOMETRY 데이터 유형의 값 또는 GEOMETRY 유형으로 평가되는 표현식입니다. 이 값을 geom1과 비교하여 geom1과 같은지 판별합니다.

**반환 타입**

BOOLEAN

geom1 또는 geom2가 null이면 null이 반환됩니다.

geom1 또는 geom2가 Geometries가 아닌 경우 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>

ST\$1Contains는 첫 번째 입력 지오메트리의 2D 프로젝션에 두 번째 입력 지오메트리의 2D 프로젝션이 포함된 경우 true를 반환합니다. 지오메트리 A는 B의 모든 지점이 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이 반환됩니다.

입력 파라미터가 Geometry가 아닌 경우 BadRequestException이 반환됩니다.

**예시**

```
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>

ST\$1Intersects는 두 입력 지오메트리의 2D 프로젝션에 공통되는 점이 하나 이상인 경우 true를 반환합니다.

**구문**

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

**인수**
+ `geom1` - GEOMETRY 데이터 유형의 값 또는 GEOMETRY 유형으로 평가되는 표현식입니다.
+ `geom2` - GEOMETRY 데이터 유형의 값 또는 GEOMETRY 유형으로 평가되는 표현식입니다.

**반환 타입**

BOOLEAN

geom1 또는 geom2가 null이면 null이 반환됩니다.

입력 파라미터가 Geometry가 아닌 경우 BadRequestException이 반환됩니다.

**예시**

```
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로 평가되는 표현식입니다.

**반환 타입**

입력 지오메트리와 동일한 단위의 DOUBLE PRECISION.

geo1 또는 geo2가 null이면 null이 반환됩니다.

입력 파라미터가 Geometry가 아닌 경우 BadRequestException이 반환됩니다.

**예시**

```
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\$1DistanceSpheroid
<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가 됩니다.
+ 입력 지오메트리의 최소 경계 상자가 점으로 변형되는 경우 반환된 지오메트리는 점입니다.
+ 위의 항목 중 어느 것도 true가 아니면 함수는 버텍스가 최소 경계 상자의 모서리인 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는 xy 데카르트 평면에 투영된 입력 형상으로부터의 거리가 입력 거리보다 작거나 같은 모든 점을 나타내는 2D 형상을 반환합니다.

**구문**

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

**인수**
+ `geom` - GEOMETRY 데이터 형식의 값 또는 GEOMETRY 형식으로 평가되는 표현식입니다.
+ `distance` - 버퍼의 거리(또는 반경)를 나타내는 데이터 유형 DOUBLE PRECISION의 값입니다.
+ `number_of_segments_per_quarter_circle` - 데이터 형식 INTEGER의 값입니다(0보다 크거나 같아야 함). 이 값은 입력 형상의 각 꼭짓점 주위의 1/4 원을 근사화하는 점의 수를 결정합니다. 음수 값은 기본적으로 0입니다. 기본값은 8입니다.

**반환 타입**

GEOMETRY

ST\$1Buffer 함수는 xy 데카르트 평면에서 2차원(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))
```

다음은 원을 근사하는 입력 포인트 지오메트리의 버퍼를 반환합니다. 이 명령은 1/4 원당 세그먼트 수로 3을 지정하므로 함수에서는 기본값인 3개 세그먼트를 사용하여 1/4 원을 근사화합니다.

```
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))
```