

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

# Neptune 그래프 쿼리
쿼리

Neptune은 그래프 액세스를 위해 다음 그래프 쿼리 언어를 지원합니다.
+ [Gremlin](https://tinkerpop.apache.org/gremlin.html)은 속성 그래프를 생성하고 쿼리하기 위해 [Apache TinkerPop](https://tinkerpop.apache.org/)에서 정의한 것입니다.

  Gremlin의 쿼리는 각 단계가 엣지를 따라 노드로 이어지는 개별 단계로 구성된 순회입니다.

  Neptune에서 Gremlin을 사용하는 방법에 대해 알아보려면 [Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md)를 참조하고, Gremlin의 Neptune 구현에 대한 구체적인 세부 정보를 찾아보려면 [Amazon Neptune에 사용되는 Gremlin 표준 규정 준수](access-graph-gremlin-differences.md)를 참조하세요.
+ [openCypher](access-graph-opencypher.md)는 속성 그래프용 선언적 쿼리 언어로, Neo4j에서 처음 개발한 후 2015년에 오픈 소스로 제공되었으며, Apache 2 오픈 소스 라이선스에 따라 [openCypher](http://www.opencypher.org/) 프로젝트에 기여했습니다. 구문은 [openCypher 사양](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf)에 문서화되어 있습니다.
+ [SPARQL](https://www.w3.org/TR/sparql11-overview/)은 [RDF](https://www.w3.org/2001/sw/wiki/RDF) 데이터를 쿼리하기 위한 그래프 패턴 매칭을 기반으로 하는 선언적 언어로, [World Wide Web Consortium](https://www.w3.org/)에서 지원합니다.

  Neptune에서 SPARQL을 사용하는 방법에 대해 알아보려면 [SPARQL을 사용하여 Neptune 그래프에 액세스](access-graph-sparql.md)를 참조하고, SPARQL의 Neptune 구현에 대한 구체적인 세부 정보를 찾아보려면 [Amazon Neptune에 적용되는 SPARQL 표준 규정 준수](feature-sparql-compliance.md)를 참조하세요.

**참고**  
Gremlin과 openCypher는 모두 Neptune에 저장된 모든 속성 그래프 데이터를 로드 방식에 관계없이 쿼리하는 데 사용할 수 있습니다.

**Topics**
+ [

# Amazon Neptune의 쿼리 대기열
](access-graph-queuing.md)
+ [

# Amazon Neptune의 쿼리 계획 캐시
](access-graph-qpc.md)
+ [

# Neptune Gremlin 또는 SPARQL 쿼리에 사용자 지정 ID 주입
](features-query-id.md)
+ [

# Gremlin을 사용하여 Neptune 그래프에 액세스
](access-graph-gremlin.md)
+ [

# openCypher를 사용하여 Neptune 그래프에 액세스
](access-graph-opencypher.md)
+ [

# SPARQL을 사용하여 Neptune 그래프에 액세스
](access-graph-sparql.md)

# Amazon Neptune의 쿼리 대기열
쿼리 대기열

그래프 애플리케이션을 개발 및 튜닝할 때 데이터베이스에서 쿼리가 대기하는 방식을 파악하는 것이 도움이 될 수 있습니다. Amazon Neptune에서 쿼리 대기는 다음과 같이 발생합니다.
+ 인스턴스 크기에 관계없이 인스턴스당 대기될 수 있는 최대 쿼리 수는 8,192개입니다. 해당 개수를 넘는 쿼리는 거부되고 `ThrottlingException`으로 인해 실패합니다 .
+ 한 번에 실행할 수 있는 최대 쿼리 수는 할당된 작업자 스레드 수에 따라 결정됩니다. 이 수는 일반적으로 사용 가능한 가상 CPU 코어(vCPU) 수의 두 배로 설정됩니다.
+ 쿼리 지연 시간에는 쿼리가 대기열에서 사용하는 시간과 네트워크 왕복 시간 및 쿼리가 실제로 실행되는 데 걸리는 시간이 포함됩니다.

## 지정된 순간에 대기열에 있는 쿼리 수 결정
대기열에 있는 쿼리 수 찾기

`MainRequestQueuePendingRequests` CloudWatch 지표는 입력 대기열에서 5분 단위로 대기 중인 요청 수를 기록합니다([Neptune CloudWatch 지표](cw-metrics.md) 참조).

Gremlin의 경우 [Gremlin 쿼리 상태 API](gremlin-api-status.md)에서 반환한 `acceptedQueryCount` 값을 사용하여 대기열의 현재 쿼리 수를 얻을 수 있습니다. 그러나 [SPARQL 쿼리 상태 API](sparql-api-status.md)에서 반환하는 `acceptedQueryCount` 값에는 완료된 쿼리를 포함하여 서버가 시작된 이후 수락된 모든 쿼리가 포함됩니다.

## 쿼리 대기열이 제한 시간에 영향을 주는 방식
쿼리 제한 시간

위에서 언급했듯이 쿼리 지연 시간에는 쿼리가 대기열에서 사용하는 시간과 쿼리가 실행되는 데 걸리는 시간이 포함됩니다.

쿼리의 제한 시간은 일반적으로 대기열에 들어갈 때부터 측정되므로 느리게 이동하는 대기열은 쿼리가 대기열에서 제거되는 즉시 많은 쿼리가 시간 초과될 수 있습니다. 이러한 상황은 분명히 바람직하지 않으므로 신속하게 실행할 수 없다면 많은 수의 쿼리를 대기시키지 않는 것이 좋습니다.

# Amazon Neptune의 쿼리 계획 캐시
쿼리 계획 캐시

 쿼리가 Neptune에 제출되면 쿼리 문자열이 구문 분석, 최적화 및 쿼리 계획으로 변환된 다음 엔진에서 실행됩니다. 애플리케이션은 종종 서로 다른 값으로 인스턴스화되는 일반적인 쿼리 패턴에 의해 지원됩니다. 쿼리 계획 캐시는 쿼리 계획을 캐싱하여 반복되는 패턴에 대한 구문 분석 및 최적화를 방지함으로써 전체 지연 시간을 줄일 수 있습니다.

 쿼리 계획 캐시는 파라미터화되지 않았거나 파라미터화된 쿼리인 **OpenCypher** 쿼리에 사용할 수 있습니다. READ와 HTTP 및 Bolt에 대해 활성화됩니다. OC 변형 쿼리에는 지원되지 **않습니다**. Gremlin 또는 SPARQL 쿼리에는 지원되지 **않습니다**.

## 쿼리 계획 캐시를 강제로 활성화 또는 비활성화하는 방법


 쿼리 계획 캐시는 지연 시간이 짧은 파라미터화된 쿼리에 대해 기본적으로 활성화됩니다. 파라미터화된 쿼리에 대한 계획은 지연 시간이 **100ms**의 임계값보다 낮은 경우에만 캐시됩니다. 이 동작은 쿼리 수준 쿼리 힌트 `QUERY:PLANCACHE`를 통해 쿼리별(파라미터화 여부와 무관하게)로 재정의할 수 있습니다. `USING` 절과 함께 사용해야 합니다. 쿼리 힌트는 `enabled` 또는 `disabled`를 값으로 허용합니다.

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

## 계획이 캐시되었는지 여부를 확인하는 방법


 HTTP READ의 경우, 쿼리가 제출되고 계획이 캐시된 경우 `explain`은 쿼리 계획 캐시와 관련된 세부 정보를 표시합니다.

```
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" MATCH(n) RETURN n LIMIT 1" \
  -d "explain=[static|details]"

Query: <QUERY STRING>
Plan cached by request: <REQUEST ID OF FIRST TIME EXECUTION>
Plan cached at: <TIMESTAMP OF FIRST TIME EXECUTION>
Parameters: <PARAMETERS, IF QUERY IS PARAMETERIZED QUERY>
Plan cache hits: <NUMBER OF CACHE HITS FOR CACHED PLAN>
First query evaluation time: <LATENCY OF FIRST TIME EXECUTION>

The query has been executed based on a cached query plan. Detailed explain with operator runtime statistics can be obtained by running the query with plan cache disabled (using HTTP parameter planCache=disabled).
```

 Bolt를 사용하는 경우 설명 기능은 지원되지 않습니다.

## 제거


 쿼리 계획은 캐시 TTL(Time to Live) 또는 캐시된 쿼리 계획의 최대 수에 도달했을 때 제거됩니다. 쿼리 계획에 도달하면 TTL이 새로 고쳐집니다. 기본값은 다음과 같습니다.
+  1000 - 인스턴스당 캐시할 수 있는 최대 계획 수입니다.
+  TTL - 300,000밀리초 또는 5분. 캐시 적중은 TTL을 다시 시작하고 다시 5분으로 재설정합니다.

## 계획이 캐시되지 않는 조건


 다음 조건에서는 쿼리 계획 캐시가 사용되지 않습니다.

1.  쿼리 힌트 `QUERY:PLANCACHE "disabled"`를 사용하여 쿼리를 제출하는 경우입니다. 쿼리를 다시 실행하고 `QUERY:PLANCACHE "disabled"`를 제거하여 쿼리 계획 캐시를 활성화할 수 있습니다.

1.  제출된 쿼리가 파라미터화된 쿼리가 아니며 힌트 `QUERY:PLANCACHE "enabled"`를 포함하지 않는 경우입니다.

1.  쿼리 평가 시간이 지연 시간 임계값보다 클 경우, 해당 쿼리는 캐시되지 않으며 쿼리 계획 캐시의 혜택을 받지 못하는 장기 실행 쿼리로 간주됩니다.

1.  쿼리에 결과를 반환하지 않는 패턴이 포함된 경우입니다.
   +  즉, 지정된 레이블을 가진 노드가 없는 경우에 `MATCH (n:nonexistentLabel) return n` 
   +  즉, `name=abcde`를 포함하는 노드가 없는 경우에 `parameters={"param": "abcde"}`인 `MATCH (n {name: $param}) return n` 

1.  쿼리 파라미터가 `list` 또는 `map`과 같은 복합 유형인 경우입니다.

   ```
   curl -k https://<endpoint>:<port>/opencypher \
     -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
     -d "parameters={\"arg\": [1, 2, 3]}"
   
   curl -k https://<endpoint>:<port>/opencypher \
     -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
     -d "parameters={\"arg\": {\"a\": 1}}"
   ```

1.  쿼리 파라미터가 데이터 로드 또는 데이터 삽입 작업의 일부가 아니었던 문자열인 경우입니다. 예를 들어, `"X"`를 삽입하기 위해 `CREATE (n {name: "X"})`가 실행되면 `RETURN "Y"`는 캐시되지만, `RETURN "X"`는 캐시되지 않으며 `"Y"`는 삽입되지 않았으며 데이터베이스에 존재하지 않습니다.

# Neptune Gremlin 또는 SPARQL 쿼리에 사용자 지정 ID 주입
사용자 지정 queryId

기본적으로 Neptune은 모든 쿼리에 고유한 `queryId` 값을 할당합니다. 이 ID를 사용해 실행 중인 쿼리에 대한 정보를 가져오거나([Gremlin 쿼리 상태 API](gremlin-api-status.md) 또는 [SPARQL 쿼리 상태 API](sparql-api-status.md) 참조) 이를 취소할 수 있습니다([Gremlin 쿼리 취소](gremlin-api-status-cancel.md) 또는 [SPARQL 쿼리 취소](sparql-api-status-cancel.md) 참조).

또한 Neptune의 경우 HTTP 헤더에서, 또는 `queryId` 쿼리 힌트를 사용하여 SPARQL 쿼리에서 Gremlin 또는 SPARQL 쿼리에 대해 자체적인 `queryId` 값을 지정할 수 있습니다. 자체 `queryID`를 할당하면 상태를 가져오거나 이를 취소하기 위해 쿼리를 손쉽게 추적할 수 있습니다.

## HTTP 헤더를 사용해 사용자 지정 `queryId` 값 주입
HTTP 헤더 사용

Gremlin 및 SPARQL 모두에서 HTTP 헤더를 사용해 자체 `queryId` 값을 쿼리에 주입할 수 있습니다.

**Gremlin 예제**

```
curl -XPOST https://your-neptune-endpoint:port \
    -d "{\"gremlin\": \
        \"g.V().limit(1).count()\" , \
        \"queryId\":\"4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47\"  }"
```

**SPARQL 예제**

```
curl https://your-neptune-endpoint:port/sparql \
    -d "query=SELECT * WHERE { ?s ?p ?o } " \
       --data-urlencode \
       "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
```

## SPARQL 쿼리 힌트를 사용해 사용자 지정 `queryId` 값을 주입
SPARQL 쿼리 힌트 사용

아래에는 SPARQL `queryId` 쿼리 힌트를 사용해 SPARQL 쿼리에 사용자 지정 `queryId` 값을 주입하는 방법에 대한 예제가 나와 있습니다.

```
curl https://your-neptune-endpoint:port/sparql \
    -d "query=PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#> \
       SELECT * WHERE { hint:Query hint:queryId \"4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47\" \
       {?s ?p ?o}}"
```

## `queryId` 값을 사용하여 쿼리 상태 확인
queryId를 사용해 상태 확인

**Gremlin 예제**

```
curl https://your-neptune-endpoint:port/gremlin/status \
    -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
```

**SPARQL 예제**

```
curl https://your-neptune-endpoint:port/sparql/status \
    -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
```

# Gremlin을 사용하여 Neptune 그래프에 액세스
Gremlin

Amazon Neptune은 Apache TinkerPop 및 Gremlin과 호환됩니다. 즉, Neptune DB 인스턴스에 연결하고 Gremlin 순회 언어를 사용하여 그래프를 쿼리할 수 있습니다(Apache TinkerPop 설명서의 [그래프](https://tinkerpop.apache.org/docs/current/reference/#graph) 참조). Gremlin의 Neptune 구현 차이점에 대해서는 [Gremlin 표준 규정 준수](access-graph-gremlin-differences.md) 섹션을 참조하세요.

 Gremlin의 *순회*는 일련의 연결된 단계입니다. 이러한 순회는 버텍스(또는 엣지)에서 시작하고, 각 버텍스의 나가는 엣지 및 해당 버텍스의 나가는 엣지를 따라가며 그래프를 조사합니다. 각 단계는 순회의 작업입니다. 자세한 내용은 TinkerPop 설명서의 The [Traversal](https://tinkerpop.apache.org/docs/current/reference/#traversal)을 참조하세요.

다양한 Neptune 엔진 버전은 서로 다른 Gremlin 버전을 지원합니다. 실행 중인 Neptune 버전의 [엔진 릴리스 페이지를](engine-releases.md) 확인하여 지원하는 Gremlin 릴리스를 확인하거나 다양한 Neptune 엔진 버전에서 지원하는 TinkerPop의 최신 및 최신 버전을 나열하는 다음 표를 참조하세요.


| Neptune 엔진 버전 | 최소 TinkerPop 버전 | 최대 TinkerPop 버전 | 
| --- | --- | --- | 
| `1.3.2.0 and newer` | `3.7.1` | `3.7.3` | 
| `1.3.1.0` | `3.6.2` | `3.6.5` | 
| `1.3.0.0` | `3.6.2` | `3.6.4` | 
| `1.2.1.0 <= 1.2.1.2` | `3.6.2` | `3.6.2` | 
| `1.1.1.0 <= 1.2.0.2` | `3.5.5` | `3.5.6` | 
| `1.1.0.0 and older` | `(deprecated)` | `(deprecated)` | 

TinkerPop 클라이언트는 일반적으로 시리즈(예`3.6.x`: 또는 `3.7.x`) 내에서 역호환되며 이러한 경계를 넘어 작동하는 경우가 많지만, 위의 표에서는 최상의 경험과 호환성을 위해 사용할 버전 조합을 권장합니다. 달리 권장되지 않는 한 일반적으로 이러한 지침을 준수하고 사용 중인 TinkerPop 버전에 맞게 클라이언트 애플리케이션을 업그레이드하는 것이 가장 좋습니다.

TinkerPop 버전을 업그레이드할 때는 항상 [TinkerPop의 업그레이드 설명서를](http://tinkerpop.apache.org/docs/current/upgrade/) 참조하는 것이 중요합니다.이 설명서는 활용할 수 있는 새로운 기능을 식별하는 데 도움이 되지만 업그레이드에 가까워질 때 주의해야 할 문제도 있습니다. 특별히 고려해야 할 문제로 언급되지 않는 한 일반적으로 업그레이드 후 기존 쿼리 및 기능이 작동할 것으로 예상해야 합니다. 마지막으로 새 기능을 사용하도록 업그레이드하는 버전이 Neptune에서 지원하는 버전보다 이후 버전인 경우 사용하지 못할 수 있습니다.

다양한 프로그래밍 언어에 GLV(Gremlin Language Variant) 및 Gremlin 액세스 지원이 있습니다. 자세한 내용은 TinkerPop 설명서의 [Gremlin 언어 변형](https://tinkerpop.apache.org/docs/current/reference/#gremlin-drivers-variants)을 참조하세요.

이 설명서에서는 다음 변형 및 프로그래밍 언어로 Neptune에 액세스하는 방법을 설명합니다.
+ [Gremlin 콘솔에서 Neptune DB 인스턴스에 연결하도록 설정](access-graph-gremlin-console.md)
+ [HTTPS REST 엔드포인트를 사용하여 Neptune DB 인스턴스에 연결](access-graph-gremlin-rest.md)
+ [Amazon Neptune과 함께 사용할 수 있는 Java 기반 Gremlin 클라이언트](access-graph-gremlin-client.md)
+ [Python을 사용하여 Neptune DB 인스턴스에 연결](access-graph-gremlin-python.md)
+ [.NET을 사용하여 Neptune DB 인스턴스에 연결](access-graph-gremlin-dotnet.md)
+ [Node.js를 사용하여 Neptune DB 인스턴스에 연결](access-graph-gremlin-node-js.md)
+ [Go를 사용하여 Neptune DB 인스턴스에 연결](access-graph-gremlin-go.md)

[SSL/HTTPS를 사용하여 Amazon Neptune 데이터베이스에 대한 연결 암호화](security-ssl.md)의 설명대로 모든 AWS 리전에서 Neptune에 연결할 경우 전송 계층 보안/보안 소켓 계층(TLS/SSL)을 사용해야 합니다.

시작하기 전에 다음을 완비해야 합니다.
+ Neptune DB 인스턴스. Neptune DB 인스턴스 생성에 대한 자세한 내용은 [Amazon Neptune 클러스터 생성](get-started-create-cluster.md) 섹션을 참조하세요.
+ 사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스입니다.

사전 조건, 로드 형식 및 로드 파라미터를 포함하여 데이터를 Neptune에 로드하는 방법에 대한 자세한 내용은 [Amazon Neptune에 데이터 로드](load-data.md) 섹션을 참조하세요.

**Topics**
+ [

# Gremlin 콘솔에서 Neptune DB 인스턴스에 연결하도록 설정
](access-graph-gremlin-console.md)
+ [

# HTTPS REST 엔드포인트를 사용하여 Neptune DB 인스턴스에 연결
](access-graph-gremlin-rest.md)
+ [

# Amazon Neptune과 함께 사용할 수 있는 Java 기반 Gremlin 클라이언트
](access-graph-gremlin-client.md)
+ [

# Python을 사용하여 Neptune DB 인스턴스에 연결
](access-graph-gremlin-python.md)
+ [

# .NET을 사용하여 Neptune DB 인스턴스에 연결
](access-graph-gremlin-dotnet.md)
+ [

# Node.js를 사용하여 Neptune DB 인스턴스에 연결
](access-graph-gremlin-node-js.md)
+ [

# Go를 사용하여 Neptune DB 인스턴스에 연결
](access-graph-gremlin-go.md)
+ [

# AWS SDK를 사용하여 Gremlin 쿼리 실행
](access-graph-gremlin-sdk.md)
+ [

# Gremlin 쿼리 힌트
](gremlin-query-hints.md)
+ [

# Gremlin 쿼리 상태 API
](gremlin-api-status.md)
+ [

# Gremlin 쿼리 취소
](gremlin-api-status-cancel.md)
+ [

# Gremlin 스크립트 기반 세션 지원
](access-graph-gremlin-sessions.md)
+ [

# Neptune에서의 Gremlin 트랜잭션
](access-graph-gremlin-transactions.md)
+ [

# Amazon Neptune과 함께 Gremlin API 사용
](gremlin-api-reference.md)
+ [

# Amazon Neptune Gremlin의 쿼리 결과 캐싱
](gremlin-results-cache.md)
+ [

# Gremlin `mergeV()` 및 `mergeE()` 단계를 사용하여 효율적인 업서트 생성
](gremlin-efficient-upserts.md)
+ [

# `fold()/coalesce()/unfold()`를 사용하여 효율적인 Gremlin 업서트 만들기
](gremlin-efficient-upserts-pre-3.6.md)
+ [

# Gremlin `explain`을 사용하여 Neptune 쿼리 실행 분석
](gremlin-explain.md)
+ [

# Neptune DFE 쿼리 엔진과 함께 Gremlin 사용
](gremlin-with-dfe.md)

# Gremlin 콘솔에서 Neptune DB 인스턴스에 연결하도록 설정
Gremlin 콘솔 설치

Gremlin 콘솔을 사용하여 REPL(read-eval-print loop) 환경에서 TinkerPop 그래프 및 쿼리를 실험할 수 있습니다.

## Gremlin 콘솔 설치 및 일반적인 방법으로 연결
일반 연결

Gremlin 콘솔을 사용하여 원격 그래프 데이터베이스에 연결할 수 있습니다. 다음 섹션에서는 Gremlin 콘솔을 설치하고 구성하여 원격으로 Neptune DB 인스턴스에 연결하는 방법을 안내합니다. 사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

SSL/TLS(필수)를 사용하여 Neptune에 연결하는 데 도움이 필요하면 [SSL/TLS 구성](access-graph-gremlin-java.md#access-graph-gremlin-java-ssl)을 참조하세요.

**참고**  
Neptune DB 클러스터에서 [IAM 인증을 활성화](iam-auth-enable.md)한 경우 여기의 지침 대신 Gremlin 콘솔을 설치하기 위한 [Gremlin 콘솔에서 IAM 인증을 사용하여 Amazon Neptune 데이터베이스에 연결](iam-auth-connecting-gremlin-console.md)의 지침을 따르세요.

**Gremlin 콘솔을 설치하고 Neptune에 연결하려면**

1. Gremlin 콘솔 이진은 Java 8 또는 Java 11이 필요합니다. 이 지침에서는 Java 11의 사용을 전제로 합니다. 다음과 같이 EC2 인스턴스에 Java 11을 설치할 수 있습니다.
   + [Amazon Linux 2(AL2)](https://aws.amazon.com/amazon-linux-2)를 사용하는 경우:

     ```
     sudo amazon-linux-extras install java-openjdk11
     ```
   + [Amazon Linux 2023(AL2023)](https://docs.aws.amazon.com/linux/al2023/ug/what-is-amazon-linux.html)을 사용하는 경우:

     ```
     sudo yum install java-11-amazon-corretto-devel
     ```
   + 다른 배포판의 경우 다음 중 적절한 것을 사용하세요.

     ```
     sudo yum install java-11-openjdk-devel
     ```

     또는:

     ```
     sudo apt-get install openjdk-11-jdk
     ```

1. 다음을 입력하여 사용자의 EC2 인스턴스에 Java 11을 기본 런타임으로 설정합니다.

   ```
   sudo /usr/sbin/alternatives --config java
   ```

   메시지가 표시되면 Java 11에 대한 숫자를 입력합니다.

1. Apache 웹 사이트에서 적절한 버전의 Gremlin 콘솔을 다운로드합니다. 에서 Neptune 버전[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md)에서 지원하는 Gremlin 버전을 확인할 수 있습니다. 예를 들어 버전 3.7.2가 필요한 경우 다음과 같이 [Apache Tinkerpop](https://tinkerpop.apache.org/download.html) 웹 사이트에서 EC2 인스턴스로 [Gremlin 콘솔](https://archive.apache.org/dist/tinkerpop/3.7.2/apache-tinkerpop-gremlin-console-3.7.2-bin.zip)을 다운로드할 수 있습니다.

   ```
   wget https://archive.apache.org/dist/tinkerpop/3.7.2/apache-tinkerpop-gremlin-console-3.7.2-bin.zip
   ```

1. Gremlin 콘솔 zip 파일의 압축을 풉니다.

   ```
   unzip apache-tinkerpop-gremlin-console-3.7.2-bin.zip
   ```

1. 압축을 푼 디렉터리로 디렉터리를 변경합니다.

   ```
   cd apache-tinkerpop-gremlin-console-3.7.2
   ```

1. 추출된 디렉토리의 `conf` 하위 디렉터리에서 다음 텍스트가 있는 파일 `neptune-remote.yaml`을 생성합니다. *your-neptune-endpoint*를 Neptune DB 인스턴스의 호스트 이름 또는 IP 주소로 바꿉니다. 대괄호(`[ ]`)를 사용해야 합니다.
**참고**  
사용자의 Neptune DB 인스턴스 호스트 이름을 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   ```
   hosts: [your-neptune-endpoint]
   port: 8182
   connectionPool: { enableSsl: true }
   serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1,
                 config: { serializeResultToString: true }}
   ```
**참고**  
 직렬 변환기가 버전 3.7.0의 `gremlin-driver` 모듈에서 새 `gremlin-util` 모듈로 이동되었습니다. 패키지가 org.apache.tinkerpop.gremlin.driver.ser에서 org.apache.tinkerpop.gremlin.util.ser로 변경되었습니다.

1. 터미널에서 Gremlin 콘솔 디렉터리(`apache-tinkerpop-gremlin-console-3.7.2`)로 이동한 후 다음 명령을 입력하여 Gremlin 콘솔을 실행합니다.

   ```
   bin/gremlin.sh
   ```

   다음 결과가 표시됩니다.

   ```
            \,,,/
            (o o)
   -----oOOo-(3)-oOOo-----
   plugin activated: tinkerpop.server
   plugin activated: tinkerpop.utilities
   plugin activated: tinkerpop.tinkergraph
   gremlin>
   ```

   이제 `gremlin>` 프롬프트가 표시됩니다. 이 프롬프트에 나머지 단계를 입력합니다.

1. `gremlin>` 프롬프트에 다음을 입력하여 Neptune DB 인스턴스에 연결합니다.

   ```
   :remote connect tinkerpop.server conf/neptune-remote.yaml
   ```

1. `gremlin>` 프롬프트에 다음을 입력하여 원격 모드로 전환합니다. 그러면 모든 Gremlin 쿼리가 원격 연결로 전송됩니다.

   ```
   :remote console
   ```

1. 다음과 같이 입력하여 Gremlin 그래프에 쿼리를 전송합니다.

   ```
   g.V().limit(1)
   ```

1. 완료했으면 다음을 입력하여 Gremlin 콘솔을 종료합니다.

   ```
   :exit
   ```

**참고**  
세미콜론(`;`) 또는 줄 바꿈 문자(`\n`)를 사용하여 각 문장을 구분합니다.  
최종 순회 이전의 각 순회는 실행할 `next()`로 끝나야 합니다. 최종 순회의 데이터만 반환됩니다.

Gremlin의 Neptune 구현에 대한 자세한 내용은 [Amazon Neptune에 사용되는 Gremlin 표준 규정 준수](access-graph-gremlin-differences.md) 섹션을 참조하세요.

# Gremlin 콘솔에 연결하는 다른 방법
연결하는 다른 방법

**일반 연결 접근 방식의 단점**

Gremlin 콘솔에 연결하는 가장 일반적인 방법은 위에서 설명한 것처럼 `gremlin>` 프롬프트에서 다음과 같은 명령을 사용하는 방법입니다.

```
gremlin> :remote connect tinkerpop.server conf/(file name).yaml
gremlin> :remote console
```

이 방법은 효과적으로 작동하며, Neptune에 쿼리를 보낼 수 있도록 지원합니다. 하지만 Groovy 스크립트 엔진을 루프에서 제외하므로, Neptune은 모든 쿼리를 순수 Gremlin으로 취급합니다. 즉, 다음과 같은 쿼리 양식은 실패합니다.

```
gremlin> 1 + 1
gremlin> x = g.V().count()
```

이렇게 연결한 경우 변수를 사용하여 가장 근접하게 할 수 있는 작업은 콘솔에서 유지하는 `result` 변수를 사용하고 다음과 같이 `:>` 기호를 사용하여 쿼리를 전송하는 것입니다.

```
gremlin> :remote console
==>All scripts will now be evaluated locally - type ':remote console' to return to remote mode for Gremlin Server - [krl-1-cluster.cluster-ro-cm9t6tfwbtsr.us-east-1.neptune.amazonaws.com/172.31.19.217:8182]
gremlin> :> g.V().count()
==>4249

gremlin> println(result)
[result{object=4249 class=java.lang.Long}]

gremlin> println(result['object'])
[4249]
```

 

**다른 연결 방법**

다음과 같이 다른 방법으로 Gremlin 콘솔에 연결할 수도 있습니다. 이 방법이 더 효과적일 수도 있습니다.

```
gremlin> g = traversal().withRemote('conf/neptune.properties')
```

`neptune.properties`는 다음과 같은 형식을 취합니다.

```
gremlin.remote.remoteConnectionClass=org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection
gremlin.remote.driver.clusterFile=conf/my-cluster.yaml
gremlin.remote.driver.sourceName=g
```

`my-cluster.yaml` 파일은 다음과 비슷해야 합니다.

```
hosts: [my-cluster-abcdefghijk.us-east-1.neptune.amazonaws.com]
port: 8182
serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1,
              config: { serializeResultToString: false } }
connectionPool: { enableSsl: true }
```

**참고**  
 직렬 변환기가 버전 3.7.0의 `gremlin-driver` 모듈에서 새 `gremlin-util` 모듈로 이동되었습니다. 패키지가 org.apache.tinkerpop.gremlin.driver.ser에서 org.apache.tinkerpop.gremlin.util.ser로 변경되었습니다.

Gremlin 콘솔 연결을 이와 같이 구성하면 다음과 같은 종류의 쿼리를 성공적으로 수행할 수 있습니다.

```
gremlin> 1+1
==>2

gremlin> x=g.V().count().next()
==>4249

gremlin> println("The answer was ${x}")
The answer was 4249
```

다음과 같이 결과가 표시되지 않도록 할 수 있습니다.

```
gremlin> x=g.V().count().next();[]
gremlin> println(x)
4249
```

일반적인 모든 쿼리 방법(터미널 단계 없음)은 계속 작동합니다. 예제:

```
gremlin> g.V().count()
==>4249
```

[https://tinkerpop.apache.org/docs/current/reference/#io-step](https://tinkerpop.apache.org/docs/current/reference/#io-step) 단계를 사용하여 이런 종류의 연결이 있는 파일을 로드할 수도 있습니다.

## IAM 인증


Neptune은 DB 클러스터에 대한 액세스를 제어하는 [IAM 인증을](iam-auth-enable.md) 지원합니다. IAM 인증을 활성화한 경우 서명 버전 4 서명을 사용하여 요청을 인증해야 합니다. Gremlin 콘솔에서 연결하기 위한 자세한 지침과 코드 예제는 섹션을 참조하세요[Gremlin 콘솔에서 IAM 인증을 사용하여 Amazon Neptune 데이터베이스에 연결](iam-auth-connecting-gremlin-console.md).

# HTTPS REST 엔드포인트를 사용하여 Neptune DB 인스턴스에 연결
HTTPS REST

Amazon Neptune은 Gremlin 쿼리용 HTTPS 엔드포인트를 제공합니다. REST 인터페이스는 DB 클러스터가 사용하는 모든 Gremlin 버전과 호환됩니다. 지원하는 Gremlin 릴리스를 확인하려면 실행 중인 Neptune 엔진 버전의 [엔진 릴리스 페이지](engine-releases.md)를 참조하세요.

**참고**  
[SSL/HTTPS를 사용하여 Amazon Neptune 데이터베이스에 대한 연결 암호화](security-ssl.md)에서 설명한 것처럼 이제 Neptune에서는 HTTP 대신 HTTPS를 사용하여 연결해야 합니다. 또한 Neptune은 현재 REST API 요청에 대해 HTTP/2를 지원하지 않습니다. 클라이언트는 엔드포인트에 연결할 때 HTTP/1.1을 사용해야 합니다.

다음은 `curl` 명령과 HTTPS를 사용하여 Gremlin 엔드포인트에 연결하는 방법입니다. 사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

Neptune DB 인스턴스의 Gremlin 쿼리용 HTTPS 엔드포인트는 `https://your-neptune-endpoint:port/gremlin`입니다.

**참고**  
사용자의 Neptune DB 인스턴스 호스트 이름을 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

## HTTP REST 엔드포인트를 사용하여 Neptune에 연결하려면
연결 예제

다음 예에서는 **curl**을 사용하여 HTTP **POST**를 통해 Gremlin 쿼리를 제출합니다. 쿼리는 `gremlin` 속성으로 post 본문에서 JSON 형식으로 제출됩니다.

```
curl -X POST -d '{"gremlin":"g.V().limit(1)"}' https://your-neptune-endpoint:port/gremlin
```

이 예제는 `g.V().limit(1)` 순회를 사용하여 그래프의 첫 번째 버텍스를 반환합니다. 이를 다른 Gremlin 순회로 대체하여 다른 항목을 쿼리할 수 있습니다.

**중요**  
기본적으로 REST 엔드포인트는 단일 JSON 결과 세트의 모든 결과를 반환합니다. 이 결과 세트가 너무 크면 Neptune DB 인스턴스에서 `OutOfMemoryError` 예외가 발생할 수 있습니다.  
청크 응답(결과가 일련의 개별 응답으로 반환됨)을 활성화하면 이를 방지할 수 있습니다. [선택적 HTTP 후행 헤더를 사용하여 여러 부분으로 구성된 Gremlin 응답 활성화](access-graph-gremlin-rest-trailing-headers.md)을(를) 참조하세요.

Gremlin 쿼리를 보낼 때는 HTTP **POST** 요청을 사용하는 것이 좋지만, HTTP **GET** 요청을 사용할 수도 있습니다.

```
curl -G "https://your-neptune-endpoint:port?gremlin=g.V().count()"
```

**참고**  
Neptune은 `bindings` 속성을 지원하지 않습니다.

# 선택적 HTTP 후행 헤더를 사용하여 여러 부분으로 구성된 Gremlin 응답 활성화
여러 부분으로 구성된 응답 활성화

기본적으로 Gremlin 쿼리에 대한 HTTP 응답은 단일 JSON 결과 세트로 반환됩니다. 결과 세트가 매우 큰 경우 이로 인해 DB 인스턴스에서 `OutOfMemoryError` 예외가 발생할 수 있습니다.

단, *청크* 응답(여러 부분으로 나누어 반환되는 응답)을 활성화할 수 있습니다. 요청에 전송 인코딩(TE) 후행 헤더(`te: trailers`)를 포함하면 됩니다. TE 헤더에 대한 자세한 내용은 [MDN 페이지(TE 요청 헤더에 관한 정보)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE)를 참조하세요.

응답이 여러 부분으로 나뉘어 반환되는 경우 첫 번째 부분이 HTTP 상태 코드 `200`(OK)으로 도착하기 때문에, 첫 번째 부분이 수신된 후 발생하는 문제를 진단하기 어려울 수 있습니다. 이후에 오류가 발생하면 일반적으로 메시지 본문에 손상된 응답이 포함되며, 이 메시지 본문의 끝에 Neptune이 오류 메시지를 추가합니다.

이러한 종류의 장애를 더 쉽게 감지하고 진단할 수 있도록 Neptune은 모든 응답 청크의 후행 헤더에 2개의 새로운 헤더 필드도 포함합니다.
+ `X-Neptune-Status`   –   응답 코드와 짧은 이름이 차례로 들어 있습니다. 예를 들어, 성공하면 후행 헤더는 `X-Neptune-Status: 200 OK`와 같습니다. 장애가 발생한 경우 응답 코드는 `X-Neptune-Status: 500 TimeLimitExceededException`과 같은 [Neptune 엔진 오류 코드](errors-engine-codes.md) 중 하나가 됩니다.
+ `X-Neptune-Detail`   –   요청이 성공하면 비어 있습니다. 오류가 발생한 경우 JSON 오류 메시지가 포함됩니다. HTTP 헤더 값에는 ASCII 문자만 사용할 수 있으므로, JSON 문자열은 URL로 인코딩됩니다.

**참고**  
Neptune은 현재 청크 응답의 `gzip` 압축을 지원하지 않습니다. 클라이언트가 청크 인코딩과 압축을 동시에 요청하는 경우 Neptune은 압축을 건너뜁니다.

# Amazon Neptune과 함께 사용할 수 있는 Java 기반 Gremlin 클라이언트
Java

Amazon Neptune에서는 [Apache TinkerPop Java Gremlin 클라이언트](https://search.maven.org/artifact/org.apache.tinkerpop/gremlin-driver)와 [Amazon Neptune용 Gremlin 클라이언트](https://search.maven.org/artifact/software.amazon.neptune/gremlin-client)라는 2개의 오픈 소스 Java 기반 Gremlin 클라이언트 중 하나를 사용할 수 있습니다.

## Apache TinkerPop Java Gremlin 클라이언트
TinkerPop 클라이언트

Apache TinkerPop Java [gremlin-driver](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java)는 모든 TinkerPop 지원 그래프 데이터베이스에서 작동하는 표준 공식 Gremlin 클라이언트입니다. 광범위한 TinkerPop 개발 공간과의 호환성이 극대화되어야 하거나, 여러 그래프 데이터베이스 시스템으로 작업하거나, Neptune과 관련된 고급 클러스터 관리 및 로드 밸런싱 기능이 필요하지 않은 경우이 클라이언트를 사용합니다. 이 클라이언트는 단일 Neptune 인스턴스에 연결하는 간단한 애플리케이션이나 클라이언트 내부가 아닌 인프라 수준에서 로드 밸런싱을 처리하려는 경우에 적합합니다.

**중요**  
Neptune 엔진 버전과의 호환성을 위해서는 올바른 Apache TinkerPop Gremlin 드라이버 버전을 선택하는 것이 중요합니다. 호환되지 않는 버전을 사용하면 연결 실패 또는 예기치 않은 동작이 발생할 수 있습니다. 자세한 버전 호환성 정보는 섹션을 참조하세요[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md).

**참고**  
Neptune과 함께 사용할 올바른 Apache TinkerPop 버전을 결정하는 데 도움이 되는 표가 로 이동되었습니다[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md). 이 표는 이전에이 페이지에 수년 동안 위치했으며 이제 TinkerPop이 지원하는 모든 프로그래밍 언어에 대한 참조를 위해 더 중앙 집중화되었습니다.

## Amazon Neptune용 Gremlin Java 클라이언트
Neptune Gremlin 클라이언트

Amazon Neptune용 Gremlin 클라이언트는 표준 TinkerPop Java 클라이언트의 드롭인 대체 역할을 하는 [오픈 소스 Java 기반 Gremlin 클라이언트](https://github.com/aws/neptune-gremlin-client)입니다.

Neptune Gremlin 클라이언트는 Neptune 클러스터에 최적화되어 있습니다. 이를 통해 클러스터의 여러 인스턴스에 걸친 트래픽 분산을 관리하고, 복제본을 추가하거나 제거할 때 클러스터 토폴로지의 변경 사항에 맞게 조정할 수 있습니다. 역할, 인스턴스 유형, 가용 영역 또는 인스턴스와 관련된 태그를 기반으로 클러스터의 인스턴스 하위 세트에 요청을 분산하도록 클라이언트를 구성할 수도 있습니다.

[Neptune Gremlin Java 클라이언트의 최신 버전](https://search.maven.org/artifact/software.amazon.neptune/gremlin-client)은 Maven Central에서 사용할 수 있습니다.

Neptune Gremlin Java 클라이언트에 대한 자세한 내용은 [이 블로그 게시물](https://aws.amazon.com/blogs/database/load-balance-graph-queries-using-the-amazon-neptune-gremlin-client/)을 참조하세요. 코드 샘플과 데모를 보려면 [클라이언트의 GitHub 프로젝트](https://github.com/aws/neptune-gremlin-client)를 확인하세요.

Neptune Gremlin 클라이언트 버전을 선택할 때는 Neptune 엔진 버전과 관련하여 기본 TinkerPop 버전을 고려해야 합니다. 의 호환성 표를 참조하여 Neptune 엔진에 적합한 TinkerPop 버전을 [Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md) 확인한 다음 다음 다음 표를 사용하여 적절한 Neptune Gremlin 클라이언트 버전을 선택합니다.


**Neptune Gremlin 클라이언트 버전 호환성**  

| Neptune Gremlin 클라이언트 버전 | TinkerPop 버전 | 
| --- | --- | 
| 3.x | 3.7.x(Java 2.x/1.x용AWS SDK) | 
| 2.1.x | 3.7.x(Java 1.x용AWS SDK) | 
| 2.0.x | 3.6.x | 
| 1.12 | 3.5.x | 

# Java 클라이언트를 사용하여 Neptune DB 인스턴스에 연결
Java 클라이언트 사용

다음 섹션에서는 Neptune DB 인스턴스에 연결하고 Apache TinkerPop Gremlin 클라이언트를 사용하여 Gremlin 순회를 수행하는 전체 Java 샘플 실행에 대해 설명합니다.

이러한 지침은 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 따라야 합니다.

**Java를 사용하여 Neptune에 연결하려면**

1. EC2 인스턴스에 Apache Maven을 설치합니다. Amazon Linux 2023(기본 설정)을 사용하는 경우 다음을 사용합니다.

   ```
   sudo dnf update -y
   sudo dnf install maven -y
   ```

   Amazon Linux 2를 사용하는 경우 [https://maven.apache.org/download.cgi](https://maven.apache.org/download.cgi:)에서 최신 바이너리를 다운로드합니다.

   ```
   sudo yum remove maven -y
   wget https://dlcdn.apache.org/maven/maven-3/ <version>/binaries/apache-maven-<version>-bin.tar.gz
   sudo tar -xzf apache-maven-<version>-bin.tar.gz -C /opt/
   sudo ln -sf /opt/apache-maven-<version> /opt/maven
   echo 'export MAVEN_HOME=/opt/maven' >> ~/.bashrc
   echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> ~/.bashrc
   source ~/.bashrc
   ```

1. **Java를 설치합니다.** Gremlin 라이브러리에는 Java 8 또는 11이 필요합니다. 다음과 같이 Java 11을 설치할 수 있습니다.
   + [Amazon Linux 2(AL2)](https://aws.amazon.com/amazon-linux-2)를 사용하는 경우:

     ```
     sudo amazon-linux-extras install java-openjdk11
     ```
   + [Amazon Linux 2023(AL2023)](https://docs.aws.amazon.com/linux/al2023/ug/what-is-amazon-linux.html)을 사용하는 경우:

     ```
     sudo yum install java-11-amazon-corretto-devel
     ```
   + 다른 배포판의 경우 다음 중 적절한 것을 사용하세요.

     ```
     sudo yum install java-11-openjdk-devel
     ```

     또는:

     ```
     sudo apt-get install openjdk-11-jdk
     ```

1. **Java 11을 EC2 인스턴스의 기본 런타임으로 설정:** Java 8을 EC2 인스턴스의 기본 런타임으로 설정하려면 다음을 입력합니다.

   ```
   sudo /usr/sbin/alternatives --config java
   ```

   메시지가 표시되면 Java 11에 대한 숫자를 입력합니다.

1. **다음과 같이 `gremlinjava`라는 새 디렉터리를 생성합니다.**

   ```
   mkdir gremlinjava
   cd gremlinjava
   ```

1.  다음과 같이 `gremlinjava` 디렉터리에서 `pom.xml` 파일을 생성한 다음, 텍스트 편집기에서 엽니다.

   ```
   nano pom.xml
   ```

1. 다음을 `pom.xml` 파일로 복사하고 저장합니다:

   ```
   <project xmlns="https://maven.apache.org/POM/4.0.0"
            xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
     <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.amazonaws</groupId>
     <artifactId>GremlinExample</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>GremlinExample</name>
     <url>https://maven.apache.org</url>
     <dependencies>
       <dependency>
         <groupId>org.apache.tinkerpop</groupId>
         <artifactId>gremlin-driver</artifactId>
         <version>3.7.2</version>
       </dependency>
       <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-jdk14</artifactId>
         <version>1.7.25</version>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>2.5.1</version>
           <configuration>
             <source>11</source>
             <target>11</target>
           </configuration>
         </plugin>
           <plugin>
             <groupId>org.codehaus.mojo</groupId>
             <artifactId>exec-maven-plugin</artifactId>
             <version>1.3</version>
             <configuration>
               <executable>java</executable>
               <arguments>
                 <argument>-classpath</argument>
                 <classpath/>
                 <argument>com.amazonaws.App</argument>
               </arguments>
               <mainClass>com.amazonaws.App</mainClass>
               <complianceLevel>1.11</complianceLevel>
               <killAfter>-1</killAfter>
             </configuration>
           </plugin>
       </plugins>
     </build>
   </project>
   ```
**참고**  
기존 Maven 프로젝트를 수정할 경우 필요한 종속성이 이전 코드에서 강조 표시됩니다.

1. 명령줄에 다음을 입력하여 예제 소스 코드의 하위 디렉터리를 생성합니다(`src/main/java/com/amazonaws/`).

   ```
   mkdir -p src/main/java/com/amazonaws/
   ```

1. `src/main/java/com/amazonaws/` 디렉터리에서 `App.java` 파일을 생성한 다음 텍스트 편집기에서 엽니다.

   ```
   nano src/main/java/com/amazonaws/App.java
   ```

1. 다음을 `App.java` 파일로 복사합니다. *your-neptune-endpoint*를 Neptune DB 인스턴스의 주소로 바꿉니다. `addContactPoint` 메서드에 `https://` 접두사를 포함하지 *마세요*.
**참고**  
사용자의 Neptune DB 인스턴스 호스트 이름을 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   ```
   package com.amazonaws;
   import org.apache.tinkerpop.gremlin.driver.Cluster;
   import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
   import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
   import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
   import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
   import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
   import org.apache.tinkerpop.gremlin.structure.T;
   
   public class App
   {
     public static void main( String[] args )
     {
       Cluster.Builder builder = Cluster.build();
       builder.addContactPoint("your-neptune-endpoint");
       builder.port(8182);
       builder.enableSsl(true);
   
       Cluster cluster = builder.create();
   
       GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
   
       // Add a vertex.
       // Note that a Gremlin terminal step, e.g. iterate(), is required to make a request to the remote server.
       // The full list of Gremlin terminal steps is at https://tinkerpop.apache.org/docs/current/reference/#terminal-steps
       g.addV("Person").property("Name", "Justin").iterate();
   
       // Add a vertex with a user-supplied ID.
       g.addV("Custom Label").property(T.id, "CustomId1").property("name", "Custom id vertex 1").iterate();
       g.addV("Custom Label").property(T.id, "CustomId2").property("name", "Custom id vertex 2").iterate();
   
       g.addE("Edge Label").from(__.V("CustomId1")).to(__.V("CustomId2")).iterate();
   
       // This gets the vertices, only.
       GraphTraversal t = g.V().limit(3).elementMap();
   
       t.forEachRemaining(
         e ->  System.out.println(t.toList())
       );
   
       cluster.close();
     }
   }
   ```

   SSL/TLS(필수)를 사용하여 Neptune에 연결하는 데 도움이 필요하면 [SSL/TLS 구성](#access-graph-gremlin-java-ssl)을 참조하세요.

1. 다음 Maven 명령을 사용하여 샘플을 컴파일하고 실행합니다.

   ```
   mvn compile exec:exec
   ```

이전 예에서는 `g.V().limit(3).elementMap()` 순회를 사용하여 그래프의 처음 두 버텍스의 각 속성의 키와 값 맵을 반환했습니다. 다른 것을 쿼리하려면 해당하는 종료 메서드 중 하나를 사용하여 다른 Gremlin 순회로 바꿉니다.

**참고**  
Gremlin 쿼리의 최종 부분 `.toList()`에서 순회를 서버로 제출하여 평가를 받아야 합니다. 이 메서드 또는 이와 유사한 메서드를 포함시키지 않는 경우에는 쿼리가 Neptune DB 인스턴스로 제출되지 않습니다.  
또한 `addV( )` 단계를 사용할 때와 같이 버텍스 또는 엣지를 추가할 때도 적절한 종료를 추가해야 합니다.

다음 메서드는 쿼리를 Neptune DB 인스턴스로 제출합니다.
+ `toList()`
+ `toSet()`
+ `next()`
+ `nextTraverser()`
+ `iterate()`

## Gremlin Java 클라이언트를 위한 SSL/TLS 구성
SSL/TLS 구성

Neptune에서는 SSL/TLS를 기본적으로 활성화해야 합니다. 일반적으로 Java 드라이버가 `enableSsl(true)`로 구성된 경우 인증서의 로컬 사본으로 `trustStore()` 또는 `keyStore()`를 설정하지 않고도 Neptune에 연결할 수 있습니다.

하지만 연결 중인 인스턴스가 퍼블릭 인증서를 확인하는 데 사용할 수 있는 인터넷 연결이 없거나 사용 중인 인증서가 퍼블릭 인증서가 아닌 경우에는 다음 단계에 따라 로컬 인증서 사본을 구성할 수 있습니다.

**SSL/TLS를 활성화하기 위한 로컬 인증서 사본 설정**

1. Oracle에서 [keytool](https://docs.oracle.com/javase/9/tools/keytool.htm#JSWOR-GUID-5990A2E4-78E3-47B7-AE75-6D1826259549)을 다운로드하여 설치합니다. 이렇게 하면 로컬 키 스토어를 훨씬 쉽게 설정할 수 있습니다.

1. `SFSRootCAG2.pem`CA 인증서를 다운로드합니다. Gremlin Java SDK에는 원격 인증서를 확인하기 위한 인증서가 필요합니다.

   ```
   wget https://www.amazontrust.com/repository/SFSRootCAG2.pem
   ```

1. JKS 또는 PKCS12 형식으로 키 스토어를 생성합니다. 이 예제에서는 JKS를 사용합니다. 프롬프트에서 이어지는 질문에 답하세요. 여기서 생성한 암호를 나중에 사용합니다.

   ```
   keytool -genkey -alias (host name) -keyalg RSA -keystore server.jks
   ```

1. 다운로드한 `SFSRootCAG2.pem` 파일을 새로 만든 키 스토어로 가져옵니다.

   ```
   keytool -import -keystore server.jks -file .pem
   ```

1. 프로그래밍 방식으로 `Cluster` 객체를 구성합니다.

   ```
   Cluster cluster = Cluster.build("(your neptune endpoint)")
                            .port(8182)
                            .enableSSL(true)
                            .keyStore(‘server.jks’)
                            .keyStorePassword("(the password from step 2)")
                            .create();
   ```

   원하는 경우 Gremlin 콘솔에서 수행할 수 있는 것처럼 구성 파일에서 동일한 작업을 수행할 수 있습니다.

   ```
   hosts: [(your neptune endpoint)]
   port: 8182
   connectionPool: { enableSsl: true, keyStore: server.jks, keyStorePassword: (the password from step 2) }
   serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }}
   ```

## IAM 인증


Neptune은 DB 클러스터에 대한 액세스를 제어하는 [IAM 인증을](iam-auth-enable.md) 지원합니다. IAM 인증을 활성화한 경우 서명 버전 4 서명을 사용하여 요청을 인증해야 합니다. Java 클라이언트에서 연결하기 위한 자세한 지침과 코드 예제는 섹션을 참조하세요[Gremlin Java와 함께 IAM을 사용하여 Amazon Neptune 데이터베이스에 연결](iam-auth-connecting-gremlin-java.md).

# 재연결 로직을 사용하여 Neptune DB 인스턴스에 연결하는 Java 예제
Java 재연결 샘플

다음 Java 예제는 재연결 로직을 통해 Gremlin 클라이언트에 연결하여 예상치 못한 연결 끊김을 복구하는 방법을 보여줍니다.

다음과 같은 종속성이 있습니다.

```
<dependency>
    <groupId>org.apache.tinkerpop</groupId>
    <artifactId>gremlin-driver</artifactId>
    <version>${gremlin.version}</version>
</dependency>

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>amazon-neptune-sigv4-signer</artifactId>
    <version>${sig4.signer.version}</version>
</dependency>

<dependency>
    <groupId>com.evanlennick</groupId>
    <artifactId>retry4j</artifactId>
    <version>0.15.0</version>
</dependency>
```

샘플 코드는 다음과 같습니다.

**중요**  
 Retry4j에서 `CallExecutor`는 스레드 세이프가 아닐 수 있습니다. 각 스레드가 자체 `CallExecutor` 인스턴스를 사용하거나 다른 재시도 라이브러리를 사용하도록 하는 것이 좋습니다.

**참고**  
 다음 예제는 requestInterceptor()를 사용하도록 업데이트되었습니다. 이는 TinkerPop 3.6.6에 추가되었습니다. TinkerPop 버전 3.6.6 이전에는 코드 예제에서 해당 릴리스에서 더 이상 사용되지 않는 handshakeInterceptor()를 사용했습니다.

```
public static void main(String args[]) {
  boolean useIam = true;

  // Create Gremlin cluster and traversal source
  Cluster.Builder builder = Cluster.build()
         .addContactPoint(System.getenv("neptuneEndpoint"))
         .port(Integer.parseInt(System.getenv("neptunePort")))
         .enableSsl(true)
         .minConnectionPoolSize(1)
         .maxConnectionPoolSize(1)
         .serializer(Serializers.GRAPHBINARY_V1D0)
         .reconnectInterval(2000);

  if (useIam) {
      builder.requestInterceptor( r -> {
         try {
            NeptuneNettyHttpSigV4Signer sigV4Signer =
                        new NeptuneNettyHttpSigV4Signer("(your region)", new DefaultAWSCredentialsProviderChain());
            sigV4Signer.signRequest(r);
         } catch (NeptuneSigV4SignerException e) {
            throw new RuntimeException("Exception occurred while signing the request", e);
         }
         return r;
      });
   }

  Cluster cluster = builder.create();

  GraphTraversalSource g = AnonymousTraversalSource
      .traversal()
      .withRemote(DriverRemoteConnection.using(cluster));

  // Configure retries
  RetryConfig retryConfig = new RetryConfigBuilder()
      .retryOnCustomExceptionLogic(getRetryLogic())
      .withDelayBetweenTries(1000, ChronoUnit.MILLIS)
      .withMaxNumberOfTries(5)
      .withFixedBackoff()
      .build();

  @SuppressWarnings("unchecked")
  CallExecutor<Object> retryExecutor = new CallExecutorBuilder<Object>()
      .config(retryConfig)
      .build();

  // Do lots of queries
  for (int i = 0; i < 100; i++){
    String id = String.valueOf(i);

    @SuppressWarnings("unchecked")
    Callable<Object> query = () -> g.V(id)
        .fold()
        .coalesce(
            unfold(),
            addV("Person").property(T.id, id))
        .id().next();

    // Retry query
    // If there are connection failures, the Java Gremlin client will automatically
    // attempt to reconnect in the background, so all we have to do is wait and retry.
    Status<Object> status = retryExecutor.execute(query);

    System.out.println(status.getResult().toString());
  }

  cluster.close();
}

private static Function<Exception, Boolean> getRetryLogic() {

  return e -> {

    Class<? extends Exception> exceptionClass = e.getClass();

    StringWriter stringWriter = new StringWriter();
    String message = stringWriter.toString();


    if (RemoteConnectionException.class.isAssignableFrom(exceptionClass)){
      System.out.println("Retrying because RemoteConnectionException");
      return true;
    }

    // Check for connection issues
    if (message.contains("Timed out while waiting for an available host") ||
        message.contains("Timed-out") && message.contains("waiting for connection on Host") ||
        message.contains("Connection to server is no longer active") ||
        message.contains("Connection reset by peer") ||
        message.contains("SSLEngine closed already") ||
        message.contains("Pool is shutdown") ||
        message.contains("ExtendedClosedChannelException") ||
        message.contains("Broken pipe") ||
        message.contains(System.getenv("neptuneEndpoint")))
    {
      System.out.println("Retrying because connection issue");
      return true;
    };

    // Concurrent writes can sometimes trigger a ConcurrentModificationException.
    // In these circumstances you may want to backoff and retry.
    if (message.contains("ConcurrentModificationException")) {
      System.out.println("Retrying because ConcurrentModificationException");
      return true;
    }

    // If the primary fails over to a new instance, existing connections to the old primary will
    // throw a ReadOnlyViolationException. You may want to back and retry.
    if (message.contains("ReadOnlyViolationException")) {
      System.out.println("Retrying because ReadOnlyViolationException");
      return true;
    }

    System.out.println("Not a retriable error");
    return false;
  };
}
```

# Python을 사용하여 Neptune DB 인스턴스에 연결
Python

**중요**  
Neptune 엔진 버전과의 호환성을 위해서는 올바른 Apache TinkerPop Gremlin 드라이버 버전을 선택하는 것이 중요합니다. 호환되지 않는 버전을 사용하면 연결 실패 또는 예기치 않은 동작이 발생할 수 있습니다. 자세한 버전 호환성 정보는 섹션을 참조하세요[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md).

다음 섹션에서는 Amazon Neptune DB 인스턴스에 연결하고 Gremlin 순회를 수행하는 Python 샘플을 실행하는 방법을 설명합니다.

사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

시작하기 전에 다음을 수행하십시오.
+ Python 3.6 이상을 [Python.org 웹 사이트](https://www.python.org/downloads/)에서 다운로드하여 설치합니다.
+ **pip**가 설치되었는지 확인합니다. **pip**를 아직 설치하지 않았거나 확신이 없다면 **pip** 설명서에서 [pip를 설치해야 하나요?](https://pip.pypa.io/en/stable/installing/#do-i-need-to-install-pip)를 참조하세요.
+ Python 설치에 없는 경우 `pip install futures`와 같이 `futures`를 다운로드합니다.



**Python을 사용하여 Neptune에 연결하려면**

1. 다음을 입력하여 `gremlinpython` 패키지를 설치합니다.

   ```
   pip install --user gremlinpython
   ```

1. 이름이 `gremlinexample.py`인 파일을 만들어 텍스트 편집기에서 엽니다.

1. 다음을 `gremlinexample.py` 파일로 복사합니다. *your-neptune-endpoint*를 Neptune DB 클러스터의 주소로 바꾸고 *your-neptune-port*를 Neptune DB 클러스터의 포트로 바꿉니다(기본값: 8182).

   사용자의 Neptune DB 인스턴스 주소를 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

    아래 예제에서는 Gremlin Python에 연결하는 방법을 보여줍니다.

   ```
   import boto3
   import os
   from botocore.auth import SigV4Auth
   from botocore.awsrequest import AWSRequest
   from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
   from gremlin_python.process.anonymous_traversal import traversal
   
   database_url = "wss://your-neptune-endpoint:your-neptune-port/gremlin"
   
   remoteConn = DriverRemoteConnection(database_url, "g")
   
   g = traversal().withRemote(remoteConn)
   
   print(g.inject(1).toList())
   remoteConn.close()
   ```

1. 다음 명령을 입력하여 샘플을 실행합니다.

   ```
   python gremlinexample.py
   ```

   이 예제 끝부분의 Gremlin 쿼리는 목록에서 버텍스(`g.V().limit(2)`)를 반환합니다. 이 목록은 표준 Python `print` 기능으로 인쇄됩니다.
**참고**  
Gremlin 쿼리의 최종 부분 `toList()`에서 순회를 서버로 제출하여 평가를 받아야 합니다. 이 메서드 또는 이와 유사한 메서드를 포함시키지 않는 경우에는 쿼리가 Neptune DB 인스턴스로 제출되지 않습니다.

   다음 메서드는 쿼리를 Neptune DB 인스턴스로 제출합니다.
   + `toList()`
   + `toSet()`
   + `next()`
   + `nextTraverser()`
   + `iterate()`

   

   앞의 예에서는 `g.V().limit(2).toList()` 순회를 사용하여 그래프의 첫 번째 버텍스 두 개를 반환했습니다. 다른 것을 쿼리하려면 해당하는 종료 메서드 중 하나를 사용하여 다른 Gremlin 순회로 바꿉니다.

## IAM 인증


Neptune은 DB 클러스터에 대한 액세스를 제어하는 [IAM 인증을](iam-auth-enable.md) 지원합니다. IAM 인증을 활성화한 경우 서명 버전 4 서명을 사용하여 요청을 인증해야 합니다. Python 클라이언트에서 연결하기 위한 자세한 지침과 코드 예제는 섹션을 참조하세요[Gremlin Python에서 IAM 인증을 사용하여 Amazon Neptune 데이터베이스에 연결](gremlin-python-iam-auth.md).

# .NET을 사용하여 Neptune DB 인스턴스에 연결
.NET

**중요**  
Neptune 엔진 버전과의 호환성을 위해서는 올바른 Apache TinkerPop Gremlin 드라이버 버전을 선택하는 것이 중요합니다. 호환되지 않는 버전을 사용하면 연결 실패 또는 예기치 않은 동작이 발생할 수 있습니다. 자세한 버전 호환성 정보는 섹션을 참조하세요[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md).

다음 섹션에는 Neptune DB 인스턴스에 연결하여 Gremlin 순회를 실행하는 C\$1로 작성된 코드 예제가 나옵니다.

Amazon Neptune에 대한 연결은 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이루어져야 합니다. 샘플 코드는 Ubuntu를 실행하는 Amazon EC2 인스턴스에서 테스트했습니다.

시작하기 전에 다음을 수행하십시오.
+ Amazon EC2 인스턴스에 .NET을 설치합니다. Windows, Linux 및 macOS 등 여러 운영 체제에 .NET을 설치하는 방법은 [.NET 시작하기](https://www.microsoft.com/net/learn/get-started/)를 참조하십시오.
+ 패키지에서 `dotnet add package gremlin.net`을 실행하여 Gremlin.NET을 설치합니다. 자세한 내용은 TinkerPop 설명서의 [Gremlin.NET](https://tinkerpop.apache.org/docs/current/reference/#gremlin-DotNet)을 참조하십시오.



**Gremlin.NET을 사용하여 Neptune에 연결하려면**

1. 새로운 .NET 프로젝트 생성.

   ```
   dotnet new console -o gremlinExample
   ```

1. 새 프로젝트 디렉터리로 디렉터리를 변경합니다.

   ```
   cd gremlinExample
   ```

1. 다음을 `Program.cs` 파일로 복사합니다. *your-neptune-endpoint*를 Neptune DB 인스턴스의 주소로 바꿉니다.

   사용자의 Neptune DB 인스턴스 주소를 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   ```
   using System;
   using System.Threading.Tasks;
   using System.Collections.Generic;
   using Gremlin.Net;
   using Gremlin.Net.Driver;
   using Gremlin.Net.Driver.Remote;
   using Gremlin.Net.Structure;
   using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource;
   namespace gremlinExample
   {
     class Program
     {
       static void Main(string[] args)
       {
         try
         {
           var endpoint = "your-neptune-endpoint";
           // This uses the default Neptune and Gremlin port, 8182
           var gremlinServer = new GremlinServer(endpoint, 8182, enableSsl: true );
           var gremlinClient = new GremlinClient(gremlinServer);
           var remoteConnection = new DriverRemoteConnection(gremlinClient, "g");
           var g = Traversal().WithRemote(remoteConnection);
           g.AddV("Person").Property("Name", "Justin").Iterate();
           g.AddV("Custom Label").Property("name", "Custom id vertex 1").Iterate();
           g.AddV("Custom Label").Property("name", "Custom id vertex 2").Iterate();
           var output = g.V().Limit<Vertex>(3).ToList();
           foreach(var item in output) {
               Console.WriteLine(item);
           }
         }
         catch (Exception e)
         {
             Console.WriteLine("{0}", e);
         }
       }
     }
   }
   ```

1. 다음 명령을 입력하여 샘플을 실행합니다.

   ```
   dotnet run
   ```

   이 예제 끝부분의 Gremlin 쿼리는 테스트를 위한 단일 버텍스 카운트를 반환합니다. 이후 콘솔로 인쇄됩니다.
**참고**  
Gremlin 쿼리의 최종 부분 `Next()`에서 순회를 서버로 제출하여 평가를 받아야 합니다. 이 메서드 또는 이와 유사한 메서드를 포함시키지 않는 경우에는 쿼리가 Neptune DB 인스턴스로 제출되지 않습니다.

   다음 메서드는 쿼리를 Neptune DB 인스턴스로 제출합니다.
   + `ToList()`
   + `ToSet()`
   + `Next()`
   + `NextTraverser()`
   + `Iterate()`

   쿼리 결과를 직렬화하고 반환해야 하는 경우 `Next()`를, 그렇지 않은 경우에는 `Iterate()`를 사용합니다.

   앞의 예제는 `g.V().Limit(3).ToList()` 순회를 사용하여 목록을 반환합니다. 다른 것을 쿼리하려면 해당하는 종료 메서드 중 하나를 사용하여 다른 Gremlin 순회로 바꿉니다.

## IAM 인증


Neptune은 DB 클러스터에 대한 액세스를 제어하는 [IAM 인증을](iam-auth-enable.md) 지원합니다. IAM 인증을 활성화한 경우 서명 버전 4 서명을 사용하여 요청을 인증해야 합니다. .NET 클라이언트에서 연결하기 위한 자세한 지침과 코드 예제는 섹션을 참조하세요[Gremlin .NET에서 IAM 인증을 사용하여 Amazon Neptune 데이터베이스에 연결](gremlin-dotnet-iam-auth.md).

# Node.js를 사용하여 Neptune DB 인스턴스에 연결
Node.js

**중요**  
Neptune 엔진 버전과의 호환성을 위해서는 올바른 Apache TinkerPop Gremlin 드라이버 버전을 선택하는 것이 중요합니다. 호환되지 않는 버전을 사용하면 연결 실패 또는 예기치 않은 동작이 발생할 수 있습니다. 자세한 버전 호환성 정보는 섹션을 참조하세요[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md).

다음 섹션에서는 Amazon Neptune DB 인스턴스에 연결하고 Gremlin 순회를 수행하는 Node.js 샘플을 실행하는 방법을 설명합니다.

사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

시작하기 전에 다음을 수행하십시오.
+ Node.js 8.11 이상 버전이 설치되었는지 확인합니다. 설치되지 않은 경우에는 [Nodejs.org 웹 사이트](https://nodejs.org)에서 Node.js를 다운로드하여 설치합니다.

**Node.js를 사용하여 Neptune에 연결하려면**

1. 다음을 입력하여 `gremlin-javascript` 패키지를 설치합니다.

   ```
   npm install gremlin
   ```

1. 이름이 `gremlinexample.js`인 파일을 만들어 텍스트 편집기에서 엽니다.

1. 다음을 `gremlinexample.js` 파일로 복사합니다. *your-neptune-endpoint*를 Neptune DB 인스턴스의 주소로 바꿉니다.

   사용자의 Neptune DB 인스턴스 주소를 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   ```
   const gremlin = require('gremlin');
   const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
   const Graph = gremlin.structure.Graph;
   
   dc = new DriverRemoteConnection('wss://your-neptune-endpoint:8182/gremlin',{});
   
   const graph = new Graph();
   const g = graph.traversal().withRemote(dc);
   
   g.V().limit(1).count().next().
       then(data => {
           console.log(data);
           dc.close();
       }).catch(error => {
           console.log('ERROR', error);
           dc.close();
       });
   ```

1. 다음 명령을 입력하여 샘플을 실행합니다.

   ```
   node gremlinexample.js
   ```

앞의 예제는 `g.V().limit(1).count().next()` 순회를 사용하여 그래프의 단일 버텍스 카운트를 반환했습니다. 다른 것을 쿼리하려면 해당하는 종료 메서드 중 하나를 사용하여 다른 Gremlin 순회로 바꿉니다.

**참고**  
Gremlin 쿼리의 최종 부분 `next()`에서 순회를 서버로 제출하여 평가를 받아야 합니다. 이 메서드 또는 이와 유사한 메서드를 포함시키지 않는 경우에는 쿼리가 Neptune DB 인스턴스로 제출되지 않습니다.

다음 메서드는 쿼리를 Neptune DB 인스턴스로 제출합니다.
+ `toList()`
+ `toSet()`
+ `next()`
+ `nextTraverser()`
+ `iterate()`

쿼리 결과를 직렬화하고 반환해야 하는 경우 `next()`를, 그렇지 않은 경우에는 `iterate()`를 사용합니다.

**중요**  
이것은 독립 Node.js 예제입니다. AWS Lambda 함수에서 이와 같은 코드를 실행하려는 경우 Neptune Lambda 함수에서 JavaScript를 효율적으로 사용하는 방법에 대한 자세한 내용은 [Lambda 함수 예](lambda-functions-examples.md) 섹션을 참조하세요.

## IAM 인증


Neptune은 DB 클러스터에 대한 액세스를 제어하는 [IAM 인증을](iam-auth-enable.md) 지원합니다. IAM 인증을 활성화한 경우 서명 버전 4 서명을 사용하여 요청을 인증해야 합니다. JavaScript 클라이언트에서 연결하기 위한 자세한 지침과 코드 예제는 섹션을 참조하세요[Gremlin JavaScript에서 IAM 인증을 사용하여 Amazon Neptune 데이터베이스에 연결](gremlin-javascript-iam-auth.md).

# Go를 사용하여 Neptune DB 인스턴스에 연결
Go

**중요**  
Neptune 엔진 버전과의 호환성을 위해서는 올바른 Apache TinkerPop Gremlin 드라이버 버전을 선택하는 것이 중요합니다. 호환되지 않는 버전을 사용하면 연결 실패 또는 예기치 않은 동작이 발생할 수 있습니다. 자세한 버전 호환성 정보는 섹션을 참조하세요[Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md).

**참고**  
gremlingo 3.5.x 버전은 작성하는 Gremlin 쿼리에서 3.4.x 기능만 사용하는 한 TinkerPop 3.4.x 버전과 역호환됩니다.

다음 섹션에서는 Amazon Neptune DB 인스턴스에 연결하고 Gremlin 순회를 수행하는 Go 샘플을 실행하는 방법을 설명합니다.

사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

시작하기 전에 다음을 수행하십시오.
+ Go 1.17 이상을 [go.dev](https://go.dev/dl/) 웹 사이트에서 다운로드하여 설치합니다.

**Go를 사용하여 Neptune에 연결하려면**

1. 빈 디렉터리에서 시작하여 새 Go 모듈을 초기화합니다.

   ```
   go mod init example.com/gremlinExample
   ```

1. gremlin-go를 새 모듈의 종속 구성 요소로 추가합니다.

   ```
   go get github.com/apache/tinkerpop/gremlin-go/v3/driver
   ```

1. 이름이 `gremlinExample.go`인 파일을 생성하여 텍스트 편집기에서 엽니다.

1. 다음을 `gremlinExample.go` 파일에 복사하고 *`(your neptune endpoint)`*를 Neptune DB 인스턴스의 주소로 대체합니다.

   ```
   package main
   
   import (
     "fmt"
     gremlingo "github.com/apache/tinkerpop/gremlin-go/v3/driver"
   )
   
   func main() {
     // Creating the connection to the server.
     driverRemoteConnection, err := gremlingo.NewDriverRemoteConnection("wss://(your neptune endpoint):8182/gremlin",
       func(settings *gremlingo.DriverRemoteConnectionSettings) {
         settings.TraversalSource = "g"
       })
     if err != nil {
       fmt.Println(err)
       return
     }
     // Cleanup
     defer driverRemoteConnection.Close()
   
     // Creating graph traversal
     g := gremlingo.Traversal_().WithRemote(driverRemoteConnection)
   
     // Perform traversal
     results, err := g.V().Limit(2).ToList()
     if err != nil {
       fmt.Println(err)
       return
     }
     // Print results
     for _, r := range results {
       fmt.Println(r.GetString())
     }
   }
   ```
**참고**  
Neptune TLS 인증서 형식은 현재 macOS의 Go 1.18\$1 버전에서 지원되지 않으며, 연결을 시작하려고 할 때 509 오류가 발생할 수 있습니다. 로컬 테스트의 경우 가져오기에 “crypto/tls”를 추가하고 다음과 같이 `DriverRemoteConnection` 설정을 수정하여 이 문제를 건너뛸 수 있습니다.  

   ```
   // Creating the connection to the server.
   driverRemoteConnection, err := gremlingo.NewDriverRemoteConnection("wss://your-neptune-endpoint:8182/gremlin",
     func(settings *gremlingo.DriverRemoteConnectionSettings) {
         settings.TraversalSource = "g"
         settings.TlsConfig = &tls.Config{InsecureSkipVerify: true}
     })
   ```

1. 다음 명령을 입력하여 샘플을 실행합니다.

   ```
   go run gremlinExample.go
   ```

이 예제 끝부분의 Gremlin 쿼리는 조각에서 버텍스(`(g.V().Limit(2))`)를 반환합니다. 그런 다음 이 조각을 반복하여 표준 `fmt.Println` 함수를 사용하여 출력합니다.

**참고**  
Gremlin 쿼리의 최종 부분 `ToList()`에서 순회를 서버로 제출하여 평가를 받아야 합니다. 이 메서드 또는 이와 유사한 메서드를 포함시키지 않는 경우에는 쿼리가 Neptune DB 인스턴스로 제출되지 않습니다.

다음 메서드는 쿼리를 Neptune DB 인스턴스로 제출합니다.
+ `ToList()`
+ `ToSet()`
+ `Next()`
+ `GetResultSet()`
+ `Iterate()`

앞의 예에서는 `g.V().Limit(2).ToList()` 순회를 사용하여 그래프의 첫 번째 버텍스 두 개를 반환했습니다. 다른 것을 쿼리하려면 해당하는 종료 메서드 중 하나를 사용하여 다른 Gremlin 순회로 바꿉니다.

## IAM 인증


Neptune은 DB 클러스터에 대한 액세스를 제어하는 [IAM 인증을](iam-auth-enable.md) 지원합니다. IAM 인증을 활성화한 경우 서명 버전 4 서명을 사용하여 요청을 인증해야 합니다. Go 클라이언트에서 연결하기 위한 자세한 지침과 코드 예제는 섹션을 참조하세요[Gremlin Go에서 IAM 인증을 사용하여 Amazon Neptune 데이터베이스에 연결](gremlin-go-iam-auth.md).

# AWS SDK를 사용하여 Gremlin 쿼리 실행
AWS SDK

 AWS SDK를 사용하면 원하는 프로그래밍 언어를 사용하여 Neptune 그래프에 대해 Gremlin 쿼리를 실행할 수 있습니다. Neptune 데이터 API SDK(서비스 이름 `neptunedata`)는 Gremlin 쿼리를 제출하기 위한 [ExecuteGremlinQuery](https://docs.aws.amazon.com/neptune/latest/data-api/API_ExecuteGremlinQuery.html) 작업을 제공합니다.

이러한 예제는 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/) | 

## Gremlin AWS SDK 예제
예제

다음 예제에서는 `neptunedata` 클라이언트를 설정하고, Gremlin 쿼리를 실행하고, 결과를 인쇄하는 방법을 보여줍니다. *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. 라는 파일을 생성하고 다음 코드를 `gremlinExample.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})
   )
   
   # Use the untyped GraphSON v3 serializer for a cleaner JSON response.
   response = client.execute_gremlin_query(
       gremlinQuery='g.V().limit(1)',
       serializer='application/vnd.gremlin-v3.0+json;types=false'
   )
   
   print(json.dumps(response['result'], indent=2))
   ```

1. 예제를 실행합니다. `python gremlinExample.py` 

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

1. [설치 지침에](https://docs.aws.amazon.com//sdk-for-java/latest/developer-guide/setup.html) 따라 AWS SDK for Java를 설정합니다.

1. 다음 코드를 사용하여를 설정하고`NeptunedataClient`, Gremlin 쿼리를 실행하고, 결과를 인쇄합니다.

   ```
   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.ExecuteGremlinQueryRequest;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteGremlinQueryResponse;
   
   // 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();
   
   // Use the untyped GraphSON v3 serializer for a cleaner JSON response.
   ExecuteGremlinQueryRequest request = ExecuteGremlinQueryRequest.builder()
       .gremlinQuery("g.V().limit(1)")
       .serializer("application/vnd.gremlin-v3.0+json;types=false")
       .build();
   
   ExecuteGremlinQueryResponse response = client.executeGremlinQuery(request);
   
   System.out.println(response.result().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. 라는 파일을 생성하고 다음 코드를 `gremlinExample.js` 붙여 넣습니다.

   ```
   import { NeptunedataClient, ExecuteGremlinQueryCommand } 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);
   
   // Use the untyped GraphSON v3 serializer for a cleaner JSON response.
   const input = {
       gremlinQuery: "g.V().limit(1)",
       serializer: "application/vnd.gremlin-v3.0+json;types=false"
   };
   
   const command = new ExecuteGremlinQueryCommand(input);
   const response = await client.send(command);
   
   console.log(JSON.stringify(response, null, 2));
   ```

1. 예제를 실행합니다. `node gremlinExample.js` 

------

# Gremlin 쿼리 힌트
쿼리 힌트

쿼리 힌트를 사용하여 Amazon Neptune에서 특정 Gremlin 쿼리에 대한 최적화 및 평가 전략을 지정할 수 있습니다.

쿼리 힌트는 다음 구문을 사용하여 쿼리에 `withSideEffect` 단계를 추가하여 지정합니다.

```
g.withSideEffect(hint, value)
```
+ *힌트* – 적용할 힌트 유형을 식별합니다.
+ *값* – 고려하는 시스템 측면의 동작을 결정합니다.

예를 들어 다음은 Gremlin 순회에 `repeatMode` 힌트를 포함하는 방법을 보여줍니다.

**참고**  
모든 Gremlin 쿼리 힌트 부작용에는 `Neptune#`이라는 접두사가 붙습니다.

```
g.withSideEffect('Neptune#repeatMode', 'DFS').V("3").repeat(out()).times(10).limit(1).path()
```

위의 쿼리는 Neptune 엔진에 기본 Neptune인 *폭 우선*(`BFS`) 대신 *깊이 우선*(`DFS`) 그래프를 순회하도록 지시합니다.

다음 단원에서는 사용 가능한 쿼리 힌트 및 해당 사용법에 대한 추가 정보를 제공합니다.

**Topics**
+ [

# Gremlin repeatMode 쿼리 힌트
](gremlin-query-hints-repeatMode.md)
+ [

# Gremlin noReordering 쿼리 힌트
](gremlin-query-hints-noReordering.md)
+ [

# Gremlin typePromotion 쿼리 힌트
](gremlin-query-hints-typePromotion.md)
+ [

# Gremlin useDFE 쿼리 힌트
](gremlin-query-hints-useDFE.md)
+ [

# 결과 캐시 사용을 위한 Gremlin 쿼리 힌트
](gremlin-query-hints-results-cache.md)

# Gremlin repeatMode 쿼리 힌트
repeatMode

Neptune `repeatMode` 쿼리 힌트는 Neptune 엔진이 Gremlin 순회에서 폭 우선, 깊이 우선 또는 청크 깊이 우선으로 `repeat()` 단계를 평가하는 방법을 지정합니다.

`repeat()` 단계의 평가 모드는 단계를 제한된 횟수만큼 반복하는 대신 경로를 검색하거나 따르는 데 사용되는 경우 중요합니다.

## 구문
구문

`repeatMode` 쿼리 힌트는 쿼리에 `withSideEffect` 단계를 추가하여 지정합니다.

```
g.withSideEffect('Neptune#repeatMode', 'mode').gremlin-traversal
```

**참고**  
모든 Gremlin 쿼리 힌트 부작용에는 `Neptune#`이라는 접두사가 붙습니다.

**사용 가능한 모드**
+ `BFS`

  폭 우선 검색

  `repeat()` 단계의 기본 실행 모드입니다. 이 모드에서는 경로를 따라 더 깊이 들어가기 전에 모든 형제 노드를 가져옵니다.

  이 버전은 메모리 집약적이며 한계가 매우 커질 수 있습니다. 쿼리가 메모리가 부족한 상태에서 실행하여 Neptune 엔진에 의해 취소될 위험이 더 높습니다. 이는 다른 Gremlin 구현과 가장 일치합니다.
+ `DFS`

  깊이 우선 검색

  다음 솔루션으로 이동하기 전에 각 경로를 최대 깊이까지 따릅니다.

  이 경우에는 메모리를 적게 사용합니다. 출발점에서부터 다중 홉(hop)까지 단일 경로를 찾는 것과 같은 상황에서 더 나은 성능을 제공할 수 있습니다.
+ `CHUNKED_DFS`

  청크 깊이 우선 검색

  1 노드(`DFS`) 또는 모든 노드(`BFS)`)가 아닌 1,000개의 노드로 구성된 청크에서 그래프 깊이를 우선 탐구하는 하이브리드 접근 방식입니다.

  Neptune 엔진은 경로를 더 깊이 따라가기 전에 각 수준에서 노드를 최대 1,000개 확보합니다.

  이는 속도와 메모리 사용량 간에 균형 잡힌 접근 방식입니다.

  `BFS`을 사용하려고 하지만 쿼리가 너무 많은 메모리를 사용하는 경우에도 유용합니다.



## 예제
예제

다음 단원에서는 Gremlin 순회에 반복 모드가 미치는 영향에 대해 설명합니다.

Neptune에서 `repeat()` 단계의 기본 모드는 모든 순회에 대해 폭 우선(`BFS`) 실행 전략을 수행하는 것입니다.

대부분의 경우 TinkerGraph 구현은 동일한 실행 전략을 사용하지만 경우에 따라 순회 실행을 변경합니다.

예를 들어 TinkerGraph 구현은 다음 쿼리를 수정합니다.

```
g.V("3").repeat(out()).times(10).limit(1).path()
```

이 순회의 `repeat()` 단계는 다음 순회로 '언롤'되어 깊이 우선(`DFS`) 전략이 됩니다.

```
g.V(<id>).out().out().out().out().out().out().out().out().out().out().limit(1).path()
```

**중요**  
Neptune 쿼리 엔진은 이 작업을 자동으로 수행하지 않습니다.

폭 우선(`BFS`)은 기본 실행 전략이며 대부분의 경우 TinkerGraph와 유사합니다. 그러나 깊이 우선(`DFS`) 전략이 더 나은 경우가 있습니다.

 

**BFS(기본값)**  
폭 우선(BFS)은 `repeat()` 연산자의 기본 실행 전략입니다.

```
g.V("3").repeat(out()).times(10).limit(1).path()
```

Neptune 엔진은 10개 홉의 솔루션을 찾기 전에 처음 9개 홉 경계를 완전히 탐색합니다. 이 방법은 최단 경로 쿼리 등 많은 경우에 효과적입니다.

그러나 앞에 나온 예제의 경우 `repeat()` 연산자에 대해 깊이 우선(`DFS`) 모드를 사용하면 순회가 훨씬 빨라집니다.

**DFS**  
다음 쿼리는 `repeat()` 연산자에 깊이 우선(`DFS`) 모드를 사용합니다.

```
g.withSideEffect("Neptune#repeatMode", "DFS").V("3").repeat(out()).times(10).limit(1)
```

이렇게 하면 다음 솔루션을 탐색하기 전에 각 솔루션을 최대 깊이까지 따릅니다.

# Gremlin noReordering 쿼리 힌트
noReordering

Gremlin 순회를 제출할 때 Neptune 쿼리 엔진은 쿼리의 순회 및 재정렬 부분의 구조를 조사하여 평가 및 쿼리 응답 시간에 필요한 작업량을 최소화하려고 합니다. 예를 들어 여러 `has()` 단계와 같이 여러 제약 조건이 있는 순회는 일반적으로 주어진 순서로 평가되지 않지만, 정적 분석을 사용하여 쿼리를 점검한 후에 재정렬됩니다.

Neptune 쿼리 엔진은 어떤 제약 조건이 더 선택적이고 먼저 실행되는지 파악하려고 합니다. 이로 인해 성능이 더 좋아지는 경우가 있지만, Neptune이 쿼리를 평가하도록 선택하는 순서가 항상 최적이 아닐 수도 있습니다.

데이터의 정확한 특성을 알고 있고 쿼리 실행 순서를 수동으로 지정하려는 경우 Neptune `noReordering` 쿼리 힌트를 사용하여 순회가 주어진 순서대로 평가되도록 지정할 수 있습니다.

## 구문
구문

`noReordering` 쿼리 힌트는 쿼리에 `withSideEffect` 단계를 추가하여 지정합니다.

```
g.withSideEffect('Neptune#noReordering', true or false).gremlin-traversal
```

**참고**  
모든 Gremlin 쿼리 힌트 부작용에는 `Neptune#`이라는 접두사가 붙습니다.

**사용 가능한 값**
+ `true`
+ `false`

# Gremlin typePromotion 쿼리 힌트
typePromotion

숫자 값 또는 범위를 필터링하는 Gremlin 순회를 제출할 때 Neptune 쿼리 엔진은 쿼리 실행 시 일반적으로 유형 승격을 사용해야 합니다. 즉, 필터링 대상 값을 포함할 수 있는 모든 유형의 값을 검사해야 합니다.

예를 들어, 55와 같은 값을 필터링하는 경우 엔진은 55와 같은 정수, 55L의 긴 정수, 55.0과 같은 부동 소수점 등을 검색해야 합니다. 각 유형 승격에는 스토리지에 대한 추가 검색이 필요하므로, 겉보기에 간단한 쿼리를 완료하는 데 예상하지 못하게 시간이 오래 걸릴 수 있습니다.

고객 연령 속성이 5보다 큰 모든 버텍스를 검색한다고 가정해 보겠습니다.

```
g.V().has('customerAge', gt(5))
```

순회를 철저하게 실행하려면 Neptune은 쿼리를 확장하여 쿼리하려는 값이 승격될 수 있는 모든 숫자 유형을 검사하도록 해야 합니다. 이 경우 `gt` 필터는 5를 초과하는 정수, 5L 이상의 긴 정수, 5.0을 초과하는 부동 소수점, 5.0을 초과하는 모든 2배수에 적용되어야 합니다. 이러한 각 유형 승격에는 스토리지에 대한 추가 조회가 필요하므로, 이 쿼리에 대해 [Gremlin `profile` API](gremlin-profile-api.md)를 실행하면 숫자 필터당 여러 필터가 표시되며 완료하는 데 예상보다 훨씬 오래 걸립니다.

특정 유형의 값만 찾으면 된다는 사실을 미리 알고 있기 때문에 유형 승격이 불필요한 경우가 많습니다. 이 경우 `typePromotion` 쿼리 힌트를 사용하여 유형 승격을 끄면 쿼리 속도를 크게 높일 수 있습니다.

## 구문
구문

`typePromotion` 쿼리 힌트는 쿼리에 `withSideEffect` 단계를 추가하여 지정합니다.

```
g.withSideEffect('Neptune#typePromotion', true or false).gremlin-traversal
```

**참고**  
모든 Gremlin 쿼리 힌트 부작용에는 `Neptune#`이라는 접두사가 붙습니다.

**사용 가능한 값**
+ `true`
+ `false`

위 쿼리에 대한 유형 승격을 설정 해제하려면 다음을 사용합니다.

```
g.withSideEffect('Neptune#typePromotion', false).V().has('customerAge', gt(5))
```

# Gremlin useDFE 쿼리 힌트
useDFE

이 쿼리 힌트를 사용하면 DFE를 사용하여 쿼리를 실행할 수 있습니다. 기본적으로 Neptune은 이 쿼리 힌트를 `true`로 설정하지 않으면 DFE를 사용하지 않습니다. 이는 [neptune\$1dfe\$1query\$1engine](parameters.md#parameters-instance-parameters-neptune_dfe_query_engine) 인스턴스 파라미터의 기본값이 `viaQueryHint`로 설정되기 때문입니다. 인스턴스 파라미터를 `enabled`로 설정하면 `useDFE` 쿼리 힌트가 `false`로 설정된 쿼리를 제외한 모든 쿼리에 DFE 엔진이 사용됩니다.

쿼리에 DFE를 활성화하는 예제:

```
g.withSideEffect('Neptune#useDFE', true).V().out()
```

# 결과 캐시 사용을 위한 Gremlin 쿼리 힌트
결과 캐시 쿼리 힌트

[쿼리 결과 캐시](gremlin-results-cache.md)가 활성화된 경우 다음 쿼리 힌트를 사용할 수 있습니다.

## Gremlin `enableResultCache` 쿼리 힌트
enableResultCache

값이 `true`인 `enableResultCache` 쿼리 힌트를 사용하면 쿼리 결과가 이미 캐싱된 경우 캐시에서 쿼리 결과가 반환됩니다. 그렇지 않은 경우 새 결과를 반환하고 캐시에서 지워질 때까지 캐싱합니다. 예제:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

나중에 정확히 동일한 쿼리를 다시 실행하여 캐싱된 결과에 액세스할 수 있습니다.

이 쿼리 힌트의 값이 `false`이거나 값이 없으면 쿼리 결과가 캐싱되지 않습니다. 하지만 이 값을 `false`로 설정해도 기존의 캐싱된 결과는 지워지지 않습니다. 캐싱된 결과를 지우려면 `invalidateResultCache` 또는 `invalidateResultCachekey` 힌트를 사용하세요.

## Gremlin `enableResultCacheWithTTL` 쿼리 힌트
enableResultCacheWithTTL

`enableResultCacheWithTTL` 쿼리 힌트는 캐시에 이미 있는 결과의 TTL에는 영향을 주지 않고 캐싱된 결과가 있는 경우 이를 반환합니다. 현재 캐싱된 결과가 없는 경우 쿼리는 새 결과를 반환하고 `enableResultCacheWithTTL` 쿼리 힌트로 지정된 Time to Live(TTL) 동안 해당 결과를 캐싱합니다. Time to Live는 초 단위로 지정됩니다. 예를 들어, 다음 쿼리는 Time to Live 값을 60초로 지정합니다.

```
g.with('Neptune#enableResultCacheWithTTL', 60)
 .V().has('genre','drama').in('likes')
```

60초의 Time to Live 시간이 끝나기 전에 동일한 쿼리(여기에서는 `g.V().has('genre','drama').in('likes')`)를 `enableResultCache` 또는 `enableResultCacheWithTTL` 쿼리 힌트와 함께 사용하여 캐싱된 결과에 액세스할 수 있습니다.

**참고**  
`enableResultCacheWithTTL`로 지정된 Time to Live 값은 이미 캐싱된 결과에는 영향을 주지 않습니다.  
`enableResultCache`를 사용하여 결과를 이전에 캐싱한 경우, 캐시를 먼저 명시적으로 지워야 `enableResultCacheWithTTL`이 새 결과를 생성하고 지정한 TTL 동안 캐싱합니다.
`enableResultCachewithTTL`를 사용하여 결과를 이전에 캐싱한 경우, 이전 TTL이 먼저 만료되어야 `enableResultCacheWithTTL`이 새 결과를 생성하고 지정한 TTL 동안 캐싱합니다.

Time to Live가 지나면 쿼리의 캐싱된 결과가 지워지고 동일한 쿼리의 후속 인스턴스가 새 결과를 반환합니다. `enableResultCacheWithTTL`가 후속 쿼리에 첨부되면 지정된 TTL을 사용하여 새 결과가 캐싱됩니다.

## Gremlin `invalidateResultCacheKey` 쿼리 힌트
invalidateResultCacheKey

`invalidateResultCacheKey` 쿼리 힌트는 `true` 또는 `false` 값을 취할 수 있습니다. `true` 값을 사용하면 `invalidateResultCacheKey`가 연결된 쿼리에 대해 캐싱된 결과가 지워집니다 예를 들어, 다음 예제에서는 쿼리 키 `g.V().has('genre','drama').in('likes')`에 대해 캐싱된 결과가 지워집니다.

```
g.with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

위 예제 쿼리에서는 새 결과가 캐싱되지 않습니다. 기존 캐싱된 결과를 지운 후 새 결과를 캐싱하려는 경우 동일한 쿼리에 `enableResultCache` 또는 `enableResultCacheWithTTL`를 포함할 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

## Gremlin `invalidateResultCache` 쿼리 힌트
invalidateResultCache

`invalidateResultCache` 쿼리 힌트는 `true` 또는 `false` 값을 취할 수 있습니다. `true` 값을 지정하면 결과 캐시의 모든 결과가 지워집니다. 예제:

```
g.with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

위 예제 쿼리에서는 결과가 캐싱되지 않습니다. 기존 캐시를 완전히 지운 후 새 결과를 캐싱하려는 경우 동일한 쿼리에 `enableResultCache` 또는 `enableResultCacheWithTTL`를 포함할 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

## Gremlin `numResultsCached` 쿼리 힌트
numResultsCached

`numResultsCached` 쿼리 힌트는 `iterate()`를 포함하는 쿼리에만 사용할 수 있으며, 힌트가 첨부된 쿼리에 대해 캐싱할 최대 결과 수를 지정합니다. `numResultsCached`가 존재할 때 캐싱된 결과는 반환되지 않고 캐싱되기만 한다는 점에 유의하세요.

예를 들어, 다음 쿼리는 결과를 최대 100개까지 캐싱하되 캐싱된 결과는 반환되지 않도록 지정합니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#numResultsCached', 100)
 .V().has('genre','drama').in('likes').iterate()
```

그런 다음 아래와 같은 쿼리를 사용하여 캐싱된 결과 범위(여기서는 처음 10개)를 검색할 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#numResultsCached', 100)
 .V().has('genre','drama').in('likes').range(0, 10)
```

## Gremlin `noCacheExceptions` 쿼리 힌트
noCacheExceptions

`noCacheExceptions` 쿼리 힌트는 `true` 또는 `false` 값을 취할 수 있습니다. `true` 값이 지정되면 결과 캐시와 관련된 모든 예외가 억제됩니다. 예제:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#noCacheExceptions', true)
 .V().has('genre','drama').in('likes')
```

특히 이 경우 쿼리 결과가 너무 커서 결과 캐시에 담을 수 없는 상황에서 발생하는 `QueryLimitExceededException`이 표시되지 않습니다.

# Gremlin 쿼리 상태 API
쿼리 상태

Gremlin 쿼리의 상태를 가져오려면 HTTP `GET` 또는 `POST`를 사용하여 `https://your-neptune-endpoint:port/gremlin/status` 엔드포인트에 대한 요청을 생성합니다.

## Gremlin 쿼리 상태 요청 파라미터
요청 파라미터
+ **queryId** (*선택 사항*)   –   실행 중인 Gremlin 쿼리의 ID입니다. 지정된 쿼리의 상태만 표시합니다.
+ **IncludeWaiting** (*선택 사항*)   –   대기 중인 모든 쿼리의 상태를 반환합니다.

  일반적으로 실행 중인 쿼리만 응답에 포함되지만, `includeWaiting` 파라미터를 지정하면 대기 중인 모든 쿼리의 상태도 반환됩니다.

## Gremlin 쿼리 상태 응답 구문
응답 구문

```
{
  "acceptedQueryCount": integer,
  "runningQueryCount": integer,
  "queries": [
    {
      "queryId":"guid",
      "queryEvalStats":
        {
          "waited": integer,
          "elapsed": integer,
          "cancelled": boolean
        },
      "queryString": "string"
    }
  ]
}
```

## Gremlin 쿼리 상태 응답 값
응답 값
+ **acceptedQueryCount**   –   대기열에 있는 쿼리를 포함하여 수락되었지만 아직 완료되지 않은 쿼리 수입니다.
+ **runningQueryCount**   –   현재 실행 중인 Gremlin 쿼리의 수입니다.
+ **queries**   –   현재 Gremlin 쿼리 목록입니다.
+ **queryId**   –   쿼리의 GUID ID입니다. Neptune이 ID 값을 각 쿼리에 자동 할당하거나 사용자가 자체 ID를 할당할 수 있습니다([Neptune Gremlin 또는 SPARQL 쿼리에 사용자 지정 ID 주입](features-query-id.md) 참조).
+ **QueryEvalStats**   –   이 쿼리에 대한 통계입니다.
+ **subqueries**   –   이 쿼리에 있는 하위 쿼리의 수입니다.
+ **elapsed**   –   지금까지 쿼리가 실행된 시간(밀리초)입니다.
+ **cancelled**   –   True는 쿼리가 취소되었음을 나타냅니다.
+ **queryString**   –   제출된 쿼리입니다. 이보다 길면 1024자로 잘립니다.
+ **waited**   –   쿼리가 대기한 시간을 밀리초 단위로 나타냅니다.

## Gremlin 쿼리 상태 예제
예제

다음은 상태 명령 실행 시 `curl` 및 HTTP `GET`을 사용한 예입니다.

```
curl https://your-neptune-endpoint:port/gremlin/status
```

이 출력은 실행 중인 쿼리 한 개를 보여줍니다.

```
{
  "acceptedQueryCount":9,
  "runningQueryCount":1,
  "queries": [
    {
      "queryId":"fb34cd3e-f37c-4d12-9cf2-03bb741bf54f",
      "queryEvalStats":
        {
          "waited": 0,
          "elapsed": 23,
          "cancelled": false
        },
      "queryString": "g.V().out().count()"
    }
  ]
}
```

# Gremlin 쿼리 취소
쿼리 취소

Gremlin 쿼리의 상태를 가져오려면 HTTP `GET` 또는 `POST`를 사용하여 `https://your-neptune-endpoint:port/gremlin/status` 엔드포인트에 대한 요청을 생성합니다.

## Gremlin 쿼리 취소 요청 파라미터
요청 파라미터
+ **cancelQuery**   –   취소에 필요합니다. 이 파라미터에 해당하는 값이 없습니다.
+ **queryId**   –   취소할 실행 중 Gremlin 쿼리의 ID입니다.

## Gremlin 쿼리 취소 예제
예제

다음은 쿼리를 취소하는 `curl` 명령의 예입니다.

```
curl https://your-neptune-endpoint:port/gremlin/status \
  --data-urlencode "cancelQuery" \
  --data-urlencode "queryId=fb34cd3e-f37c-4d12-9cf2-03bb741bf54f"
```

취소에 성공하면 HTTP `200` OK를 반환합니다.

# Gremlin 스크립트 기반 세션 지원
Gremlin 스크립트 기반 세션

Amazon Neptune에서는 Gremlin 세션을 암시적 트랜잭션과 함께 사용할 수 있습니다. Gremlin 세션에 대한 자세한 내용은 Apache TinkerPop 설명서의 [세션 고려](http://tinkerpop.apache.org/docs/current/reference/#sessions)를 참조하십시오. 아래 섹션에서는 Java에서 Gremlin 세션을 사용하는 방법을 설명합니다.

**중요**  
현재 Neptune에서 스크립트 기반 세션을 가장 길게 열어 둘 수 있는 시간은 10분입니다. 이 시간 전에 세션을 닫지 않으면 세션 시간이 초과되고 해당 세션 내 모든 것이 롤백됩니다.

**Topics**
+ [

## Gremlin 콘솔의 Gremlin 세션
](#access-graph-gremlin-sessions-console)
+ [

## Gremlin Language Variant의 Gremlin 세션
](#access-graph-gremlin-sessions-glv)

## Gremlin 콘솔의 Gremlin 세션
콘솔의 세션

`session` 파라미터 없이 Gremlin 콘솔에서 원격 연결을 생성하는 경우에는 *세션 없음* 모드에서 원격 연결이 생성됩니다. 이 모드에서는 서버에 제출되는 각각의 요청이 그 자체로 완벽한 트랜잭션으로 처리되기 때문에 요청 간에 상태가 저장되지 않습니다. 요청이 실패하는 경우 해당 요청만 롤백됩니다.

`session` 파라미터를 *사용하는* 원격 연결을 생성하면 원격 연결을 종료할 때까지 지속되는 스크립트 기반 세션이 생성됩니다. 각 세션은 콘솔이 생성하고 사용자에게 반환하는 고유한 UUID로 식별됩니다.

다음은 세션을 생성하는 콘솔의 예제입니다. 쿼리가 제출되고 나면 또 다른 호출이 세션을 닫고 이러한 쿼리를 커밋합니다.

**참고**  
서버 측 리소스를 릴리스하려면 Gremlin 클라이언트를 항상 닫아야 합니다.

```
gremlin> :remote connect tinkerpop.server conf/neptune-remote.yaml session
  . . .
  . . .
gremlin> :remote close
```

자세한 내용은 TinkerPop 설명서의 [세션](http://tinkerpop.apache.org/docs/current/reference/#console-sessions)을 참조하십시오.

세션 동안 실행되는 모든 쿼리는 쿼리가 성공적으로 수행되고 사용자가 원격 연결을 닫아야 커밋되는 단일 트랜잭션을 형성합니다. 쿼리가 실패하거나 사용자가 Neptune에서 지원하는 최대 세션 수명 내에 연결을 닫지 않으면 세션 트랜잭션이 커밋되지 않고 해당 트랜잭션 내 모든 쿼리가 롤백됩니다.

## Gremlin Language Variant의 Gremlin 세션
GLV의 세션

다음 예제에서와 같이 GLV(Gremlin Language Variant)에서는 여러 쿼리를 단일 트랜잭션으로 발행하려면 `SessionedClient` 객체를 생성해야 합니다.

```
try {                              // line 1
  Cluster cluster = Cluster.open();                    // line 2
  Client client = cluster.connect("sessionName");      // line 3
   ...
   ...
} finally {
  // Always close. If there are no errors, the transaction is committed; otherwise, it's rolled back.
  client.close();
}
```

위 예제의 라인 3에서는 해당 클러스터에 대해 설정된 구성 옵션에 따라 `SessionedClient` 객체를 생성합니다. 연결 메서드에 전달할 *sessionName* 문자열은 세션의 고유한 이름이 됩니다. 충돌을 방지하려면 이름에 UUID를 사용합니다.

클라이언트는 세션 트랜잭션이 초기화될 때 이를 시작합니다. 세션 동안 실행되는 모든 쿼리는 사용자가 `client.close( )`을 호출할 때만 커밋됩니다. 또한 단일 쿼리가 실패하거나 사용자가 Neptune에서 지원하는 최대 세션 수명 내에 연결을 닫지 않으면 세션 트랜잭션이 실패하고 해당 트랜잭션 내 모든 쿼리가 롤백됩니다.

**참고**  
서버 측 리소스를 릴리스하려면 Gremlin 클라이언트를 항상 닫아야 합니다.

```
GraphTraversalSource g = traversal().withRemote(conn);

Transaction tx = g.tx();

// Spawn a GraphTraversalSource from the Transaction.
// Traversals spawned from gtx are executed within a single transaction.
GraphTraversalSource gtx = tx.begin();
try {
  gtx.addV('person').iterate();
  gtx.addV('software').iterate();

  tx.commit();
} finally {
    if (tx.isOpen()) {
        tx.rollback();
    }
}
```

# Neptune에서의 Gremlin 트랜잭션
Gremlin 트랜잭션

Gremlin [트랜잭션](transactions.md)이 실행되는 컨텍스트는 여러 가지가 있습니다. Gremlin을 사용할 때는 작업 중인 컨텍스트와 그 의미를 이해하는 것이 중요합니다.
+ **`Script-based`**   –   요청은 다음과 같은 텍스트 기반 Gremlin 문자열을 사용하여 이루어집니다.
  + Java 드라이버 및 `Client.submit(string)` 사용.
  + Gremlin 콘솔 및 `:remote connect` 사용.
  + HTTP API 사용.
+ **`Bytecode-based`**   –   요청은 [Gremlin Language Variants](https://tinkerpop.apache.org/docs/current/reference/#gremlin-drivers-variants)(GLV)의 직렬화된 일반 Gremlin 바이트코드를 사용하여 이루어집니다.

  Java 드라이버 `g = traversal().withRemote(...)`를 사용하는 경우를 예로 들 수 있습니다.

위 컨텍스트 중 하나에는 세션 없이 또는 세션에 바인딩된 상태로 전송되는 요청의 추가 컨텍스트가 있습니다.

**참고**  
 Gremlin 트랜잭션은 항상 커밋되거나 롤백되어야 서버 측 리소스를 릴리스할 수 있습니다. 트랜잭션 중에 오류가 발생하는 경우 실패한 특정 요청뿐만 아니라 전체 트랜잭션을 다시 시도하는 것이 중요합니다.

## 세션 없는 요청
세션 없음

 세션이 없는 경우 요청은 단일 트랜잭션과 동일합니다.

스크립트의 경우 단일 요청으로 전송된 하나 이상의 Gremlin 문이 단일 트랜잭션으로 커밋되거나 롤백된다는 의미입니다. 예제:

```
Cluster cluster = Cluster.open();
Client client = cluster.connect(); // sessionless
// 3 vertex additions in one request/transaction:
client.submit("g.addV();g.addV();g.addV()").all().get();
```

바이트코드의 경우 `g`에서 생성되고 실행되는 각 순회에 대해 세션 없는 요청이 이루어집니다.

```
GraphTraversalSource g = traversal().withRemote(...);

// 3 vertex additions in three individual requests/transactions:
g.addV().iterate();
g.addV().iterate();
g.addV().iterate();

// 3 vertex additions in one single request/transaction:
g.addV().addV().addV().iterate();
```

## 세션에 바인딩된 요청
세션에 바인딩된 요청

세션에 바인딩된 경우 단일 트랜잭션의 컨텍스트 내에서 여러 요청을 적용할 수 있습니다.

스크립트의 경우 모든 그래프 작업을 하나의 포함된 문자열 값으로 연결할 필요가 없다는 의미입니다.

```
Cluster cluster = Cluster.open();
Client client = cluster.connect(sessionName); // session
try {
    // 3 vertex additions in one request/transaction:
    client.submit("g.addV();g.addV();g.addV()").all().get();
} finally {
    client.close();
}

try {
    // 3 vertex additions in three requests, but one transaction:
    client.submit("g.addV()").all().get(); // starts a new transaction with the same sessionName
    client.submit("g.addV()").all().get();
    client.submit("g.addV()").all().get();
} finally {
    client.close();
}
```

바이트코드의 경우 TinkerPop `3.5.x` 이후 트랜잭션을 명시적으로 제어하고 세션을 투명하게 관리할 수 있습니다. Gremlin Language Variants(GLV)는 다음과 같이 Gremlin의 `tx()` 구문을 지원하여 트랜잭션을 `commit()`하거나 `rollback()`합니다.

```
GraphTraversalSource g = traversal().withRemote(conn);

Transaction tx = g.tx();

// Spawn a GraphTraversalSource from the Transaction.
// Traversals spawned from gtx are executed within a single transaction.
GraphTraversalSource gtx = tx.begin();
try {
    gtx.addV('person').iterate();
    gtx.addV('software').iterate();

    tx.commit();
} finally {
    if (tx.isOpen()) {
        tx.rollback();
    }
}
```

위의 예제는 Java로 작성되었지만, Python, Javascript, .NET에서도 이 `tx()` 구문을 사용할 수 있습니다.

**주의**  
세션 없는 읽기 전용 쿼리는 [SNAPSHOT](transactions-isolation-levels.md) 격리 상태에서 실행되지만, 명시적 트랜잭션 내에서 실행되는 읽기 전용 쿼리는 [SERIALIZABLE](transactions-isolation-levels.md) 격리 상태에서 실행됩니다. `SERIALIZABLE` 격리 상태에서 실행되는 읽기 전용 쿼리는 `SNAPSHOT` 격리 상태에서 실행되는 쿼리와 달리 높은 오버헤드를 유발하고 동시 쓰기를 차단하거나 이로 인해 차단될 수 있습니다.

# Amazon Neptune과 함께 Gremlin API 사용
Gremlin API 사용

**참고**  
Amazon Neptune은 `bindings` 속성을 지원하지 않습니다.

Gremlin HTTPS 요청은 모두 단일 엔드포인트(`https://your-neptune-endpoint:port/gremlin`)를 사용합니다. 모든 Neptune 연결은 HTTPS를 사용해야 합니다.

WebSocket을 통해 Gremlin 콘솔을 Neptune 그래프에 직접 연결할 수 있습니다.

Gremlin 엔드포인트 연결에 대한 자세한 내용은 [Gremlin을 사용하여 Neptune 그래프에 액세스](access-graph-gremlin.md) 단원을 참조하십시오.

Amazon Neptune의 Gremlin 구현에는 고려해야 할 특정 세부 정보 및 차이가 있습니다. 자세한 내용은 [Amazon Neptune에 사용되는 Gremlin 표준 규정 준수](access-graph-gremlin-differences.md) 단원을 참조하십시오.

Gremlin 언어 및 순회에 대한 자세한 내용은 Apache TinkerPop 설명서의 [The Traversal](https://tinkerpop.apache.org/docs/current/reference/#traversal)을 참조하십시오.

# Amazon Neptune Gremlin의 쿼리 결과 캐싱
쿼리 결과 캐싱

Amazon Neptune은 Gremlin 쿼리에 대한 결과 캐시를 지원합니다.

쿼리 결과 캐시를 활성화한 다음 쿼리 힌트를 사용하여 Gremlin 읽기 전용 쿼리의 결과를 캐싱할 수 있습니다.

그런 다음 쿼리를 다시 실행하면 캐시에 있는 한 지연 시간이 짧고 I/O 비용 없이 캐싱된 결과가 검색됩니다. 이는 HTTP 엔드포인트와 WebSocket을 사용하여 바이트코드나 문자열 형식으로 제출된 쿼리에 모두 적용됩니다.

**참고**  
쿼리 캐시가 활성화된 경우에도 프로필 엔드포인트로 전송된 쿼리는 캐싱되지 않습니다.

여러 가지 방법으로 Neptune 쿼리 결과 캐시가 동작하는 방식을 제어할 수 있습니다. 예제:
+ 캐싱된 결과를 페이지를 지정한 블록 단위로 표시할 수 있습니다.
+ 지정된 쿼리의 Time To Live(TTL) 값을 지정할 수 있습니다.
+ 지정된 쿼리의 캐시를 지울 수 있습니다.
+ 전체 캐시를 지울 수 있습니다.
+ 결과가 캐시 크기를 초과할 경우 알림을 받도록 설정할 수 있습니다.

캐시는 최근 최소 사용(LRU) 정책을 사용하여 유지 관리됩니다. 즉, 캐시에 할당된 공간이 가득 차면 가장 오랫동안 사용되지 않은 결과를 제거하여 새 결과가 캐싱될 때 공간을 확보합니다.

**중요**  
`t3.medium` 또는 `t4.medium` 인스턴스 유형에서는 쿼리 결과 캐시를 사용할 수 없습니다.

## Neptune에서 쿼리 결과 캐시 활성화
캐시 활성화

 쿼리 결과 캐시는 클러스터의 모든 인스턴스 또는 인스턴스별로 활성화할 수 있습니다. 클러스터의 모든 인스턴스에서 결과 캐시를 활성화하려면 클러스터의 `neptune_result_cache` 파라미터를 `cluster-parameter-group`로 설정합니다`1`. 특정 인스턴스에서 이 기능을 활성화하려면 해당 인스턴스의 `instance-parameter-group` 내 `neptune_result_cache` 파라미터 값을 `1`로 설정하세요. 클러스터 파라미터 그룹 설정은 인스턴스 파라미터 그룹 값을 재정의합니다.

 결과 캐시 파라미터 설정을 적용하려면 영향을 받는 인스턴스에서 다시 시작해야 합니다. `cluster-parameter-group`을 통해 클러스터의 모든 인스턴스에서 결과 캐시를 활성화할 수 있지만 각 인스턴스는 자체 캐시를 유지합니다. 쿼리 결과 캐시 기능은 클러스터 전체 캐시가 아닙니다.

결과 캐시가 활성화되면 Neptune은 쿼리 결과를 캐싱하기 위해 현재 메모리의 일부를 따로 보관합니다. 사용하는 인스턴스 유형이 크고 사용 가능한 메모리가 많을수록 Neptune은 캐시에 더 많은 메모리를 할당합니다.

결과 캐시 메모리가 가득 차면 Neptune은 가장 오랫동안 사용되지 않은(LRU) 캐싱된 결과를 자동으로 삭제하여 새 결과를 제공합니다.

[인스턴스 상태](access-graph-status.md) 명령을 사용하여 결과 캐시의 현재 상태를 확인할 수 있습니다.

## 힌트를 사용하여 쿼리 결과 캐시
캐시 사용

쿼리 결과 캐시가 활성화되면 쿼리 힌트를 사용하여 쿼리 캐싱을 제어할 수 있습니다. 아래의 모든 예제는 동일한 쿼리 순회에 적용됩니다. 즉, 다음과 같습니다.

```
g.V().has('genre','drama').in('likes')
```

### `enableResultCache` 사용하기
enableResultCache

쿼리 결과 캐시가 활성화되면 다음과 같이 `enableResultCache` 쿼리 힌트를 사용하여 Gremlin 쿼리 결과를 캐싱할 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

그러면 Neptune은 쿼리 결과를 반환하고 캐싱도 수행합니다. 나중에 정확히 동일한 쿼리를 다시 실행하여 캐싱된 결과에 액세스할 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

캐싱된 결과를 식별하는 캐시 키는 쿼리 문자열 자체입니다. 즉, 다음과 같습니다.

```
g.V().has('genre','drama').in('likes')
```

### `enableResultCacheWithTTL` 사용하기
enableResultCacheWithTTL

`enableResultCacheWithTTL` 쿼리 힌트를 사용하여 쿼리 결과를 캐싱해야 하는 기간을 지정할 수 있습니다. 예를 들어, 다음 쿼리는 쿼리 결과가 120초 후에 만료되도록 지정합니다.

```
g.with('Neptune#enableResultCacheWithTTL', 120)
 .V().has('genre','drama').in('likes')
```

다시 말하지만, 캐싱된 결과를 식별하는 캐시 키는 기본 쿼리 문자열입니다.

```
g.V().has('genre','drama').in('likes')
```

또한 `enableResultCache` 쿼리 힌트와 함께 해당 쿼리 문자열을 사용하여 캐싱된 결과에 액세스할 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

결과가 캐싱된 후 120초 이상 경과한 경우 해당 쿼리는 새로운 결과를 반환하고 Time To Live 값 없이 캐싱합니다.

`enableResultCacheWithTTL` 쿼리 힌트와 함께 동일한 쿼리를 다시 실행하여 캐싱된 결과에 액세스할 수도 있습니다. 예제:

```
g.with('Neptune#enableResultCacheWithTTL', 140)
 .V().has('genre','drama').in('likes')
```

120초(현재 유효한 TTL)가 경과할 때까지 `enableResultCacheWithTTL` 쿼리 힌트를 사용하는 이 새 쿼리는 캐싱된 결과를 반환합니다. 120초 후에 새 결과를 반환하고 140초의 Time To Live 값으로 캐싱합니다.

**참고**  
쿼리 키의 결과가 이미 캐싱된 경우 `enableResultCacheWithTTL`을 사용한 동일한 쿼리 키는 새 결과를 생성하지 않으며 현재 캐싱된 결과의 Time To Live 값에도 영향을 주지 않습니다.  
`enableResultCache`를 사용하여 결과를 이전에 캐싱한 경우 해당 캐시를 먼저 지워야 `enableResultCacheWithTTL`이 새 결과를 생성하고 지정한 TTL 동안 캐싱합니다.
`enableResultCachewithTTL`를 사용하여 결과를 이전에 캐싱한 경우, 이전 TTL이 먼저 만료되어야 `enableResultCacheWithTTL`이 새 결과를 생성하고 지정한 TTL 동안 캐싱합니다.

### `invalidateResultCacheKey` 사용하기
invalidateResultCacheKey

`invalidateResultCacheKey` 쿼리 힌트를 사용하여 특정 쿼리 하나에 대해 캐싱된 결과를 지울 수 있습니다. 예제:

```
g.with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

이 쿼리는 쿼리 키 `g.V().has('genre','drama').in('likes')`의 캐시를 지우고 해당 쿼리에 대한 새 결과를 반환합니다.

`enableResultCache` 또는 `enableResultCacheWithTTL`과 `invalidateResultCacheKey`를 함께 사용할 수도 있습니다. 예를 들어, 다음 쿼리는 현재 캐싱된 결과를 지우고 새 결과를 캐싱하여 반환합니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

### `invalidateResultCache` 사용하기
invalidateResultCache

`invalidateResultCache` 쿼리 힌트를 사용하여 쿼리 결과 캐시에 있는 캐싱된 결과를 모두 지울 수 있습니다. 예제:

```
g.with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

이 쿼리는 전체 결과 캐시를 지우고 쿼리에 대한 새 결과를 반환합니다.

`enableResultCache` 또는 `enableResultCacheWithTTL`과 `invalidateResultCache`를 함께 사용할 수도 있습니다. 예를 들어, 다음 쿼리는 전체 결과 캐시를 지우고 이 쿼리의 새 결과를 캐싱하여 반환합니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

## 캐싱된 쿼리 결과 페이지 매김
캐싱된 결과 페이지 매김

다음과 같은 결과를 이미 대량으로 캐싱했다고 가정해 봅시다.

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

이제 다음과 같은 범위 쿼리를 실행한다고 가정해 보겠습니다.

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes').range(0,10)
```

Neptune은 먼저 전체 캐시 키(`g.V().has('genre','drama').in('likes').range(0,10)`)를 찾습니다. 해당 키가 존재하지 않는 경우 Neptune은 다음으로 범위(`g.V().has('genre','drama').in('likes')`)가 없는 해당 쿼리 문자열에 키가 있는지 확인합니다. Neptune은 해당 키를 찾으면 범위에 지정된 대로 캐시에서 처음 10개의 결과를 가져옵니다.

**참고**  
끝에 범위가 있는 쿼리에 `invalidateResultCacheKey` 쿼리 힌트를 사용하는 경우 Neptune은 범위가 있는 쿼리와 정확히 일치하는 항목을 찾지 못하면 범위가 없는 쿼리의 캐시를 지웁니다.

### `.iterate()`과 함께 `numResultsCached` 사용
numResultsCached

`numResultsCached` 쿼리 힌트를 사용하면 캐싱되는 모든 결과를 반환하지 않고 결과 캐시를 채울 수 있습니다. 이는 많은 결과를 페이지로 나누려는 경우에 유용할 수 있습니다.

`numResultsCached` 쿼리 힌트는 `iterate()`로 끝나는 쿼리에만 사용할 수 있습니다.

예를 들어, 샘플 쿼리의 처음 50개 결과를 캐싱하려는 경우는 다음과 같습니다.

```
g.with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').iterate()
```

이 경우 캐시의 쿼리 키는 `g.with("Neptune#numResultsCached", 50).V().has('genre','drama').in('likes')`입니다. 이제 다음 쿼리로 캐싱된 결과 중 처음 10개를 검색할 수 있습니다.

```
g.with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').range(0, 10)
```

또한 아래와 같이 쿼리에서 다음 10개 결과를 검색할 수 있습니다.

```
g.with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').range(10, 20)
```

잊지 말고 `numResultsCached` 힌트를 포함하세요. 이는 쿼리 키의 필수 부분이므로, 캐싱된 결과에 액세스하려면 반드시 있어야 합니다.

**`numResultsCached` 사용 시 유의 사항**
+ **`numResultsCached`와 함께 제공하는 번호는 쿼리 끝에 적용됩니다.**   예를 들어, 다음 쿼리가 실제로 캐싱되면 범위 `(1000, 1500)`이 됩니다.

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 500)
   .V().range(1000, 2000).iterate()
  ```
+ **`numResultsCached`와 함께 제공하는 숫자는 캐싱할 최대 결과 수를 지정합니다.**   예를 들어, 다음 쿼리가 실제로 캐싱되면 범위 `(1000, 2000)`이 됩니다.

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 100000)
   .V().range(1000, 2000).iterate()
  ```
+ **`.range().iterate()`로 끝나는 쿼리에 의해 캐싱된 결과에는 고유한 범위가 있습니다.**   예를 들어, 다음과 같은 쿼리를 사용하여 결과를 캐싱한다고 가정해 봅시다.

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 500)
   .V().range(1000, 2000).iterate()
  ```

  캐시에서 처음 100개의 결과를 검색하려면 다음과 같은 쿼리를 작성합니다.

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 500)
   .V().range(1000, 2000).range(0, 100)
  ```

  100개의 결과는 범위 `(1000, 1100)` 내 기본 쿼리의 결과와 동일합니다.

## 캐싱된 결과를 찾는 데 사용되는 쿼리 캐시 키
쿼리 캐시 키

쿼리 결과가 캐싱된 후 동일한 *쿼리 캐시 키*를 사용하는 후속 쿼리는 새 쿼리를 생성하지 않고 캐시에서 결과를 검색합니다. 쿼리의 쿼리 캐시 키는 다음과 같이 평가됩니다.

1. `numResultsCached`를 제외한 모든 캐시 관련 쿼리 힌트는 무시됩니다.

1. 마지막 `iterate()` 단계는 무시됩니다.

1. 나머지 쿼리는 바이트코드 표현에 따라 정렬됩니다.

결과 문자열을 캐시에 이미 있는 쿼리 결과의 인덱스와 비교하여 쿼리에 대한 캐시 적중 여부를 확인합니다.

다음 쿼리를 예로 들어 보겠습니다.

```
g.withSideEffect('Neptune#typePromotion', false).with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').iterate()
```

이 파일은 다음과 같은 바이트코드 버전으로 저장됩니다.

```
g.withSideEffect('Neptune#typePromotion', false)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes')
```

## 결과 캐시와 관련된 예외
예외

이전에 캐싱된 모든 항목을 제거한 후에도 캐싱하려는 쿼리 결과가 너무 커서 캐시 메모리에 담을 수 없는 경우 Neptune에서 `QueryLimitExceededException` 오류가 발생합니다. 결과는 반환되지 않으며 예외로 인해 다음과 같은 오류 메시지가 생성됩니다.

```
The result size is larger than the allocated cache,
      please refer to results cache best practices for options to rerun the query.
```

다음과 같이 `noCacheExceptions` 쿼리 힌트를 사용하여 이 메시지를 숨길 수 있습니다.

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#noCacheExceptions', true)
 .V().has('genre','drama').in('likes')
```

# Gremlin `mergeV()` 및 `mergeE()` 단계를 사용하여 효율적인 업서트 생성
3.6.x 이후의 효율적인 업서트

업서트(또는 조건부 삽입)는 버텍스나 엣지가 이미 있는 경우 이를 다시 사용하고 존재하지 않는 경우 이를 생성합니다. 효율적인 업서트는 Gremlin 쿼리의 성능에 상당한 차이를 만들 수 있습니다.

업서트를 사용하면 멱등식 삽입 연산을 작성할 수 있습니다. 이러한 연산을 몇 번 실행해도 전체 결과는 동일합니다. 이는 그래프의 동일한 부분을 동시에 수정하면 하나 이상의 트랜잭션이 `ConcurrentModificationException`와 함께 롤백되어 재시도가 필요한 동시 쓰기 시나리오에서 유용합니다.

예를 들어, 다음 쿼리는 제공된 `Map`을 사용하여 `"v-1"`의 `T.id`이 있는 버텍스를 먼저 찾도록 하여 버텍스를 업서트합니다. 해당 버텍스를 찾으면 버텍스가 반환됩니다. 찾을 수 없는 경우 해당 `id` 및 속성을 가진 버텍스가 `onCreate` 절을 통해 생성됩니다.

```
g.mergeV([(id):'v-1']).
  option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org'])
```

## 일괄 처리 업서트로 처리량 향상
일괄 처리 업서트

쓰기 처리량이 높은 시나리오의 경우 `mergeV()` 및 `mergeE()` 단계를 조합하여 버텍스와 엣지를 일괄적으로 업서트할 수 있습니다. 일괄 처리를 사용하면 많은 수의 버텍스와 엣지를 업서트할 때 발생하는 트랜잭션 오버헤드가 줄어듭니다. 그런 다음 여러 클라이언트를 통해 배치 요청을 병렬로 업서트하여 처리량을 더욱 개선할 수 있습니다.

일반적으로 배치 요청당 약 200개의 레코드를 업서트하는 것이 좋습니다. 레코드는 단일 버텍스, 엣지 레이블 또는 속성입니다. 예를 들어, 레이블이 하나이고 속성이 4개인 버텍스는 5개의 레코드를 생성합니다. 레이블과 단일 속성이 있는 엣지는 레코드 2개를 생성합니다. 각각 레이블이 하나이고 속성이 4개인 버텍스 배치를 업서트하려면 `200 / (1 + 4) = 40`이므로 배치 크기를 40으로 시작해야 합니다.

배치 크기를 시험해 볼 수 있습니다. 처음에는 배치당 200개의 레코드가 좋지만, 워크로드에 따라 배치 크기가 더 크거나 작을 수 있습니다. 하지만 Neptune에서 요청당 전체 Gremlin 단계 수를 제한할 수 있다는 점에 유의하세요. 이 제한은 문서화되어 있지 않지만, 안전을 위해 요청에 포함된 Gremlin 단계가 1,500개를 넘지 않도록 하는 것이 좋습니다. Neptune은 1,500단계가 넘는 대규모 배치 요청을 거부할 수 있습니다.

처리량을 늘리려면 여러 클라이언트를 사용하여 배치를 병렬로 업서트할 수 있습니다([효율적인 멀티스레드 Gremlin 쓰기 생성](best-practices-gremlin-multithreaded-writes.md) 참조). 클라이언트 수는 Neptune 라이터 인스턴스의 작업자 스레드 수와 같아야 합니다. 이 수는 일반적으로 서버 vCPU 수의 2배입니다. 예를 들어, `r5.8xlarge` 인스턴스에는 32개의 vCPU와 64개의 작업자 스레드가 있습니다. `r5.8xlarge`를 사용하는 처리량이 높은 쓰기 시나리오의 경우 64개의 클라이언트가 Neptune에 배치 업서트를 병렬로 작성하는 것이 좋습니다.

각 클라이언트는 배치 요청을 제출하고 요청이 완료될 때까지 기다렸다가 다른 요청을 제출해야 합니다. 여러 클라이언트가 병렬로 실행되더라도 각 개별 클라이언트는 순차적으로 요청을 제출합니다. 이렇게 하면 서버 측 요청 대기열에 과부하가 걸리지 않고 모든 작업자 스레드를 점유하는 요청 스트림이 서버에 꾸준히 공급됩니다([Neptune DB 클러스터의 DB 인스턴스 크기 조정](feature-overview-db-clusters.md#feature-overview-sizing-instances) 참조).

## Traverser가 여러 개 생성되는 단계 피하기
여러 개의 Traverser 피하기

Gremlin 단계가 실행되면 들어오는 Traverser를 받아 하나 이상의 출력 Traverser를 내보냅니다. 한 단계에서 내보내는 Traverser 수에 따라 다음 단계가 실행되는 횟수가 결정됩니다.

일반적으로 배치 작업을 수행할 때는 버텍스 A 업서트와 같은 각 작업을 한 번 실행하여 버텍스 A 업서트, 버텍스 B 업서트, 버텍스 C 업서트 식의 작업 순서를 이루도록 하려고 합니다. 단계가 한 요소만 생성하거나 수정하는 경우 하나의 Traverser만 내보내고 다음 작업을 나타내는 단계는 한 번만 실행됩니다. 반면, 작업에서 2개 이상의 요소를 만들거나 수정하면 여러 Traverser가 생성되어 후속 단계가 생성된 Traverser당 한 번씩 여러 번 실행됩니다. 이로 인해 데이터베이스에서 불필요한 추가 작업이 수행될 수 있으며, 경우에 따라 원하지 않는 추가 버텍스, 엣지 또는 속성값이 생성될 수 있습니다.

문제가 발생할 수 있는 예로는 `g.V().addV()`와 같은 쿼리를 들 수 있습니다. 이 간단한 쿼리는 그래프에 있는 모든 버텍스에 버텍스를 추가합니다. `V()`는 그래프의 각 버텍스에 대해 Traverser를 내보내고 각 Traverser가 `addV()`에 대한 호출을 트리거하기 때문입니다.

여러 Traverser를 내보낼 수 있는 연산을 처리하는 방법은 [업서트와 삽입 혼합](#gremlin-upserts-and-inserts) 섹션을 참조하세요.

## 버텍스 업서트
버텍스 업서트

이 `mergeV()` 단계는 버텍스 업서트를 위해 특별히 설계되었습니다. 그래프의 기존 버텍스와 일치하는 요소를 나타내는 `Map`을 인수로 사용하고 요소를 찾을 수 없는 경우 해당 `Map`을 사용하여 새 버텍스를 만듭니다. 또한 이 단계를 통해 생성 또는 일치 시 동작을 변경할 수 있으며, `option()` 복조기를 `Merge.onCreate` 및 `Merge.onMatch` 토큰과 함께 적용하여 각각의 동작을 제어할 수 있습니다. 이 단계를 사용하는 방법에 대한 자세한 내용은 TinkerPop [참조 설명서](https://tinkerpop.apache.org/docs/current/reference/#mergevertex-step)를 참조하세요.

버텍스 ID를 사용하여 특정 버텍스가 존재하는지 확인할 수 있습니다. Neptune은 ID와 관련된 동시 사용 사례가 많은 경우 업서트를 최적화하므로, 이 접근 방식은 선호됩니다. 예를 들어, 다음 쿼리는 지정된 버텍스 ID가 없는 경우 버텍스를 생성하고 존재하는 경우 이를 재사용합니다.

```
g.mergeV([(T.id): 'v-1']).
    option(onCreate, [(T.label): 'PERSON', email: 'person-1@example.org', age: 21]).
    option(onMatch, [age: 22]).
  id()
```

참고로 이 쿼리는 `id()` 단계로 끝납니다. 버텍스 업서트를 위해 반드시 필요하지는 않지만, `id()` 단계는 업서트 쿼리의 끝까지 서버가 모든 버텍스 속성을 클라이언트로 다시 직렬화하지 않도록 보장하므로 쿼리의 잠금 비용을 줄이는 데 도움이 됩니다.

또는 버텍스 속성을 사용하여 버텍스를 식별할 수 있습니다.

```
g.mergeV([email: 'person-1@example.org']).
    option(onCreate, [(T.label): 'PERSON', age: 21]).
    option(onMatch, [age: 22]).
  id()
```

가능하면 사용자가 제공한 자체 ID를 사용하여 버텍스를 만들고 이 ID를 사용하여 업서트 작업 중에 버텍스가 존재하는지 확인하세요. 이를 통해 Neptune은 업서트를 최적화할 수 있습니다. 동시 수정이 흔한 경우 ID 기반 업서트가 속성 기반 업서트보다 훨씬 더 효율적일 수 있습니다.

### 버텍스 업서트 연결
버텍스 업서트 연결

버텍스 업서트를 체인으로 연결하여 일괄적으로 삽입할 수 있습니다.

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .id()
```

또는 다음 `mergeV()` 구문을 사용할 수 있습니다.

```
g.mergeV([(T.id): 'v-1', (T.label): 'PERSON', email: 'person-1@example.org']).
  mergeV([(T.id): 'v-2', (T.label): 'PERSON', email: 'person-2@example.org']).
  mergeV([(T.id): 'v-3', (T.label): 'PERSON', email: 'person-3@example.org'])
```

하지만 이 형식의 쿼리에는 `id`별 기본 조회에 불필요한 요소가 검색 기준에 포함되므로, 이전 쿼리만큼 효율적이지 않습니다.

## 엣지 업서트
엣지 업서트

이 `mergeE()` 단계는 엣지 업서트를 위해 특별히 설계되었습니다. 그래프의 기존 엣지와 일치하는 요소를 나타내는 `Map`을 인수로 사용하고 요소를 찾을 수 없는 경우 해당 `Map`을 사용하여 새 엣지를 만듭니다. 또한 이 단계를 통해 생성 또는 일치 시 동작을 변경할 수 있으며, `option()` 복조기를 `Merge.onCreate` 및 `Merge.onMatch` 토큰과 함께 적용하여 각각의 동작을 제어할 수 있습니다. 이 단계를 사용하는 방법에 대한 자세한 내용은 TinkerPop [참조 설명서](https://tinkerpop.apache.org/docs/current/reference/#mergeedge-step)를 참조하세요.

사용자 지정 버텍스 ID를 사용하여 버텍스를 업서트하는 것과 같은 방식으로 엣지 ID를 사용하여 엣지를 업서트할 수 있습니다. 다시 말하지만, 이 방법을 사용하면 Neptune에서 쿼리를 최적화할 수 있어 선호됩니다. 예를 들어, 다음 쿼리는 엣지가 아직 없으면 엣지 ID를 기반으로 엣지를 생성하고 존재하면 재사용합니다. 또한 쿼리는 새 엣지를 만들어야 하는 경우 `Direction.from` 및 `Direction.to` 버텍스의 ID를 사용합니다.

```
g.mergeE([(T.id): 'e-1']).
    option(onCreate, [(from): 'v-1', (to): 'v-2', weight: 1.0]).
    option(onMatch, [weight: 0.5]).
  id()
```

참고로 이 쿼리는 `id()` 단계로 끝납니다. 엣지 업서트를 위해 반드시 필요하지는 않지만, 업서트 쿼리의 끝에 `id()` 단계를 추가하면 서버가 모든 엣지 속성을 클라이언트로 다시 직렬화하지 않도록 보장하므로 쿼리의 잠금 비용을 줄이는 데 도움이 됩니다.

많은 애플리케이션이 사용자 지정 버텍스 ID를 사용하지만, Neptune은 자체적으로 엣지 ID를 생성합니다. 엣지의 ID는 몰라도 `from` 및 `to` 버텍스 ID는 알고 있다면 다음과 같은 쿼리를 사용하여 엣지를 업서트할 수 있습니다.

```
g.mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']).
  id()
```

`mergeE()`에서 참조하는 모든 버텍스는 엣지를 만드는 단계에 존재해야 합니다.

### 엣지 업서트 연결
엣지 업서트 연결

버텍스 업서트와 마찬가지로 배치 요청의 경우 `mergeE()` 단계를 서로 연결하는 것도 간단합니다.

```
g.mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']).
  mergeE([(from): 'v-2', (to): 'v-3', (T.label): 'KNOWS']).
  mergeE([(from): 'v-3', (to): 'v-4', (T.label): 'KNOWS']).
  id()
```

## 버텍스 업서트와 엣지 업서트의 결합
업서트 결합

버텍스와 버텍스를 연결하는 엣지를 모두 업서트해야 하는 경우가 있을 수 있습니다. 여기에 제시된 배치 예제를 혼합하여 사용할 수 있습니다. 다음 예제에서는 버텍스 3개와 엣지 2개를 업서트합니다.

```
g.mergeV([(id):'v-1']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org']).
  mergeV([(id):'v-2']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-2@example.org']).
  mergeV([(id):'v-3']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-3@example.org']).
  mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']).
  mergeE([(from): 'v-2', (to): 'v-3', (T.label): 'KNOWS']).
 id()
```

## 업서트와 삽입 혼합
업서트와 삽입 혼합

버텍스와 버텍스를 연결하는 엣지를 모두 업서트해야 하는 경우가 있을 수 있습니다. 여기에 제시된 배치 예제를 혼합하여 사용할 수 있습니다. 다음 예제에서는 버텍스 3개와 엣지 2개를 업서트합니다.

업서트는 일반적으로 한 번에 한 요소씩 진행됩니다. 여기에 제시된 업서트 패턴을 고수하면 각 업서트 작업에서 Traverser가 하나만 생성되어 후속 작업이 한 번만 실행됩니다.

하지만 업서트와 삽입을 섞어서 사용해야 하는 경우도 있습니다. 예를 들어, 엣지를 사용하여 동작이나 이벤트의 인스턴스를 나타내는 경우가 이에 해당합니다. 요청에서는 업서트를 사용하여 필요한 버텍스가 모두 존재하는지 확인한 다음, 삽입을 통해 엣지를 추가할 수 있습니다. 이런 요청 유형의 경우 각 작업에서 발생할 수 있는 잠재적 Traverser 수에 유의하세요.

업서트와 삽입을 혼합하여 이벤트를 나타내는 엣지를 그래프에 추가하는 다음 예제를 생각해 보세요.

```
// Fully optimized, but inserts too many edges
g.mergeV([(id):'v-1']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org']).
  mergeV([(id):'v-2']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-2@example.org']).
  mergeV([(id):'v-3']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-3@example.org']).
  mergeV([(T.id): 'c-1', (T.label): 'CITY', name: 'city-1']).
  V('p-1', 'p-2').
  addE('FOLLOWED').to(V('p-1')).
  V('p-1', 'p-2', 'p-3').
  addE('VISITED').to(V('c-1')).
  id()
```

쿼리는 FOLLOWED 엣지 2개와 VISITED 엣지 3개의 총 5개의 엣지를 삽입해야 합니다. 하지만 쿼리를 작성하면 8개(FOLLOWED 2개, VISITED 6개)가 삽입됩니다. 그 이유는 FOLLOWED 엣지 2개를 삽입하는 작업에서 2개의 Traverser가 발생하여 3개의 엣지를 삽입하는 후속 삽입 작업이 2번씩 실행되기 때문입니다.

잠재적으로 2개 이상의 Traverser를 내보낼 수 있는 각 작업 뒤에 `fold()` 단계를 추가하면 문제를 해결할 수 있습니다.

```
g.mergeV([(T.id): 'v-1', (T.label): 'PERSON', email: 'person-1@example.org']).
  mergeV([(T.id): 'v-2', (T.label): 'PERSON', email: 'person-2@example.org']).
  mergeV([(T.id): 'v-3', (T.label): 'PERSON', email: 'person-3@example.org']).
  mergeV([(T.id): 'c-1', (T.label): 'CITY', name: 'city-1']).
  V('p-1', 'p-2').
  addE('FOLLOWED').
    to(V('p-1')).
  fold().
  V('p-1', 'p-2', 'p-3').
  addE('VISITED').
    to(V('c-1')).
  id()
```

여기서는 FOLLOWED 엣지를 삽입하는 작업 뒤에 `fold()` 단계를 삽입했습니다. 그 결과 단일 Traverser가 생성되어 후속 작업이 한 번만 실행됩니다.

이 접근 방식의 단점은 `fold()`가 최적화되지 않아 쿼리가 완전히 최적화되지 않았다는 것입니다. `fold()` 이후 발생하는 삽입 작업도 이제 최적화되지 않습니다.

후속 단계를 대신하여 Traverser 수를 줄이는 데 `fold()`를 사용해야 하는 경우 가장 비용이 적게 드는 작업이 쿼리에서 최적화되지 않은 부분을 차지하도록 작업을 정렬해 보세요.

## 카디널리티 설정
카디널리티 설정

 Neptune의 버텍스 속성에 대한 기본 카디널리티가 설정되어 있습니다. 즉, mergeV()를 사용할 때 맵에 제공된 값에 모두 해당 카디널리티가 부여됩니다. 단일 카디널리티를 사용하려면 해당 사용에 명시적이어야 합니다. TinkerPop 3.7.0부터 다음 예제와 같이 카디널리티를 맵의 일부로 제공할 수 있는 새로운 구문이 있습니다.

```
g.mergeV([(T.id): '1234']).
  option(onMatch, ['age': single(20), 'name': single('alice'), 'city': set('miami')])
```

 또는 해당 `option`에 대해 기본값으로 카디널리티를 다음과 같이 설정할 수 있습니다.

```
// age and name are set to single cardinality by default
g.mergeV([(T.id): '1234']).
  option(onMatch, ['age': 22, 'name': 'alice', 'city': set('boston')], single)
```

 버전 3.7.0 이전의 `mergeV()`에서는 카디널리티 설정을 위한 옵션이 더 적습니다. 일반적인 접근 방식은 다음과 같이 `property()` 단계로 대체하는 것입니다: 

```
g.mergeV([(T.id): '1234']). 
  option(onMatch, sideEffect(property(single,'age', 20).
  property(set,'city','miami')).constant([:]))
```

**참고**  
 이 접근 방식은 시작 단계와 함께 사용될 때만 `mergeV()`에서 작동합니다. 따라서 단일 순회 내에서 `mergeV()`를 연속적으로 호출할 수 없습니다. 시작 단계 이후에 이 구문을 사용하는 첫 번째 `mergeV()`는 들어오는 순회자가 그래프 요소일 경우 오류를 발생시키기 때문입니다. 이 경우 `mergeV()` 호출을 각각 시작 단계가 될 수 있는 여러 요청으로 나누고 싶을 것입니다.

# `fold()/coalesce()/unfold()`를 사용하여 효율적인 Gremlin 업서트 만들기
3.6.x 이전의 효율적인 업서트

업서트(또는 조건부 삽입)는 버텍스나 엣지가 이미 있는 경우 이를 다시 사용하고 존재하지 않는 경우 이를 생성합니다. 효율적인 업서트는 Gremlin 쿼리의 성능에 상당한 차이를 만들 수 있습니다.

이 페이지에서는 `fold()/coalesce()/unfold()` Gremlin 패턴을 사용하여 효율적인 업서트를 만드는 방법을 보여줍니다. 그러나 엔진 버전 [1.2.1.0](engine-releases-1.2.1.0.md)에서 Neptune에 도입된 TinkerPop 버전 3.6.x가 출시됨에 따라 대부분의 경우 새로운 `mergeV()` 및 `mergeE()` 단계가 선호됩니다. 여기에 설명된 `fold()/coalesce()/unfold()` 패턴은 일부 복잡한 상황에서도 여전히 유용할 수 있지만, 일반적으로 [Gremlin `mergeV()` 및 `mergeE()` 단계를 사용하여 효율적인 업서트 생성](gremlin-efficient-upserts.md)에서 설명한 대로 가능한 경우 `mergeV()` 및 `mergeE()`를 사용합니다.

업서트를 사용하면 멱등식 삽입 연산을 작성할 수 있습니다. 이러한 연산을 몇 번 실행해도 전체 결과는 동일합니다. 이는 그래프의 동일한 부분을 동시에 수정하면 하나 이상의 트랜잭션이 `ConcurrentModificationException`와 함께 롤백되어 재시도가 필요한 동시 쓰기 시나리오에서 유용합니다.

예를 들어, 다음 쿼리는 데이터 세트에서 지정된 버텍스를 먼저 찾은 후 해당 결과를 목록으로 접어 버텍스를 업서트합니다. `coalesce()` 단계에 제공된 첫 번째 순회에서 쿼리는 이 목록을 펼칩니다. 펼친 목록이 비어 있지 않으면 `coalesce()`에서 결과가 출력됩니다. 그러나 버텍스가 현재 존재하지 않아 `unfold()`에서 빈 컬렉션을 반환하면 `coalesce()`는 제공된 두 번째 순회를 평가하기 위해 이동하며, 이 두 번째 순회에서는 쿼리가 누락된 버텍스를 생성합니다.

```
g.V('v-1').fold()
          .coalesce(
             unfold(),
             addV('Person').property(id, 'v-1')
                           .property('email', 'person-1@example.org')
           )
```

## 업서트용으로 최적화된 `coalesce()` 형식 사용
`coalesce()` 최적화

Neptune은 업데이트 처리량을 높이기 위해 `fold().coalesce(unfold(), ...)` 관용구를 최적화할 수 있지만, 이 최적화는 `coalesce()`의 두 부분이 모두 버텍스 또는 엣지를 반환하고 그 외에는 아무것도 반환하지 않는 경우에만 작동합니다. `coalesce()`의 일부에서 속성 등의 다른 요소를 반환하려고 하면 Neptune 최적화가 수행되지 않습니다. 쿼리는 성공할 수 있지만, 특히 대규모 데이터 세트의 경우 최적화된 버전만큼 성능이 좋지는 않습니다.

최적화되지 않은 업서트 쿼리는 실행 시간을 늘리고 처리량을 줄이므로, Gremlin `explain` 엔드포인트를 사용하여 업서트 쿼리가 완전히 최적화되었는지 확인하는 것이 좋습니다. `explain` 계획을 검토할 때는 `+ not converted into Neptune steps` 및 `WARNING: >>`으로 시작하는 라인을 찾아보세요. 예제:

```
+ not converted into Neptune steps: [FoldStep, CoalesceStep([[UnfoldStep], [AddEdgeSte...
WARNING: >> FoldStep << is not supported natively yet
```

이러한 경고를 통해 쿼리를 완전히 최적화하는 데 방해가 되는 부분을 식별할 수 있습니다.

쿼리를 완전히 최적화할 수 없는 경우도 있습니다. 이러한 상황에서는 최적화할 수 없는 단계를 쿼리 끝에 넣어 엔진이 최대한 많은 단계를 최적화할 수 있도록 해야 합니다. 이 기법은 최적화가 불가능할 수 있는 추가 수정을 동일한 버텍스나 엣지에 적용하기 전에 버텍스 또는 엣지 세트에 대해 최적화된 업서트를 모두 수행하는 일부 배치 업서트 예제에서 사용됩니다.

## 일괄 처리 업서트로 처리량 향상
일괄 처리 업서트

쓰기 처리량이 높은 시나리오의 경우 업서트 단계를 연결하여 버텍스와 엣지를 일괄적으로 업서트할 수 있습니다. 일괄 처리를 사용하면 많은 수의 버텍스와 엣지를 업서트할 때 발생하는 트랜잭션 오버헤드가 줄어듭니다. 그런 다음 여러 클라이언트를 통해 배치 요청을 병렬로 업서트하여 처리량을 더욱 개선할 수 있습니다.

일반적으로 배치 요청당 약 200개의 레코드를 업서트하는 것이 좋습니다. 레코드는 단일 버텍스, 엣지 레이블 또는 속성입니다. 예를 들어, 레이블이 하나이고 속성이 4개인 버텍스는 5개의 레코드를 생성합니다. 레이블과 단일 속성이 있는 엣지는 레코드 2개를 생성합니다. 각각 레이블이 하나이고 속성이 4개인 버텍스 배치를 업서트하려면 `200 / (1 + 4) = 40`이므로 배치 크기를 40으로 시작해야 합니다.

배치 크기를 시험해 볼 수 있습니다. 처음에는 배치당 200개의 레코드가 좋지만, 워크로드에 따라 배치 크기가 더 크거나 작을 수 있습니다. 하지만 Neptune에서 요청당 전체 Gremlin 단계 수를 제한할 수 있다는 점에 유의하세요. 이 제한은 문서화되어 있지 않지만, 안전을 위해 요청에 포함된 Gremlin 단계가 1,500개를 넘지 않도록 하는 것이 좋습니다. Neptune은 1,500단계가 넘는 대규모 배치 요청을 거부할 수 있습니다.

처리량을 늘리려면 여러 클라이언트를 사용하여 배치를 병렬로 업서트할 수 있습니다([효율적인 멀티스레드 Gremlin 쓰기 생성](best-practices-gremlin-multithreaded-writes.md) 참조). 클라이언트 수는 Neptune 라이터 인스턴스의 작업자 스레드 수와 같아야 합니다. 이 수는 일반적으로 서버 vCPU 수의 2배입니다. 예를 들어, `r5.8xlarge` 인스턴스에는 32개의 vCPU와 64개의 작업자 스레드가 있습니다. `r5.8xlarge`를 사용하는 처리량이 높은 쓰기 시나리오의 경우 64개의 클라이언트가 Neptune에 배치 업서트를 병렬로 작성하는 것이 좋습니다.

각 클라이언트는 배치 요청을 제출하고 요청이 완료될 때까지 기다렸다가 다른 요청을 제출해야 합니다. 여러 클라이언트가 병렬로 실행되더라도 각 개별 클라이언트는 순차적으로 요청을 제출합니다. 이렇게 하면 서버 측 요청 대기열에 과부하가 걸리지 않고 모든 작업자 스레드를 점유하는 요청 스트림이 서버에 꾸준히 공급됩니다([Neptune DB 클러스터의 DB 인스턴스 크기 조정](feature-overview-db-clusters.md#feature-overview-sizing-instances) 참조).

## Traverser가 여러 개 생성되는 단계 피하기
여러 개의 Traverser 피하기

Gremlin 단계가 실행되면 들어오는 Traverser를 받아 하나 이상의 출력 Traverser를 내보냅니다. 한 단계에서 내보내는 Traverser 수에 따라 다음 단계가 실행되는 횟수가 결정됩니다.

일반적으로 배치 작업을 수행할 때는 버텍스 A 업서트와 같은 각 작업을 한 번 실행하여 버텍스 A 업서트, 버텍스 B 업서트, 버텍스 C 업서트 식의 작업 순서를 이루도록 하려고 합니다. 단계가 한 요소만 생성하거나 수정하는 경우 하나의 Traverser만 내보내고 다음 작업을 나타내는 단계는 한 번만 실행됩니다. 반면, 작업에서 2개 이상의 요소를 만들거나 수정하면 여러 Traverser가 생성되어 후속 단계가 생성된 Traverser당 한 번씩 여러 번 실행됩니다. 이로 인해 데이터베이스에서 불필요한 추가 작업이 수행될 수 있으며, 경우에 따라 원하지 않는 추가 버텍스, 엣지 또는 속성값이 생성될 수 있습니다.

문제가 발생할 수 있는 예로는 `g.V().addV()`와 같은 쿼리를 들 수 있습니다. 이 간단한 쿼리는 그래프에 있는 모든 버텍스에 버텍스를 추가합니다. `V()`는 그래프의 각 버텍스에 대해 Traverser를 내보내고 각 Traverser가 `addV()`에 대한 호출을 트리거하기 때문입니다.

여러 Traverser를 내보낼 수 있는 연산을 처리하는 방법은 [업서트와 삽입 혼합](#gremlin-upserts-pre-3.6-and-inserts) 섹션을 참조하세요.

## 버텍스 업서트
버텍스 업서트

버텍스 ID를 사용하여 해당하는 버텍스가 존재하는지 확인할 수 있습니다. Neptune은 ID와 관련된 동시 사용 사례가 많은 경우 업서트를 최적화하므로, 이 접근 방식은 선호됩니다. 예를 들어, 다음 쿼리는 지정된 버텍스 ID가 없는 경우 버텍스를 생성하고 존재하는 경우 이를 재사용합니다.

```
g.V('v-1')
 .fold()
  .coalesce(unfold(),
            addV('Person').property(id, 'v-1')
                          .property('email', 'person-1@example.org'))
  .id()
```

참고로 이 쿼리는 `id()` 단계로 끝납니다. 버텍스 업서트를 위해 반드시 필요하지는 않지만, 업서트 쿼리의 끝에 `id()` 단계를 추가하면 서버가 모든 버텍스 속성을 클라이언트로 다시 직렬화하지 않도록 보장하므로 쿼리의 잠금 비용을 줄이는 데 도움이 됩니다.

또는 버텍스 속성을 사용하여 버텍스가 존재하는지 확인할 수 있습니다.

```
g.V()
 .hasLabel('Person')
 .has('email', 'person-1@example.org')
 .fold()
 .coalesce(unfold(),
           addV('Person').property('email', 'person-1@example.org'))
 .id()
```

가능하면 사용자가 제공한 자체 ID를 사용하여 버텍스를 만들고 이 ID를 사용하여 업서트 작업 중에 버텍스가 존재하는지 확인하세요. 이를 통해 Neptune은 ID와 관련된 업서트를 최적화할 수 있습니다. 동시 수정이 빈번하게 발생하는 경우 ID 기반 업서트가 속성 기반 업서트보다 훨씬 더 효율적일 수 있습니다.

### 버텍스 업서트 연결
버텍스 업서트 연결

버텍스 업서트를 체인으로 연결하여 일괄적으로 삽입할 수 있습니다.

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .id()
```

## 엣지 업서트
엣지 업서트

사용자 지정 버텍스 ID를 사용하여 버텍스를 업서트하는 것과 같은 방식으로 엣지 ID를 사용하여 엣지를 업서트할 수 있습니다. 다시 말하지만, 이 방법을 사용하면 Neptune에서 쿼리를 최적화할 수 있어 선호됩니다. 예를 들어, 다음 쿼리는 엣지가 아직 없으면 엣지 ID를 기반으로 엣지를 생성하고 존재하면 재사용합니다. 또한 쿼리는 새 엣지를 만들어야 하는 경우 `from` 및 `to` 버텍스의 ID를 사용합니다.

```
g.E('e-1')
 .fold()
 .coalesce(unfold(),
           addE('KNOWS').from(V('v-1'))
                        .to(V('v-2'))
                        .property(id, 'e-1'))
 .id()
```

많은 애플리케이션이 사용자 지정 버텍스 ID를 사용하지만, Neptune은 자체적으로 엣지 ID를 생성합니다. 엣지의 ID는 몰라도 `from` 및 `to` 버텍스 ID는 알고 있다면 다음과 같은 공식을 사용하여 엣지를 업서트할 수 있습니다.

```
g.V('v-1')
 .outE('KNOWS')
 .where(inV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           addE('KNOWS').from(V('v-1'))
                        .to(V('v-2')))
 .id()
```

단, `where()` 절의 버텍스 단계는 `otherV()`가 아닌 `inV()`(또는 엣지를 찾는 데 `inE()`를 사용한 적이 있다면 `outV()`)여야 합니다. 여기서는 `otherV()`를 사용하지 마세요. 사용하면 쿼리가 최적화되지 않아 성능이 저하될 수 있습니다. 예를 들어, Neptune은 다음 쿼리를 최적화하지 않습니다.

```
// Unoptimized upsert, because of otherV()
g.V('v-1')
 .outE('KNOWS')
 .where(otherV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           addE('KNOWS').from(V('v-1'))
                        .to(V('v-2')))
 .id()
```

엣지 또는 버텍스 ID를 사전에 알 수 없는 경우 버텍스 속성을 사용하여 업서트할 수 있습니다.

```
g.V()
 .hasLabel('Person')
 .has('name', 'person-1')
 .outE('LIVES_IN')
 .where(inV().hasLabel('City').has('name', 'city-1'))
 .fold()
 .coalesce(unfold(),
           addE('LIVES_IN').from(V().hasLabel('Person')
                                    .has('name', 'person-1'))
                           .to(V().hasLabel('City')
                                  .has('name', 'city-1')))
 .id()
```

버텍스 업서트와 마찬가지로, Neptune이 업서트를 완전히 최적화할 수 있도록 속성 기반 업서트보다는 엣지 ID 또는 `from` 및 `to` 버텍스 ID를 사용하는 ID 기반 엣지 업서트를 사용하는 것이 좋습니다.

### `from` 및 `to` 버텍스 존재 여부 확인
버텍스 존재 여부 확인

`addE().from().to()`와 같이 새 엣지를 만드는 단계의 구성을 참고하세요. 이 구성을 통해 쿼리는 `from` 및 `to` 버텍스의 존재 여부를 모두 확인할 수 있습니다. 둘 중 하나가 존재하지 않는 경우 쿼리는 다음과 같은 오류를 반환합니다.

```
{
  "detailedMessage": "Encountered a traverser that does not map to a value for child...
  "code": "IllegalArgumentException",
  "requestId": "..."
}
```

`from` 또는 `to` 버텍스가 존재하지 않을 가능성이 있는 경우 버텍스 사이의 엣지를 업서트하기 전에 버텍스를 업서트해야 합니다. [버텍스 업서트와 엣지 업서트의 결합](#gremlin-upserts-pre-3.6-vertexes-and-edges)을(를) 참조하세요.

`V().addE().to()`와 같이 사용하지 말아야 할 엣지를 만들 수 있는 다른 방법이 있습니다. `from` 버텍스가 있는 경우에만 엣지를 추가합니다. `to` 버텍스가 존재하지 않는 경우 앞에서 설명한 것처럼 쿼리에서 오류가 발생하지만, `from` 버텍스가 없으면 오류가 발생하지 않고 엣지 삽입이 자동으로 실패합니다. 예를 들어, 다음 업서트는 `from` 버텍스가 없는 경우 엣지를 업서트하지 않고 완료합니다.

```
// Will not insert edge if from vertex does not exist
g.V('v-1')
 .outE('KNOWS')
 .where(inV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           V('v-1').addE('KNOWS')
                   .to(V('v-2')))
 .id()
```

### 엣지 업서트 연결
엣지 업서트 연결

엣지 업서트를 연결하여 배치 요청을 생성하려면 엣지 ID를 이미 알고 있더라도 각 업서트를 버텍스 조회로 시작해야 합니다.

업서트하려는 엣지의 ID와 `from` 및 `to` 버텍스의 ID를 이미 알고 있는 경우 다음 공식을 사용할 수 있습니다.

```
g.V('v-1')
 .outE('KNOWS')
 .hasId('e-1')
 .fold()
 .coalesce(unfold(),
           V('v-1').addE('KNOWS')
                   .to(V('v-2'))
                   .property(id, 'e-1'))
 .V('v-3')
 .outE('KNOWS')
 .hasId('e-2').fold()
 .coalesce(unfold(),
           V('v-3').addE('KNOWS')
                   .to(V('v-4'))
                   .property(id, 'e-2'))
 .V('v-5')
 .outE('KNOWS')
 .hasId('e-3')
 .fold()
 .coalesce(unfold(),
           V('v-5').addE('KNOWS')
                   .to(V('v-6'))
                   .property(id, 'e-3'))
 .id()
```

가장 일반적인 배치 엣지 업서트 시나리오는 `from` 및 `to` 버텍스 ID는 아는데 업서트하려는 엣지의 ID는 모르는 경우일 것입니다. 이 경우 다음 공식을 사용하세요.

```
g.V('v-1')
 .outE('KNOWS')
 .where(inV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           V('v-1').addE('KNOWS')
                   .to(V('v-2')))

 .V('v-3')
 .outE('KNOWS')
 .where(inV().hasId('v-4'))
 .fold()
 .coalesce(unfold(),
           V('v-3').addE('KNOWS')
                   .to(V('v-4')))
 .V('v-5')
 .outE('KNOWS')
 .where(inV().hasId('v-6'))
 .fold()
 .coalesce(unfold(),
           V('v-5').addE('KNOWS').to(V('v-6')))
 .id()
```

일반적이지 않지만, 업서트하려는 엣지의 ID는 아는데 `from` 및 `to` 버텍스의 ID는 모르는 경우 다음 공식을 사용하면 됩니다.

```
g.V()
 .hasLabel('Person')
 .has('email', 'person-1@example.org')
 .outE('KNOWS')
 .hasId('e-1')
 .fold()
 .coalesce(unfold(),
           V().hasLabel('Person')
              .has('email', 'person-1@example.org')
              .addE('KNOWS')
              .to(V().hasLabel('Person')
                     .has('email', 'person-2@example.org'))
                     .property(id, 'e-1'))
 .V()
 .hasLabel('Person')
 .has('email', 'person-3@example.org')
 .outE('KNOWS')
 .hasId('e-2')
 .fold()
 .coalesce(unfold(),
           V().hasLabel('Person')
              .has('email', 'person-3@example.org')
              .addE('KNOWS')
              .to(V().hasLabel('Person')
                     .has('email', 'person-4@example.org'))
              .property(id, 'e-2'))
 .V()
 .hasLabel('Person')
 .has('email', 'person-5@example.org')
 .outE('KNOWS')
 .hasId('e-1')
 .fold()
 .coalesce(unfold(),
           V().hasLabel('Person')
              .has('email', 'person-5@example.org')
              .addE('KNOWS')
              .to(V().hasLabel('Person')
                     .has('email', 'person-6@example.org'))
                     .property(id, 'e-3'))
 .id()
```

## 버텍스 업서트와 엣지 업서트의 결합
업서트 결합

버텍스와 버텍스를 연결하는 엣지를 모두 업서트해야 하는 경우가 있을 수 있습니다. 여기에 제시된 배치 예제를 혼합하여 사용할 수 있습니다. 다음 예제에서는 버텍스 3개와 엣지 2개를 업서트합니다.

```
g.V('p-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-1')
                         .property('email', 'person-1@example.org'))
 .V('p-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-2')
                         .property('name', 'person-2@example.org'))
 .V('c-1')
 .fold()
 .coalesce(unfold(),
           addV('City').property(id, 'c-1')
                       .property('name', 'city-1'))
 .V('p-1')
 .outE('LIVES_IN')
 .where(inV().hasId('c-1'))
 .fold()
 .coalesce(unfold(),
           V('p-1').addE('LIVES_IN')
                   .to(V('c-1')))
 .V('p-2')
 .outE('LIVES_IN')
 .where(inV().hasId('c-1'))
 .fold()
 .coalesce(unfold(),
           V('p-2').addE('LIVES_IN')
                   .to(V('c-1')))
 .id()
```

## 업서트와 삽입 혼합
업서트와 삽입 혼합

버텍스와 버텍스를 연결하는 엣지를 모두 업서트해야 하는 경우가 있을 수 있습니다. 여기에 제시된 배치 예제를 혼합하여 사용할 수 있습니다. 다음 예제에서는 버텍스 3개와 엣지 2개를 업서트합니다.

업서트는 일반적으로 한 번에 한 요소씩 진행됩니다. 여기에 제시된 업서트 패턴을 고수하면 각 업서트 작업에서 Traverser가 하나만 생성되어 후속 작업이 한 번만 실행됩니다.

하지만 업서트와 삽입을 섞어서 사용해야 하는 경우도 있습니다. 예를 들어, 엣지를 사용하여 동작이나 이벤트의 인스턴스를 나타내는 경우가 이에 해당합니다. 요청에서는 업서트를 사용하여 필요한 버텍스가 모두 존재하는지 확인한 다음, 삽입을 통해 엣지를 추가할 수 있습니다. 이런 요청 유형의 경우 각 작업에서 발생할 수 있는 잠재적 Traverser 수에 유의하세요.

업서트와 삽입을 혼합하여 이벤트를 나타내는 엣지를 그래프에 추가하는 다음 예제를 생각해 보세요.

```
// Fully optimized, but inserts too many edges
g.V('p-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-1')
                         .property('email', 'person-1@example.org'))
 .V('p-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-2')
                         .property('name', 'person-2@example.org'))
 .V('p-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-3')
                         .property('name', 'person-3@example.org'))
 .V('c-1')
 .fold()
 .coalesce(unfold(),
           addV('City').property(id, 'c-1')
                       .property('name', 'city-1'))
 .V('p-1', 'p-2')
 .addE('FOLLOWED')
 .to(V('p-1'))
 .V('p-1', 'p-2', 'p-3')
 .addE('VISITED')
 .to(V('c-1'))
 .id()
```

쿼리는 FOLLOWED 엣지 2개와 VISITED 엣지 3개의 총 5개의 엣지를 삽입해야 합니다. 하지만 쿼리를 작성하면 8개(FOLLOWED 2개, VISITED 6개)가 삽입됩니다. 그 이유는 FOLLOWED 엣지 2개를 삽입하는 작업에서 2개의 Traverser가 발생하여 3개의 엣지를 삽입하는 후속 삽입 작업이 2번씩 실행되기 때문입니다.

잠재적으로 2개 이상의 Traverser를 내보낼 수 있는 각 작업 뒤에 `fold()` 단계를 추가하면 문제를 해결할 수 있습니다.

```
g.V('p-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-1')
                         .property('email', 'person-1@example.org'))
 .V('p-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-2').
                         .property('name', 'person-2@example.org'))
 .V('p-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-3').
                         .property('name', 'person-3@example.org'))
 .V('c-1')
 .fold().
 .coalesce(unfold(),
            addV('City').property(id, 'c-1').
                        .property('name', 'city-1'))
 .V('p-1', 'p-2')
 .addE('FOLLOWED')
 .to(V('p-1'))
 .fold()
 .V('p-1', 'p-2', 'p-3')
 .addE('VISITED')
 .to(V('c-1')).
 .id()
```

여기서는 FOLLOWED 엣지를 삽입하는 작업 뒤에 `fold()` 단계를 삽입했습니다. 그 결과 단일 Traverser가 생성되어 후속 작업이 한 번만 실행됩니다.

이 접근 방식의 단점은 `fold()`가 최적화되지 않아 쿼리가 완전히 최적화되지 않았다는 것입니다. `fold()` 이후 발생하는 삽입 작업은 이제 최적화되지 않습니다.

후속 단계를 대신하여 Traverser 수를 줄이는 데 `fold()`를 사용해야 하는 경우 가장 비용이 적게 드는 작업이 쿼리에서 최적화되지 않은 부분을 차지하도록 작업을 정렬해 보세요.

## 기존 버텍스와 엣지를 수정하는 업서트
수정하는 업서트

버텍스나 엣지가 없는 경우 새것인지 기존에 있던 것인지 관계없이 버텍스나 엣지를 새로 만들어 속성을 추가하거나 업데이트하고 싶을 때가 있습니다.

속성을 추가 또는 수정하려면 `property()` 단계를 사용하세요. 이 단계는 `coalesce()` 단계 외부에서 사용하세요. `coalesce()` 단계 내 기존 버텍스 또는 엣지의 속성을 수정하려고 하면 Neptune 쿼리 엔진에서 쿼리를 최적화하지 못할 수 있습니다.

다음 쿼리는 업서트된 각 버텍스의 카운터 속성을 추가하거나 업데이트합니다. 각 `property()` 단계에는 단일 카디널리티가 적용되므로, 새 값이 기존 값 세트에 추가되지 않고 기존 값을 모두 대체합니다.

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .property(single, 'counter', 1)
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .property(single, 'counter', 2)
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .property(single, 'counter', 3)
 .id()
```

업서트된 모든 요소에 적용되는 속성값(예: `lastUpdated` 타임스탬프 값)이 있는 경우 쿼리가 끝날 때 이를 추가하거나 업데이트할 수 있습니다.

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .V('v-2').
 .fold().
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .V('v-1', 'v-2', 'v-3')
 .property(single, 'lastUpdated', datetime('2020-02-08'))
 .id()
```

버텍스나 엣지를 추가로 수정해야 하는지 여부를 결정하는 추가 조건이 있는 경우 `has()` 단계를 사용하여 수정이 적용될 요소를 필터링할 수 있습니다. 다음 예제에서는 `has()` 단계를 사용하여 `version` 속성값을 기준으로 업서트된 버텍스를 필터링합니다. 그런 다음 쿼리는 `version`이 3보다 작은 모든 버텍스의 `version`을 3으로 업데이트합니다.

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org')
                         .property('version', 3))
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org')
                         .property('version', 3))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org')
                         .property('version', 3))
 .V('v-1', 'v-2', 'v-3')
 .has('version', lt(3))
 .property(single, 'version', 3)
 .id()
```

# Gremlin `explain`을 사용하여 Neptune 쿼리 실행 분석
Gremlin `explain`

Amazon Neptune에는 *Explain*이라는 Gremlin 기능이 추가되었습니다. 이 기능은 Neptune 엔진에 의해 수행되는 실행 접근 방식을 이해하기 위한 셀프 서비스 도구입니다. Gremlin 쿼리를 제출하는 HTTP 호출에 `explain` 파라미터를 추가하여 이 도구를 호출합니다.

`explain` 기능은 쿼리 실행 계획의 논리 구조에 대한 정보를 제공합니다. [Gremlin 쿼리 조정](gremlin-traversal-tuning.md)에 설명된 대로 이 정보를 사용하여 잠재적 평가 및 실행 병목 현상을 식별하고 쿼리를 조정할 수 있습니다. 그런 다음 [쿼리 힌트](gremlin-query-hints.md)를 사용하여 쿼리 실행 계획을 개선할 수 있습니다.

**Topics**
+ [

# Neptune에서 Gremlin 쿼리가 작동하는 방법 이해
](gremlin-explain-background.md)
+ [

# Neptune에서 Gremlin `explain` API 사용
](gremlin-explain-api.md)
+ [

# Neptune의 Gremlin `profile` API
](gremlin-profile-api.md)
+ [

# `explain` 및 `profile`을 사용하여 Gremlin 쿼리 조정
](gremlin-traversal-tuning.md)
+ [

# Amazon Neptune의 네이티브 Gremlin 단계 지원
](gremlin-step-support.md)

# Neptune에서 Gremlin 쿼리가 작동하는 방법 이해
배경 정보

Amazon Neptune의 Gremlin `explain` 및 `profile` 보고서를 최대한 활용하려면 Gremlin 쿼리에 대한 몇 가지 배경 정보를 이해하는 것이 도움이 됩니다.

**Topics**
+ [

# Neptune의 Gremlin 문
](gremlin-explain-background-statements.md)
+ [

# Neptune이 문 인덱스를 사용하여 Gremlin 쿼리를 처리하는 방법
](gremlin-explain-background-indexing-examples.md)
+ [

# Gremlin 쿼리가 Neptune에서 처리되는 방법
](gremlin-explain-background-querying.md)

# Neptune의 Gremlin 문
명령문

Amazon Neptune의 속성 그래프 데이터는 4위치(쿼드) 문으로 이루어져 있습니다. 이러한 문 각각은 속성 그래프 데이터의 개별 원자 단위를 나타냅니다. 자세한 내용은 [Neptune 그래프 데이터 모델](feature-overview-data-model.md) 단원을 참조하십시오. RDF(Resource Description Framework) 데이터 모델과 마찬가지로 4개 위치는 다음과 같습니다.
+ `subject (S)`
+ `predicate (P)`
+ `object (O)`
+ `graph (G)`

각 문은 하나 이상의 리소스에 대한 어설션입니다. 예를 들어 문은 두 리소스 간에 관계가 존재한다는 것을 어설션하거나 일부 리소스에 속성(키-값 페어)을 연결할 수 있습니다.

조건자는 관계 또는 속성의 유형을 설명하는 문의 동사로 생각하면 됩니다. 객체는 관계 또는 속성 값의 대상입니다. 그래프 위치는 선택 사항이며 많은 다양한 방식으로 사용할 수 있습니다. Neptune의 속성 그래프(PG) 데이터에서는 아예 사용되지 않기도 하고(null 그래프), 엣지의 식별자를 표현하는 데 사용되기도 합니다. 공유된 리소스 식별자가 있는 문 집합이 그래프를 생성합니다.

Neptune 속성 그래프 데이터 모델에는 3가지 클래스의 문이 있습니다.

**Topics**
+ [버텍스 레이블 문](#gremlin-explain-background-vertex-labels)
+ [엣지 문](#gremlin-explain-background-edge-statements)
+ [속성 문](#gremlin-explain-background-property-statements)

## Gremlin 버텍스 레이블 문
버텍스 레이블 문

Neptune의 버텍스 레이블 문의 용도는 2가지입니다.
+ 먼저, 버텍스의 레이블을 추적합니다.
+ 이러한 문 중 최소한 하나가 존재한다는 것은 그래프에서 특정 버텍스가 존재한다는 것을 암시합니다.

이들 문의 제목이 버텍스 식별자이고 객체가 레이블이며, 둘 모두 사용자가 지정합니다. 이들 문에 대한 특수 고정 조건자(`<~label>`으로 표시)와 기본 그래프 식별자(null 그래프)(`<~>`로 표시)가 사용됩니다.

예를 들어 다음 `addV` 순회를 생각해 보십시오.

```
g.addV("Person").property(id, "v1")
```

이러한 순회로 인해 다음 문이 그래프에 추가되는 결과가 초래됩니다.

```
StatementEvent[Added(<v1> <~label> <Person> <~>) .]
```

## Gremlin 엣지 문
엣지 문

Gremlin 엣지 문은 Neptune에서 그래프의 두 버텍스 간에 엣지가 존재한다는 것을 암시합니다. 엣지 문의 제목(S)은 소스 `from` 버텍스입니다. 조건자(P)는 사용자가 제공한 엣지 레이블입니다. 객체(O)는 대상 `to` 버텍스입니다. 그래프(G)는 사용자가 제공한 엣지 식별자입니다.

예를 들어 다음 `addE` 순회를 생각해 보십시오.

```
g.addE("knows").from(V("v1")).to(V("v2")).property(id, "e1")
```

순회로 인해 다음 문이 그래프에 추가되는 결과가 초래됩니다.

```
StatementEvent[Added(<v1> <knows> <v2> <e1>) .]
```

## Gremlin 속성 문
속성 문

Neptune의 Gremlin 속성 문은 버텍스 또는 엣지에 대한 개별 속성 값을 어설션합니다. 제목은 사용자가 제공한 버텍스 또는 엣지 식별자입니다. 조건자는 속성 이름(키)이고 객체는 개별 속성 값입니다. 다시 말하지만 그래프(G)는 기본 설정된 그래프 식별자(null 그래프)(`<~>`로 표시)입니다.

다음 버텍스 속성 예제를 생각해 보세요.

```
g.V("v1").property("name", "John")
```

이 문의 결과는 다음과 같습니다.

```
StatementEvent[Added(<v1> <name> "John" <~>) .]
```

속성 문은 객체가 기본값(`string`, `date`, `byte`, `short`, `int`, `long`, `float` 또는 `double`)이라는 점에서 다른 문과 다릅니다. 이들의 객체는 다른 어설션의 제목으로 사용할 수 있는 리소스 식별자가 아닙니다.

다중 속성의 경우, 해당 집합의 각 개별 속성 값은 자체 문을 수신합니다.

```
g.V("v1").property(set, "phone", "956-424-2563").property(set, "phone", "956-354-3692 (tel:9563543692)")
```

이 결과는 다음과 같습니다.

```
StatementEvent[Added(<v1> <phone> "956-424-2563" <~>) .]
StatementEvent[Added(<v1> <phone> "956-354-3692" <~>) .]
```

엣지 속성은 버텍스 속성과 유사하게 처리되지만 (S) 위치에서 엣지 식별자를 사용합니다. 예를 들어 엣지에 속성을 추가하는 경우:

```
g.E("e1").property("weight", 0.8)
```

그러면 다음 문이 그래프에 추가됩니다.

```
StatementEvent[Added(<e1> <weight> 0.8 <~>) .]
```

# Neptune이 문 인덱스를 사용하여 Gremlin 쿼리를 처리하는 방법
문 인덱스

문은 [Neptune에서 문을 인덱싱하는 방식](feature-overview-storage-indexing.md)에 설명된 대로 3개의 문 인덱스를 통해 Amazon Neptune에서 액세스할 수 있습니다. Neptune은 Gremlin 쿼리에서 일부 위치가 알려져 있고 나머지 위치는 인덱스 검색을 통해 검색되도록 남겨두는 문 *패턴*을 추출합니다.

Neptune은 속성 그래프 스키마의 크기가 크지 않다고 가정합니다. 이는 각기 다른 엣지 레이블 및 속성 이름의 수가 매우 적어서 서로 다른 조건자의 총 수가 적어진다는 의미입니다. Neptune은 별도의 인덱스에서 서로 다른 조건자를 추적합니다. OSGP 인덱스를 사용하기 보다는 이러한 조건자의 캐시를 사용해 `{ all P x POGS }`에 대해 유니온 스캔을 수행합니다. 역 순회 OSGP 인덱스가 필요하지 않기 때문에 스토리지 공간과 로드 처리량을 모두 줄일 수 있습니다.

Neptune Gremlin Explain/Profile API를 사용하면 그래프의 조건자 수를 알아낼 수 있습니다. 사용자는 애플리케이션에서 속성 그래프 스키마의 크기가 작다는 Neptune 가정이 무효화되는지 여부를 확인할 수 있습니다.

다음 예제는 Neptune이 어떻게 인덱스를 사용하여 Gremlin 쿼리를 처리하는지 보여줍니다.

**질문: 버텍스 `v1`의 레이블은 무엇입니까?**

```
  Gremlin code:      g.V('v1').label()
  Pattern:           (<v1>, <~label>, ?, ?)
  Known positions:   SP
  Lookup positions:  OG
  Index:             SPOG
  Key range:         <v1>:<~label>:*
```

**질문: 버텍스 `v1`의 ‘알려진’ 발신 엣지는 무엇입니까?**

```
  Gremlin code:      g.V('v1').out('knows')
  Pattern:           (<v1>, <knows>, ?, ?)
  Known positions:   SP
  Lookup positions:  OG
  Index:             SPOG
  Key range:         <v1>:<knows>:*
```

**질문: `Person` 버텍스 레이블을 가지고 있는 버텍스는 무엇입니까?**

```
  Gremlin code:      g.V().hasLabel('Person')
  Pattern:           (?, <~label>, <Person>, <~>)
  Known positions:   POG
  Lookup positions:  S
  Index:             POGS
  Key range:         <~label>:<Person>:<~>:*
```

**질문: 주어진 엣지 `e1`의 소스/대상 버텍스는 무엇입니까?**

```
  Gremlin code:      g.E('e1').bothV()
  Pattern:           (?, ?, ?, <e1>)
  Known positions:   G
  Lookup positions:  SPO
  Index:             GPSO
  Key range:         <e1>:*
```

Neptune에 포함되지 **않은** 문 인덱스는 역 순회 OSGP 인덱스입니다. 아래 예제에서와 같이 이 인덱스는 모든 엣지 레이블에서 수신 엣지를 수집하는 데 사용할 수 있습니다.

**질문: 수신 인접 버텍스 `v1`은 무엇인가요?**

```
  Gremlin code:      g.V('v1').in()
  Pattern:           (?, ?, <v1>, ?)
  Known positions:   O
  Lookup positions:  SPG
  Index:             OSGP  // <-- Index does not exist
```

# Gremlin 쿼리가 Neptune에서 처리되는 방법
쿼리 처리

Amazon Neptune에서는 보다 복잡한 순회를 일련의 패턴으로 표현할 수 있기 때문에 조인 생성을 위해 패턴에서 공유할 수 있는 명명된 변수에 대한 정의에 따라 관계를 생성할 수 있습니다. 방법은 다음 예제와 같습니다.

**질문: 버텍스 `v1`의 2홉 이웃은 무엇입니까?**

```
  Gremlin code:      g.V(‘v1’).out('knows').out('knows').path()
  Pattern:           (?1=<v1>, <knows>, ?2, ?) X Pattern(?2, <knows>, ?3, ?)

  The pattern produces a three-column relation (?1, ?2, ?3) like this:
                     ?1     ?2     ?3
                     ================
                     v1     v2     v3
                     v1     v2     v4
                     v1     v5     v6
```

두 패턴에서 `?2` 변수를 공유하여(첫 번째 패턴의 O 위치와 두 번째 패턴의 S 위치에서) 첫 번째 홉 이웃에서 두 번째 홉 이웃으로의 조인을 생성합니다. 각 Neptune 솔루션에는 3개의 명명된 변수에 대한 바인딩이 포함되어 있기 때문에 이를 사용하여 [TinkerPop Traverser](http://tinkerpop.apache.org/docs/current/reference/#_the_traverser)(경로 정보 포함)를 다시 생성할 수 있습니다.

```
```

Gremlin 쿼리 처리의 첫 번째 단계는 쿼리를 일련의 TinkerPop [단계](http://tinkerpop.apache.org/docs/current/reference/#graph-traversal-steps)로 구성된 TinkerPop [Traversal](http://tinkerpop.apache.org/docs/current/reference/#traversal) 객체로 구문 분석하는 것입니다. 오픈 소스 [Apache TinkerPop 프로젝트](http://tinkerpop.apache.org/)의 일부인 이들 단계는 참조 구현 시 Gremlin 순회를 구성하는 논리적 연산자이자 물리적 연산자입니다. 둘 모두 쿼리 모델을 표현하는 데 사용됩니다. 이들은 표현하는 연산자의 시맨틱에 따라 솔루션을 만들 수 있는 실행 가능한 연산자입니다. 예를 들어, `.V()`는 TinkerPop [GraphStep](http://tinkerpop.apache.org/docs/current/reference/#graph-step)에서 표현 및 실행이 모두 이루어집니다.

이러한 기성 TinkerPop 단계는 실행이 가능하기 때문에 TinkerPop Traversal이 Gremlin 쿼리를 실행하고 올바른 답을 만들 수 있습니다. 그러나 크기가 큰 그래프에서 실행될 때는 TinkerPop 단계가 매우 비효율적이고 속도가 느려질 수 있습니다. Neptune은 이러한 단계를 사용하는 대신에 앞서 설명한 대로 순회를 패턴 그룹으로 이루어진 선언 형태로 변환하고자 시도합니다.

현재 Neptune은 네이티브 쿼리 엔진에서 모든 Gremlin 연산자(단계)를 지원하지 않습니다. 따라서 가능한 많은 단계들을 단일 `NeptuneGraphQueryStep`로 축소하고자 시도합니다. 여기에는 변환된 모든 단계에 대한 선언형 논리적 쿼리 계획이 포함되어 있습니다. 모든 단계가 변환되는 것이 가장 이상적입니다. 하지만 단계가 변환되지 않는 문제에 봉착하면 Neptune은 네이티브 실행을 중단하고 해당 시점부터 TinkerPop 단계가 될 때까지 모든 쿼리 실행을 연기합니다. 네이티브 실행을 연계하려고 시도하지 않습니다.

단계가 논리적 쿼리 계획으로 변환되고 나면 Neptune은 일련의 쿼리 옵티마이저를 실행하여 정적 분석과 예상되는 카디널리티에 따라 쿼리 계획을 재작성합니다. 이러한 옵티마이저는 범위 수에 따라 재정렬 연산자와 비슷한 기능을 수행하고, 불필요하거나 중복된 연산자를 제거하고, 필터를 재배열하고, 연산자를 서로 다른 그룹에 배치합니다.

최적화된 쿼리 계획이 만들어지면 Neptune은 쿼리 실행 작업을 수행하는 물리적 연산자의 파이프라인을 생성합니다. 여기에는 문 인덱스에서의 데이터 읽기, 다양한 형식의 조인 수행, 필터링, 정령 등이 포함됩니다. 파이프라인에서 솔루션 스트림이 만들어지고, 이는 다시 TinkerPop Traverser 객체의 스트림으로 변환됩니다.

## 쿼리 결과의 직렬화
직렬화

Amazon Neptune은 현재 TinkerPop 응답 메시지 직렬 변환기를 활용하여 쿼리 결과(TinkerPop Traversers)를 직렬화된 데이터로 변환하고, 이러한 데이터는 네트워크를 통해 클라이언트로 다시 전송됩니다. 이러한 직렬화 형식은 지나치게 상세한 경향이 있습니다.

예를 들어, `g.V().limit(1)` 같은 버텍스 쿼리의 결과를 직렬화하려면 Neptune 쿼리 엔진이 단일 검색을 수행하여 쿼리 결과를 생성해야 합니다. 그러나 `GraphSON` 직렬 변환기는 대량의 추가 검색을 수행하여 버텍스를 직렬화 형식으로 패키지화합니다. 따라서 레이블을 얻기 위한 검색과 속성 키를 얻기 위한 검색, 그리고 버텍스가 각 키의 모든 값을 얻을 수 있도록 속성 키당 검색을 추가로 수행해야 합니다.

일부 직렬화 형식은 더 효율적이지만 모두 추가 검색이 필요합니다. 뿐만 아니라 TinkerPop 직렬 변환기는 중복 검색을 굳이 피하려 하지 않기 때문에 다수의 검색이 불필요하게 반복되는 결과가 초래됩니다.

따라서 필요한 정보에 대해서만 구체적으로 물어보는 쿼리를 작성하는 것이 매우 중요합니다. 예를 들어 `g.V().limit(1).id()`은 버텍스 ID만 반환하고 추가적인 모든 직렬 변환기 검색을 제거합니다. [Neptune의 Gremlin `profile` API](gremlin-profile-api.md)를 사용하면 쿼리 실행 및 직렬화 동안 이루어지는 검색 호출의 수를 확인할 수 있습니다.

# Neptune에서 Gremlin `explain` API 사용
Gremlin `explain` API

Amazon Neptune Gremlin `explain` API는 지정된 쿼리가 실행된 경우에 실행할 쿼리 계획을 반환합니다. API가 실제로 쿼리를 실행하는 것은 아니기 때문에 계획은 거의 동시에 반환됩니다.

이는 Neptune 엔진 고유의 정보를 보고할 수 있다는 점에서 TinkerPop .explain() 단계와 다릅니다.

## Gremlin `explain` 보고서에 포함된 정보
`explain`의 정보

`explain` 보고서에는 다음 정보가 포함됩니다.
+ 요청에 따른 쿼리 문자열.
+ **원래 순회.** 이는 TinkerPop 단계로 쿼리 문자열을 구문 분석하여 생성한 TinkerPop 순회 객체입니다. 이는 TinkerPop TinkerGraph를 토대로 쿼리에서 `.explain()`을 실행하여 생성한 원래 쿼리와 동등합니다.
+ **변환된 순회.** TinkerPop 순회를 Neptune 논리적 쿼리 계획 표현으로 변환하여 생성한 Neptune 순회입니다. 많은 경우에 전체 TinkerPop 순회는 2개의 Neptune 단계로 변환되는데, 하나는 전체 쿼리(`NeptuneGraphQueryStep`)를 실행하는 단계이고 다른 하나는 Neptune 쿼리 엔진 출력을 TinkerPop Traversers(`NeptuneTraverserConverterStep`)로 다시 변환하는 단계입니다.
+ **최적화된 순회.** 일련의 정적 작업 감소 옵티마이저를 통해 실행된 이후에 최적화된 버전의 Neptune 쿼리 계획입니다. 이 옵티마이저는 정적 분석 및 예상 카디널리티를 토대로 쿼리를 재작성합니다. 이러한 옵티마이저는 범위 수에 따라 재정렬 연산자와 비슷한 기능을 수행하고, 불필요하거나 중복된 연산자를 제거하고, 필터를 재배열하고, 연산자를 서로 다른 그룹에 배치합니다.
+ **조건자 수.** 앞서 설명한 Neptune 인덱싱 전략으로 인해 서로 다른 조건자를 다수 가지고 있으면 성능 문제가 발생할 수 있습니다. 엣지 레이블(`.in` 또는 `.both`)과 함께 역 순회 연산자를 사용하는 쿼리의 경우에는 특히 그렇습니다. 이러한 연산자를 사용하고 조건자 수가 충분히 많은 경우에는 `explain` 보고서에 경고 메시지가 표시됩니다.
+ **DFE 정보.** DFE 대체 엔진이 활성화되면 최적화된 순회에 다음과 같은 순회 구성 요소가 표시될 수 있습니다.
  + **`DFEStep`**   –   하위 `DFENode`가 포함된 순회 시 Neptune에 최적화된 DFE 단계입니다. `DFEStep`은 DFE 엔진에서 실행되는 쿼리 계획의 일부를 나타냅니다.
  + **`DFENode`**   –   하나 이상의 하위 `DFEJoinGroupNodes`로 표현된 중간 표현을 포함합니다.
  + **`DFEJoinGroupNode`**   –   하나 이상의 `DFENode` 또는 `DFEJoinGroupNode` 요소의 조인을 나타냅니다.
  + **`NeptuneInterleavingStep`**   –   하위 `DFEStep`이 포함된 순회 시 Neptune에 최적화된 DFE 단계입니다.

    프론티어 요소, 사용된 경로 요소 등과 같은 순회 관련 정보가 들어 있는 `stepInfo` 요소도 포함되어 있습니다. 이 정보는 하위 `DFEStep`을 처리하는 데 사용됩니다.

  DFE에서 쿼리를 평가하고 있는지 쉽게 확인할 수 있는 방법은 `explain` 출력에 `DFEStep`이 포함되어 있는지 확인하는 것입니다. `DFEStep`의 일부가 아닌 순회에 속하지 않은 부분은 DFE에 의해 실행되지 않으며 TinkerPop 엔진에 의해 실행됩니다.

  샘플 보고서는 [DFE가 활성화된 경우의 예제](#gremlin-explain-dfe) 섹션을 참조하세요.

## Gremlin `explain` 구문
`explain`의 구문

`explain` API의 구문은 쿼리용 HTTP API의 구문과 동일합니다. 단, 다음 예제와 같이 `/gremlin` 대신 `/gremlin/explain`를 엔드포인트로 사용하는 경우는 예외입니다.

```
curl -X POST https://your-neptune-endpoint:port/gremlin/explain -d '{"gremlin":"g.V().limit(1)"}'
```

앞의 쿼리에서는 다음 출력이 생성됩니다.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============
g.V().limit(1)

Original Traversal
==================
[GraphStep(vertex,[]), RangeGlobalStep(0,1)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
        }, finishers=[limit(1)], annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .], {estimatedCardinality=INFINITY}
        }, finishers=[limit(1)], annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 18
```

## 변환되지 않은 Unconverted TinkerPop 단계
변환되지 않은 단계

순회의 모든 TinkerPop 단계가 네이티브 Neptune 연산자 범위를 가지고 있는 것이 가장 이상적입니다. 그렇지 않은 경우에는 Neptune이 연산자 범위의 격차에 대해 TinkerPop 단계 실행을 폴백합니다. 순회에서 Neptune이 아직 네이티브 범위를 가지고 있지 않은 단계를 사용하는 경우에는 `explain` 보고서에 격차가 발생했음을 알리는 경고가 표시됩니다.

해당되는 네이티브 Neptune 연산자가 없는 단계가 나타나면 해당 시점부터는 후속 단계에 네이티브 Neptune 연산자가 있는 경우라도 전체 순회가 TinkerPop 단계를 사용하여 실행됩니다.

단, Neptune 전체 텍스트 검색이 호출되는 경우는 예외입니다. NepTuneSearchStep은 네이티브에 상당하는 항목이 없는 단계를 전체 텍스트 검색 단계로 구현합니다.

## 쿼리의 모든 단계에 네이티브에 상당하는 항목이 있는 `explain` 출력 예제
네이티브에 상당하는 항목이 포함된 예제

다음은 모든 단계에 네이티브에 상당하는 항목이 있는 쿼리에 대한 `explain` 보고서 예제입니다.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============
g.V().out()

Original Traversal
==================
[GraphStep(vertex,[]), VertexStep(OUT,vertex)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
            PatternNode[(?1, ?5, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .]
            PatternNode[(?3, <~label>, ?4, <~>) . project ask .]
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], maxVarId=7}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, ?5, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], maxVarId=7}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 18
```

## 쿼리의 일부 단계에 네이티브에 상당하는 항목이 없는 예
네이티브에 상당하는 항목이 포함되지 않은 예제

Neptune은 `GraphStep` 및 `VertexStep`을 기본적으로 처리하지만, 사용자가 `FoldStep` 및 `UnfoldStep`를 도입하면 `explain` 출력이 달라집니다.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============
g.V().fold().unfold().out()

Original Traversal
==================
[GraphStep(vertex,[]), FoldStep, UnfoldStep, VertexStep(OUT,vertex)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
        }, annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep
]
+ not converted into Neptune steps: [FoldStep, UnfoldStep, VertexStep(OUT,vertex)]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .], {estimatedCardinality=INFINITY}
        }, annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep,
    NeptuneMemoryTrackerStep
]
+ not converted into Neptune steps: [FoldStep, UnfoldStep, VertexStep(OUT,vertex)]

WARNING: >> FoldStep << is not supported natively yet
```

이 경우, `FoldStep`은 네이티브 실행을 중단합니다. 그러나 후속 `VertexStep`조차도 더 이상 기본적으로 처리되지 않는데, 이는 `Fold/Unfold` 단계의 다운스트림에 나타나기 때문입니다.

성능 및 비용 절감을 위해서는 TinkerPop 단계를 구현하는 대신에 Neptune 쿼리 엔진 내에서 기본적으로 최대한 많은 양의 작업이 처리되도록 순회를 구성하려고 해야 합니다.

## Neptune 전체 텍스트 검색을 사용하는 쿼리의 예제
전체 텍스트 검색 예제

다음 쿼리는 Neptune 전체 텍스트 검색을 사용합니다.

```
g.withSideEffect("Neptune#fts.endpoint", "some_endpoint")
  .V()
  .tail(100)
  .has("Neptune#fts mark*")
  -------
  .has("name", "Neptune#fts mark*")
  .has("Person", "name", "Neptune#fts mark*")
```

이 `.has("name", "Neptune#fts mark*")` 부분은 `name`이 있는 버텍스로 검색을 제한하고, `.has("Person", "name", "Neptune#fts mark*")`은 `name` 및 레이블 `Person`이 있는 버텍스로 검색을 제한합니다. 이로 인해 `explain` 보고서에 다음과 같은 순회가 발생합니다.

```
Final Traversal
[NeptuneGraphQueryStep(Vertex) {
    JoinGroupNode {
        PatternNode[(?1, termid(1,URI), ?2, termid(0,URI)) . project distinct ?1 .], {estimatedCardinality=INFINITY}
    }, annotations={path=[Vertex(?1):GraphStep], maxVarId=4}
}, NeptuneTraverserConverterStep, NeptuneTailGlobalStep(10), NeptuneTinkerpopTraverserConverterStep, NeptuneSearchStep {
    JoinGroupNode {
        SearchNode[(idVar=?3, query=mark*, field=name) . project ask .], {endpoint=some_endpoint}
    }
    JoinGroupNode {
        SearchNode[(idVar=?3, query=mark*, field=name) . project ask .], {endpoint=some_endpoint}
    }
}]
```

## DFE가 활성화된 경우의 `explain` 사용 예제
DFE가 활성화된 경우의 예제

다음은 DFE 대체 쿼리 엔진이 활성화된 경우의 `explain` 보고서 예제입니다.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============

g.V().as("a").out().has("name", "josh").out().in().where(eq("a"))


Original Traversal
==================
[GraphStep(vertex,[])@[a], VertexStep(OUT,vertex), HasStep([name.eq(josh)]), VertexStep(OUT,vertex), VertexStep(IN,vertex), WherePredicateStep(eq(a))]

Converted Traversal
===================
Neptune steps:
[
    DFEStep(Vertex) {
      DFENode {
        DFEJoinGroupNode[ children={
          DFEPatternNode[(?1, <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>, ?2, <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>) . project DISTINCT[?1] {rangeCountEstimate=unknown}],
          DFEPatternNode[(?1, ?3, ?4, ?5) . project ALL[?1, ?4] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}]
        }, {rangeCountEstimate=unknown}
        ]
      } [Vertex(?1):GraphStep@[a], Vertex(?4):VertexStep]
    } ,
    NeptuneTraverserConverterDFEStep
]
+ not converted into Neptune steps: HasStep([name.eq(josh)]),
Neptune steps:
[
    NeptuneInterleavingStep {
      StepInfo[joinVars=[?7, ?1], frontierElement=Vertex(?7):HasStep, pathElements={a=(last,Vertex(?1):GraphStep@[a])}, listPathElement={}, indexTime=0ms],
      DFEStep(Vertex) {
        DFENode {
          DFEJoinGroupNode[ children={
            DFEPatternNode[(?7, ?8, ?9, ?10) . project ALL[?7, ?9] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}],
            DFEPatternNode[(?12, ?11, ?9, ?13) . project ALL[?9, ?12] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}]
          }, {rangeCountEstimate=unknown}
          ]
        } [Vertex(?9):VertexStep, Vertex(?12):VertexStep]
      } 
    }
]
+ not converted into Neptune steps: WherePredicateStep(eq(a)),
Neptune steps:
[
    DFECleanupStep
]


Optimized Traversal
===================
Neptune steps:
[
    DFEStep(Vertex) {
      DFENode {
        DFEJoinGroupNode[ children={
          DFEPatternNode[(?1, ?3, ?4, ?5) . project ALL[?1, ?4] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}]
        }, {rangeCountEstimate=unknown}
        ]
      } [Vertex(?1):GraphStep@[a], Vertex(?4):VertexStep]
    } ,
    NeptuneTraverserConverterDFEStep
]
+ not converted into Neptune steps: NeptuneHasStep([name.eq(josh)]),
Neptune steps:
[
    NeptuneMemoryTrackerStep,
    NeptuneInterleavingStep {
      StepInfo[joinVars=[?7, ?1], frontierElement=Vertex(?7):HasStep, pathElements={a=(last,Vertex(?1):GraphStep@[a])}, listPathElement={}, indexTime=0ms],
      DFEStep(Vertex) {
        DFENode {
          DFEJoinGroupNode[ children={
            DFEPatternNode[(?7, ?8, ?9, ?10) . project ALL[?7, ?9] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}],
            DFEPatternNode[(?12, ?11, ?9, ?13) . project ALL[?9, ?12] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}]
          }, {rangeCountEstimate=unknown}
          ]
        } [Vertex(?9):VertexStep, Vertex(?12):VertexStep]
      } 
    }
]
+ not converted into Neptune steps: WherePredicateStep(eq(a)),
Neptune steps:
[
    DFECleanupStep
]


WARNING: >> [NeptuneHasStep([name.eq(josh)]), WherePredicateStep(eq(a))] << (or one of the children for each step) is not supported natively yet

Predicates
==========
# of predicates: 8
```

보고서의 DFE 관련 섹션에 대한 설명은 [`explain`의 정보](#gremlin-explain-api-results) 섹션을 참조하세요.

# Neptune의 Gremlin `profile` API
Gremlin `profile` API

Neptune Gremlin `profile` API는 지정된 Gremlin 순회를 실행하고 해당 실행에 대한 다양한 지표를 수집하여 출력 지표로서 프로파일 보고서를 만듭니다.

이는 Neptune 엔진 고유의 정보를 보고할 수 있다는 점에서 TinkerPop .profile() 단계와 다릅니다.

프로파일 보고서에는 다음과 같이 쿼리 계획에 대한 정보가 포함됩니다.
+ 물리적 연산자 파이프라인
+ 쿼리 실행 및 직렬화를 위한 인덱스 작업
+ 결과 크기

`profile` API는 쿼리에서 엔드포인트로 `/gremlin` 대신 `/gremlin/profile`를 사용하는 연장된 버전의 HTTP API 구문을 사용합니다.

## Neptune Gremlin `profile` 전용 파라미터
`profile` 파라미터
+ **profile.results** – `boolean`, 허용된 값: `TRUE` 및 `FALSE`, 기본값: `TRUE`.

  쿼리 결과가 true이면 `profile` 보고서의 일부로 수집 및 표시됩니다. false인 경우에는 결과 수만 표시됩니다.
+ **profile.chop** – `int`, 기본값: 250.

  0으로 설정되어 있지 않으면 결과 문자열이 해당되는 문자 수에서 잘립니다. 이렇게 하면 모든 결과가 캡처되는 것을 막을 수 있습니다. 또한 프로파일 보고서에서 문자열의 크기를 간단히 제한할 수 있습니다. 0으로 설정되어 있으면 문자열에 모든 결과가 포함됩니다.
+ **profile.serializer** – `string`, 기본값: `<null>`.

  null이 아니면 수집된 결과가 직렬화된 응답 메시지에서 이 파라미터가 지정한 형식으로 반환됩니다. 이러한 응답 메시지를 만들기 위해 필요한 인덱스 작업의 수가 클라이언트에 전송되는 바이트 크기와 함께 보고됩니다.

  허용값은 `<null>` 또는 모든 유효한 MIME 형식 또는 TinkerPop 드라이버 "Serializers" 열거형 값입니다.

  ```
  "application/json" or "GRAPHSON"
  "application/vnd.gremlin-v1.0+json" or "GRAPHSON_V1"
  "application/vnd.gremlin-v1.0+json;types=false" or "GRAPHSON_V1_UNTYPED"
  "application/vnd.gremlin-v2.0+json" or "GRAPHSON_V2"
  "application/vnd.gremlin-v2.0+json;types=false" or "GRAPHSON_V2_UNTYPED"
  "application/vnd.gremlin-v3.0+json" or "GRAPHSON_V3"
  "application/vnd.gremlin-v3.0+json;types=false" or "GRAPHSON_V3_UNTYPED"
  "application/vnd.graphbinary-v1.0" or "GRAPHBINARY_V1"
  ```
+ **profile.indexOps** – `boolean`, 허용된 값: `TRUE` 및 `FALSE`, 기본값: `FALSE`.

  true이면 쿼리 실행 및 직렬화 동안 수행된 모든 인덱스 작업에 대한 세부 보고서가 표시됩니다. 경고: 보고서가 상세 표시될 수 있습니다.



## Neptune Gremlin `profile`의 샘플 출력
샘플 `profile` 출력

다음은 `profile` 쿼리 샘플입니다.

```
curl -k -X POST https://your-neptune-endpoint:port/gremlin/profile \
     -d '{"gremlin":"g.V().hasLabel(\"airport\").has(\"code\", \"AUS\").emit().repeat(in().simplePath()).times(2).limit(100)", "profile.serializer":"application/vnd.gremlin-v3.0+json"}'
```

이 쿼리는 블로그 게시물인 [사용자를 대신한 그래프 작성 - 1부 - 항로](https://aws.amazon.com/blogs/database/let-me-graph-that-for-you-part-1-air-routes/) 항로 샘플 그래프에서 실행하면 다음과 같은 `profile` 보고서를 생성합니다.

```
*******************************************************
                Neptune Gremlin Profile
*******************************************************

Query String
==================
g.V().hasLabel("airport").has("code", "AUS").emit().repeat(in().simplePath()).times(2).limit(100)

Original Traversal
==================
[GraphStep(vertex,[]), HasStep([~label.eq(airport), code.eq(AUS)]), RepeatStep(emit(true),[VertexStep(IN,vertex), PathFilterStep(simple), RepeatEndStep],until(loops(2))), RangeGlobalStep(0,100)]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <code>, "AUS", ?) . project ?1 .], {estimatedCardinality=1, indexTime=84, hashJoin=true, joinTime=3, actualTotalOutput=1}
            PatternNode[(?1, <~label>, ?2=<airport>, <~>) . project ask .], {estimatedCardinality=3374, indexTime=29, hashJoin=true, joinTime=0, actualTotalOutput=61}
            RepeatNode {
                Repeat {
                    PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . SimplePathFilter(?1, ?3)) .], {hashJoin=true, estimatedCardinality=50148, indexTime=0, joinTime=3}
                }
                Emit {
                    Filter(true)
                }
                LoopsCondition {
                    LoopsFilter([?1, ?3],eq(2))
                }
            }, annotations={repeatMode=BFS, emitFirst=true, untilFirst=false, leftVar=?1, rightVar=?3}
        }, finishers=[limit(100)], annotations={path=[Vertex(?1):GraphStep, Repeat[Vertex(?3):VertexStep]], joinStats=true, optimizationTime=495, maxVarId=7, executionTime=323}
    },
    NeptuneTraverserConverterStep
]

Physical Pipeline
=================
NeptuneGraphQueryStep
    |-- StartOp
    |-- JoinGroupOp
        |-- SpoolerOp(100)
        |-- DynamicJoinOp(PatternNode[(?1, <code>, "AUS", ?) . project ?1 .], {estimatedCardinality=1, indexTime=84, hashJoin=true})
        |-- SpoolerOp(100)
        |-- DynamicJoinOp(PatternNode[(?1, <~label>, ?2=<airport>, <~>) . project ask .], {estimatedCardinality=3374, indexTime=29, hashJoin=true})
        |-- RepeatOp
            |-- <upstream input> (Iteration 0) [visited=1, output=1 (until=0, emit=1), next=1]
            |-- BindingSetQueue (Iteration 1) [visited=61, output=61 (until=0, emit=61), next=61]
                |-- SpoolerOp(100)
                |-- DynamicJoinOp(PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . SimplePathFilter(?1, ?3)) .], {hashJoin=true, estimatedCardinality=50148, indexTime=0})
            |-- BindingSetQueue (Iteration 2) [visited=38, output=38 (until=38, emit=0), next=0]
                |-- SpoolerOp(100)
                |-- DynamicJoinOp(PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . SimplePathFilter(?1, ?3)) .], {hashJoin=true, estimatedCardinality=50148, indexTime=0})
        |-- LimitOp(100)

Runtime (ms)
============
Query Execution:  392.686
Serialization:   2636.380

Traversal Metrics
=================
Step                                                               Count  Traversers       Time (ms)    % Dur
-------------------------------------------------------------------------------------------------------------
NeptuneGraphQueryStep(Vertex)                                        100         100         314.162    82.78
NeptuneTraverserConverterStep                                        100         100          65.333    17.22
                                            >TOTAL                     -           -         379.495        -

Repeat Metrics
==============
Iteration  Visited   Output    Until     Emit     Next
------------------------------------------------------
        0        1        1        0        1        1
        1       61       61        0       61       61
        2       38       38       38        0        0
------------------------------------------------------
               100      100       38       62       62

Predicates
==========
# of predicates: 16

WARNING: reverse traversal with no edge label(s) - .in() / .both() may impact query performance

Results
=======
Count: 100
Output: [v[3], v[3600], v[3614], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[47], v[49], v[136], v[13], v[15], v[16], v[17], v[18], v[389], v[20], v[21], v[22], v[23], v[24], v[25], v[26], v[27], v[28], v[416], v[29], v[30], v[430], v[31], v[9...
Response serializer: GRYO_V3D0
Response size (bytes): 23566

Index Operations
================
Query execution:
    # of statement index ops: 3
    # of unique statement index ops: 3
    Duplication ratio: 1.0
    # of terms materialized: 0
Serialization:
    # of statement index ops: 200
    # of unique statement index ops: 140
    Duplication ratio: 1.43
    # of terms materialized: 393
```

Neptune `explain`로의 호출을 통해 반환되는 쿼리 계획 외에도 `profile` 결과에는 쿼리 실행과 관련된 런타임 통계가 포함됩니다. 각 조인 작업에는 조인 수행에 소요된 시간을 비롯해 이를 통해 전달된 실제 솔루션의 수가 태그로 지정됩니다.

`profile` 출력에는 핵심 쿼리 실행 단계 동안 소요된 시간을 비롯해 `profile.serializer` 직렬화 단계(옵션이 지정된 경우)가 포함됩니다.

각 단계 동안 수행된 인덱스 작업에 대한 분석도 `profile` 출력 하단에 포함됩니다.

동일한 쿼리를 연속적으로 실행하면 캐싱으로 인해 런타임 및 인덱스 작업 측면에서 서로 다른 결과가 표시될 수 있습니다.

`repeat()` 단계를 사용하는 쿼리의 경우, `repeat()` 단계가 `NeptuneGraphQueryStep`의 일부로 푸시 다운된 경우에 각 반복 작업의 경계 지대에 대한 분석이 제공됩니다.

## DFE가 활성화된 경우의 `profile` 보고서 차이
DFE `profile` 보고서

Neptune DFE 대체 쿼리 엔진이 활성화되면 `profile` 출력이 약간 달라집니다.

**최적화된 순회:** 이 섹션은 `explain` 출력 섹션과 비슷하지만, 추가 정보가 포함되어 있습니다. 여기에는 계획 시 고려했던 DFE 연산자 유형, 관련 최악의 사례 및 최적 사례 비용 추정치가 포함됩니다.

**물리적 파이프라인:** 이 섹션에서는 쿼리를 실행하는 데 사용되는 연산자를 캡처합니다. `DFESubQuery` 요소는 DFE가 담당하는 계획의 일부를 실행하는 데 사용하는 물리적 계획을 추상화합니다. `DFESubQuery` 요소는 DFE 통계가 나열된 다음 섹션에 설명되어 있습니다.

**DFEQueryEngine 통계:** 이 섹션은 쿼리의 적어도 일부가 DFE에 의해 실행되는 경우에만 표시됩니다. DFE에 특화된 다양한 런타임 통계를 요약하고, `DFESubQuery`에 의한 쿼리 실행의 여러 부분에 소요된 시간을 세부적으로 분석한 정보를 포함합니다.

이 섹션에서는 다양한 `DFESubQuery` 요소의 중첩된 하위 쿼리를 단순화하고, 고유 식별자는 `subQuery=`로 시작하는 헤더로 표시됩니다.

**순회 지표:** 이 섹션에는 단계별 순회 지표가 표시되며, DFE 엔진이 쿼리의 전체 또는 일부를 실행하는 경우 `DFEStep` 및/또는 `NeptuneInterleavingStep`에 대한 지표가 표시됩니다. [`explain` 및 `profile`을 사용하여 Gremlin 쿼리 조정](gremlin-traversal-tuning.md)을(를) 참조하세요.

**참고**  
DFE는 랩 모드에서 출시된 실험용 기능이므로, `profile` 출력의 정확한 형식은 여전히 변경될 수 있습니다.

## Neptune 데이터 흐름 엔진(DFE)이 활성화된 경우의 샘플 `profile` 출력
DFE `profile` 출력 예제

DFE 엔진을 사용하여 Gremlin 쿼리를 실행하는 경우 [Gremlin `profile` API](#gremlin-profile-api)의 출력 형식은 아래 예제와 같이 지정됩니다.

쿼리:

```
curl https://your-neptune-endpoint:port/gremlin/profile \
  -d "{\"gremlin\": \"g.withSideEffect('Neptune#useDFE', true).V().has('code', 'ATL').out()\"}"
```

```
*******************************************************
                    Neptune Gremlin Profile
    *******************************************************

    Query String
    ==================
    g.withSideEffect('Neptune#useDFE', true).V().has('code', 'ATL').out()

    Original Traversal
    ==================
    [GraphStep(vertex,[]), HasStep([code.eq(ATL)]), VertexStep(OUT,vertex)]

    Optimized Traversal
    ===================
    Neptune steps:
    [
        DFEStep(Vertex) {
          DFENode {
            DFEJoinGroupNode[null](
              children=[
                DFEPatternNode((?1, vp://code[419430926], ?4, defaultGraph[526]) . project DISTINCT[?1] objectFilters=(in(ATL[452987149]) . ), {rangeCountEstimate=1},
                  opInfo=(type=PipelineJoin, cost=(exp=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=0.00),wc=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=0.00)),
                    disc=(type=PipelineScan, cost=(exp=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=34.00),wc=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=34.00))))),
                DFEPatternNode((?1, ?5, ?6, ?7) . project ALL[?1, ?6] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807})],
              opInfo=[
                OperatorInfoWithAlternative[
                  rec=(type=PipelineJoin, cost=(exp=(in=1.00,out=27.76,io=0.00,comp=0.00,mem=0.00),wc=(in=1.00,out=27.76,io=0.00,comp=0.00,mem=0.00)),
                    disc=(type=PipelineScan, cost=(exp=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00),wc=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00)))),
                  alt=(type=PipelineScan, cost=(exp=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00),wc=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00)))]])
          } [Vertex(?1):GraphStep, Vertex(?6):VertexStep]
        } ,
        NeptuneTraverserConverterDFEStep,
        DFECleanupStep
    ]


    Physical Pipeline
    =================
    DFEStep
        |-- DFESubQuery1

    DFEQueryEngine Statistics
    =================
    DFESubQuery1
    ╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤════════╤═══════════╗
    ║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio  │ Time (ms) ║
    ╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪════════╪═══════════╣
    ║ 0  │ 1      │ -      │ DFESolutionInjection  │ solutions=[]                                                                                                 │ -    │ 0        │ 1         │ 0.00   │ 0.01      ║
    ║    │        │        │                       │ outSchema=[]                                                                                                 │      │          │           │        │           ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_1 │ -    │ 1        │ 1         │ 1.00   │ 0.02      ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 2  │ 3      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_2 │ -    │ 1        │ 242       │ 242.00 │ 0.02      ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 3  │ 4      │ -      │ DFEMergeChunks        │ -                                                                                                            │ -    │ 242      │ 242       │ 1.00   │ 0.01      ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 4  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 242      │ 0         │ 0.00   │ 0.01      ║
    ╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧════════╧═══════════╝


    subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_1
    ╔════╤════════╤════════╤══════════════════════╤═════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
    ║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                   │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
    ╠════╪════════╪════════╪══════════════════════╪═════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
    ║ 0  │ 1      │ -      │ DFEPipelineScan      │ pattern=Node(?1) with property 'code' as ?4 and label 'ALL' │ -    │ 0        │ 1         │ 0.00  │ 0.22      ║
    ║    │        │        │                      │ inlineFilters=[(?4 IN ["ATL"])]                             │      │          │           │       │           ║
    ║    │        │        │                      │ patternEstimate=1                                           │      │          │           │       │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 1  │ 2      │ -      │ DFEMergeChunks       │ -                                                           │ -    │ 1        │ 1         │ 1.00  │ 0.02      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 2  │ 4      │ -      │ DFERelationalJoin    │ joinVars=[]                                                 │ -    │ 2        │ 1         │ 0.50  │ 0.09      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 3  │ 2      │ -      │ DFESolutionInjection │ solutions=[]                                                │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
    ║    │        │        │                      │ outSchema=[]                                                │      │          │           │       │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 4  │ -      │ -      │ DFEDrain             │ -                                                           │ -    │ 1        │ 0         │ 0.00  │ 0.01      ║
    ╚════╧════════╧════════╧══════════════════════╧═════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


    subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_2
    ╔════╤════════╤════════╤══════════════════════╤═════════════════════════════════════╤══════╤══════════╤═══════════╤════════╤═══════════╗
    ║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                           │ Mode │ Units In │ Units Out │ Ratio  │ Time (ms) ║
    ╠════╪════════╪════════╪══════════════════════╪═════════════════════════════════════╪══════╪══════════╪═══════════╪════════╪═══════════╣
    ║ 0  │ 1      │ -      │ DFESolutionInjection │ solutions=[]                        │ -    │ 0        │ 1         │ 0.00   │ 0.01      ║
    ║    │        │        │                      │ outSchema=[?1]                      │      │          │           │        │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 1  │ 2      │ 3      │ DFETee               │ -                                   │ -    │ 1        │ 2         │ 2.00   │ 0.01      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 2  │ 4      │ -      │ DFEDistinctColumn    │ column=?1                           │ -    │ 1        │ 1         │ 1.00   │ 0.21      ║
    ║    │        │        │                      │ ordered=false                       │      │          │           │        │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 3  │ 5      │ -      │ DFEHashIndexBuild    │ vars=[?1]                           │ -    │ 1        │ 1         │ 1.00   │ 0.03      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 4  │ 5      │ -      │ DFEPipelineJoin      │ pattern=Edge((?1)-[?7:?5]->(?6))    │ -    │ 1        │ 242       │ 242.00 │ 0.51      ║
    ║    │        │        │                      │ constraints=[]                      │      │          │           │        │           ║
    ║    │        │        │                      │ patternEstimate=9223372036854775807 │      │          │           │        │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 5  │ 6      │ 7      │ DFESync              │ -                                   │ -    │ 243      │ 243       │ 1.00   │ 0.02      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 6  │ 8      │ -      │ DFEForwardValue      │ -                                   │ -    │ 1        │ 1         │ 1.00   │ 0.01      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 7  │ 8      │ -      │ DFEForwardValue      │ -                                   │ -    │ 242      │ 242       │ 1.00   │ 0.02      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 8  │ 9      │ -      │ DFEHashIndexJoin     │ -                                   │ -    │ 243      │ 242       │ 1.00   │ 0.31      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 9  │ -      │ -      │ DFEDrain             │ -                                   │ -    │ 242      │ 0         │ 0.00   │ 0.01      ║
    ╚════╧════════╧════════╧══════════════════════╧═════════════════════════════════════╧══════╧══════════╧═══════════╧════════╧═══════════╝


    Runtime (ms)
    ============
    Query Execution: 11.744

    Traversal Metrics
    =================
    Step                                                               Count  Traversers       Time (ms)    % Dur
    -------------------------------------------------------------------------------------------------------------
    DFEStep(Vertex)                                                      242         242          10.849    95.48
    NeptuneTraverserConverterDFEStep                                     242         242           0.514     4.52
                                                >TOTAL                     -           -          11.363        -

    Predicates
    ==========
    # of predicates: 18

    Results
    =======
    Count: 242


    Index Operations
    ================
    Query execution:
        # of statement index ops: 0
        # of terms materialized: 0
```

**참고**  
DFE 엔진은 랩 모드에서 출시된 실험용 기능이기 때문에 `profile` 출력의 정확한 형식은 변경될 수 있습니다.

# `explain` 및 `profile`을 사용하여 Gremlin 쿼리 조정
Gremlin 쿼리 조정

Neptune [설명](gremlin-explain-api.md) 및 [프로필](gremlin-profile-api.md) API에서 가져온 보고서에 있는 정보를 사용하여 Amazon Neptune에서 Gremlin 쿼리를 조정하여 성능을 향상시킬 수 있는 경우가 많습니다. 이를 위해서는 Neptune이 Gremlin 순회를 처리하는 방법을 이해하는 것이 도움이 됩니다.

**중요**  
TinkerPop 버전 3.4.11에서 쿼리 처리 방식의 정확성을 향상시키는 변경 사항이 적용되었지만, 현재로서는 쿼리 성능에 간혹 심각한 영향을 미칠 수 있습니다.  
예를 들어 다음과 같은 쿼리는 실행 속도가 상당히 느릴 수 있습니다.  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  out()
```
이제 TinkerPop 3.4.11의 변경 사항으로 인해 제한 단계 이후의 버텍스를 최적화하지 않은 방식으로 가져옵니다. 이를 방지하려면 `order().by()` 이후 언제든지 barrier() 단계를 추가하여 쿼리를 수정할 수 있습니다. 예제:  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  barrier().
  out()
```
TinkerPop 3.4.11은 Neptune [엔진 버전 1.0.5.0](engine-releases-1.0.5.0.md)에서 활성화되었습니다.

## Neptune의 Gremlin 순회 처리에 대한 이해
순회 처리

Gremlin 순회가 Neptune으로 전송되면 순회를 엔진이 실행할 기본 실행 계획으로 변환하는 3가지 주요 프로세스인 구문 분석, 변환, 최적화를 거치게 됩니다.

![\[3가지 프로세스를 통해 Gremlin 쿼리를 실행 계획으로 변환합니다.\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/images/Gremlin_traversal_processing.png)


### 순회 구문 분석 프로세스
구문 분석

순회 처리의 첫 번째 단계는 순회를 공통 언어로 구문 분석하는 것입니다. Neptune에서 이러한 공통 언어는 [TinkerPop API](http://tinkerpop.apache.org/javadocs/3.4.8/full/org/apache/tinkerpop/gremlin/process/traversal/Step.html)의 일부인 TinkerPop 단계 세트입니다. 각 단계는 순회 내 계산 단위를 나타냅니다.

Gremlin 순회는 문자열이나 바이트코드로 Neptune에 보낼 수 있습니다. REST 엔드포인트와 Java 클라이언트 드라이버 `submit()` 메서드는 다음 예제와 같이 순회를 문자열로 전송합니다.

```
client.submit("g.V()")
```

[Gremlin language variants(GLV)](https://tinkerpop.apache.org/docs/current/tutorials/gremlin-language-variants/)를 사용하는 애플리케이션과 언어 드라이버는 바이트코드로 순회를 전송합니다.

### 순회 변환 프로세스
변환

순회 처리의 두 번째 단계는 TinkerPop 단계를 변환된 Neptune 단계와 변환되지 않은 Neptune 단계 세트로 변환하는 것입니다. Apache TinkerPop Gremlin 쿼리 언어의 단계 대부분은 기본 Neptune 엔진에서 실행되도록 최적화된 Neptune 전용 단계로 변환됩니다. 순회 중에 Neptune에 상응하는 단계가 없는 TinkerPop 단계가 발견되면 해당 단계와 순회의 모든 후속 단계가 TinkerPop 쿼리 엔진에 의해 처리됩니다.

어떤 상황에서 어떤 단계를 변환할 수 있는지에 대한 자세한 내용은 [Gremlin 단계 지원](gremlin-step-support.md)을 참조하세요.

### 순회 최적화 프로세스
최적화

순회 처리의 마지막 단계는 옵티마이저를 통해 변환된 단계와 변환되지 않은 일련의 단계를 실행하여 최적의 실행 계획을 결정하는 것입니다. 이 최적화의 결과는 Neptune 엔진이 처리하는 실행 계획입니다.

## Neptune Gremlin `explain` API를 사용하여 쿼리 조정
설명 출력을 사용하여 조정

Neptune 설명 API는 Gremlin `explain()` 단계와 다릅니다. 쿼리를 실행할 때 Neptune 엔진이 처리할 최종 실행 계획을 반환합니다. 처리를 수행하지 않기 때문에 사용된 파라미터에 관계없이 동일한 계획을 반환하며 출력에는 실제 실행에 대한 통계가 포함되지 않습니다.

앵커리지의 모든 공항 버텍스를 찾는 다음과 같은 간단한 순회를 생각해 보세요.

```
g.V().has('code','ANC')
```

Neptune `explain` API를 통해 이 순회를 실행하는 방법에는 2가지가 있습니다. 첫 번째 방법은 다음과 같이 설명 엔드포인트에 REST를 호출하는 것입니다.

```
curl -X POST https://your-neptune-endpoint:port/gremlin/explain -d '{"gremlin":"g.V().has('code','ANC')"}'
```

두 번째 방법은 Neptune 워크벤치의 [%%gremlin](notebooks-magics.md#notebooks-cell-magics-gremlin) 셀 매직을 `explain` 파라미터와 함께 사용하는 것입니다. 그러면 셀 본문에 포함된 순회가 Neptune `explain` API로 전달되고 셀을 실행할 때 결과 출력이 표시됩니다.

```
%%gremlin explain

g.V().has('code','ANC')
```

결과 `explain` API 출력은 순회에 대한 Neptune의 실행 계획을 설명합니다. 아래 이미지에서 볼 수 있듯이, 계획에는 처리 파이프라인의 3단계가 각각 포함됩니다.

![\[간단한 Gremlin 순회에 대한 API 출력을 설명합니다.\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/images/Gremlin_explain_output_1.png)


### 변환되지 않은 단계 확인을 통해 순회 조정
변환되지 않은 단계

Neptune `explain` API 출력에서 가장 먼저 확인해야 할 사항 중 하나는 Neptune 네이티브 단계로 변환되지 않는 Gremlin 단계입니다. 쿼리 계획에서 Neptune 네이티브 단계로 변환할 수 없는 단계가 발견되면 해당 단계와 계획의 모든 후속 단계를 Gremlin 서버에서 처리합니다.

위 예제에서는 순회의 모든 단계가 변환되었습니다. 이 순회에 대한 `explain` API 출력을 살펴보겠습니다.

```
g.V().has('code','ANC').out().choose(hasLabel('airport'), values('code'), constant('Not an airport'))
```

아래 이미지에서 볼 수 있듯이, Neptune은 `choose()` 단계를 변환할 수 없습니다.

![\[모든 단계를 변환할 수 없는 API 출력에 대해 설명합니다.\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/images/Gremlin_explain_output_2.png)


순회 성능을 조정하는 방법에는 여러 가지가 있습니다. 첫 번째는 변환할 수 없는 단계를 제거하는 방식으로 다시 작성하는 것입니다. 또 다른 방법은 다른 모든 단계를 네이티브 단계로 변환할 수 있도록 단계를 순회 종료 시점으로 옮기는 것입니다.

단계가 변환되지 않은 쿼리 계획을 항상 조정할 필요는 없습니다. 변환할 수 없는 단계가 순회 종료 시점이고 그래프가 순회되는 방식보다는 출력 형식이 지정되는 방식과 관련된 경우 성능에 거의 영향을 미치지 않을 수 있습니다.

### 


Neptune `explain` API의 출력을 검사할 때 확인해야 할 또 다른 사항은 인덱스를 사용하지 않는 단계입니다. 다음 순회는 앵커리지에 도착하는 항공편이 있는 모든 공항을 검색합니다.

```
g.V().has('code','ANC').in().values('code')
```

이 순회에 대한 설명 API의 출력은 다음과 같습니다.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============

g.V().has('code','ANC').in().values('code')

Original Traversal
==================
[GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,vertex), PropertiesStep([code],value)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
            PatternNode[(?1, <code>, "ANC", ?) . project ask .]
            PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .]
            PatternNode[(?3, <~label>, ?4, <~>) . project ask .]
            PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .]
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1}
            PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY}
            PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 26

WARNING: reverse traversal with no edge label(s) - .in() / .both() may impact query performance
```

출력 하단에 있는 `WARNING` 메시지는 Neptune이 유지 관리하는 3개의 인덱스 중 하나를 사용하여 순회의 `in()` 단계를 처리할 수 없기 때문에 발생합니다([Neptune에서 문을 인덱싱하는 방식](feature-overview-storage-indexing.md) 및 [Neptune의 Gremlin 문](gremlin-explain-background-statements.md)참조). 이 `in()` 단계에는 엣지 필터가 없어 `SPOG`, `POGS` 또는 `GPSO` 인덱스를 사용하여 해결할 수 없습니다. 대신 Neptune은 요청된 버텍스를 찾기 위해 통합 스캔을 수행해야 하는데, 이렇게 하면 효율성이 훨씬 떨어집니다.

이 상황에서 순회를 조정하는 방법은 2가지입니다. 첫 번째 방법은 인덱싱된 조회를 사용하여 쿼리를 해결할 수 있도록 `in()` 단계에 필터링 기준을 하나 이상 추가하는 것입니다. 위 예제의 경우 다음과 같을 수 있습니다.

```
g.V().has('code','ANC').in('route').values('code')
```

수정된 순회에 대한 Neptune `explain` API 출력에는 더 이상 다음 `WARNING` 메시지가 포함되지 않습니다.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============

g.V().has('code','ANC').in('route').values('code')

Original Traversal
==================
[GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,[route],vertex), PropertiesStep([code],value)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
            PatternNode[(?1, <code>, "ANC", ?) . project ask .]
            PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . ContainsFilter(?5 in (<route>)) .]
            PatternNode[(?3, <~label>, ?4, <~>) . project ask .]
            PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .]
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1}
            PatternNode[(?3, ?5=<route>, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=32042}
            PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 26
```

이런 종류의 순회를 많이 실행하는 경우 선택 사항인 `OSGP` 인덱스가 활성화된 Neptune DB 클러스터에서 순회를 실행하는 방법도 있습니다([OSGP 인덱스 활성화](feature-overview-storage-indexing.md#feature-overview-storage-indexing-osgp) 참조). `OSGP` 인덱스를 활성화하면 단점이 뒤따릅니다.
+ 데이터를 로드하기 전에 DB 클러스터에서 활성화해야 합니다.
+ 버텍스와 엣지의 삽입 속도가 최대 23%까지 느려질 수 있습니다.
+ 스토리지 사용량이 약 20% 증가하게 됩니다.
+ 모든 인덱스에 요청을 분산시키는 읽기 쿼리로 인해 지연 시간이 길어질 수 있습니다.

제한된 쿼리 패턴 세트에는 `OSGP` 인덱스를 사용하는 것이 좋습니다. 하지만 이러한 패턴을 자주 실행하지 않는다면 일반적으로 3가지 기본 인덱스를 사용하여 작성한 순회 문제를 해결할 수 있도록 하는 것이 좋습니다.

### 많은 수의 조건자 사용
많은 조건자

Neptune은 그래프의 각 엣지 레이블과 각각의 고유한 버텍스 또는 엣지 속성 이름을 조건자로 취급하며, 상대적으로 적은 수의 고유 조건자와 함께 작동하도록 기본적으로 설계되었습니다. 그래프 데이터에 조건자가 수천 개가 넘으면 성능이 저하될 수 있습니다.

다음과 같은 경우 Neptune `explain` 출력에서 경고를 표시합니다.

```
Predicates
==========
# of predicates: 9549
WARNING: high predicate count (# of distinct property names and edge labels)
```

레이블과 속성의 개수, 즉 조건자 수를 줄이기 위해 데이터 모델을 재작업하기가 어려운 경우, 위에서 설명한 것처럼 `OSGP` 인덱스가 활성화된 DB 클러스터에서 실행하여 순회를 조정하는 것이 가장 좋습니다.

## Neptune Gremlin `profile` API를 사용하여 순회 조정
프로필 출력을 사용하여 조정

Neptune `profile` API는 Gremlin `profile()` 단계와 상당히 다릅니다. `explain` API와 마찬가지로 출력에는 Neptune 엔진이 순회를 실행할 때 사용하는 쿼리 계획이 포함됩니다. 또한 `profile` 출력에는 파라미터 설정 방식에 따른 순회의 실제 실행 통계도 포함됩니다.

앵커리지의 모든 공항 버텍스를 찾는 단순 순회를 다시 예로 들어 보겠습니다.

```
g.V().has('code','ANC')
```

`explain` API와 마찬가지로 REST 호출을 사용하여 `profile` API를 간접적으로 호출할 수 있습니다.

```
curl -X POST https://your-neptune-endpoint:port/gremlin/profile -d '{"gremlin":"g.V().has('code','ANC')"}'
```

Neptune 워크벤치의 [%%gremlin](notebooks-magics.md#notebooks-cell-magics-gremlin) 셀 매직도 `profile` 파라미터와 함께 사용할 수 있습니다. 그러면 셀 본문에 포함된 순회가 Neptune `profile` API로 전달되고 셀을 실행할 때 결과 출력이 표시됩니다.

```
%%gremlin profile

g.V().has('code','ANC')
```

결과 `profile` API 출력에는 다음 이미지에서 볼 수 있듯이 순회에 대한 Neptune의 실행 계획과 계획 실행에 대한 통계가 모두 포함됩니다.

![\[Neptune profile API 출력의 예입니다.\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/images/Gremlin_profile_output_1.png)


`profile` 출력에서 실행 계획 섹션에는 순회에 대한 최종 실행 계획만 포함되며, 중간 단계는 포함되지 않습니다. 파이프라인 섹션에는 수행된 물리적 파이프라인 작업과 순회 실행에 걸린 실제 시간(밀리초)이 포함됩니다. 런타임 지표는 서로 다른 두 버전의 순회를 최적화하기 위해 걸리는 시간을 비교하는 데 매우 유용합니다.

**참고**  
첫 번째 실행으로 인해 관련 데이터가 캐싱되기 때문에 순회의 초기 런타임은 일반적으로 후속 런타임보다 깁니다.

`profile` 출력의 세 번째 섹션에는 실행 통계와 순회 결과가 포함됩니다. 이 정보가 순회 조정에 얼마나 유용한지 알아보려면 이름이 'Anchora'로 시작하는 모든 공항과 해당 공항에서 두 번의 홉으로 도달할 수 있는 모든 공항을 찾아 공항 코드, 비행 경로 및 거리를 반환하는 다음 순회 방법을 고려해 보세요.

```
%%gremlin profile

g.withSideEffect("Neptune#fts.endpoint", "{your-OpenSearch-endpoint-URL").
    V().has("city", "Neptune#fts Anchora~").
    repeat(outE('route').inV().simplePath()).times(2).
    project('Destination', 'Route').
        by('code').
        by(path().by('code').by('dist'))
```

### Neptune `profile` API 출력의 순회 지표
순회 지표

모든 `profile` 출력에서 사용할 수 있는 첫 번째 지표 세트는 순회 지표입니다. 이러한 지표는 Gremlin `profile()` 단계 지표와 비슷하지만, 몇 가지 차이점이 있습니다.

```
Traversal Metrics
=================
Step                                                               Count  Traversers       Time (ms)    % Dur
-------------------------------------------------------------------------------------------------------------
NeptuneGraphQueryStep(Vertex)                                       3856        3856          91.701     9.09
NeptuneTraverserConverterStep                                       3856        3856          38.787     3.84
ProjectStep([Destination, Route],[value(code), ...                  3856        3856         878.786    87.07
  PathStep([value(code), value(dist)])                              3856        3856         601.359
                                            >TOTAL                     -           -        1009.274        -
```

순회-지표 표의 첫 번째 열에는 순회에서 실행되는 단계가 나열됩니다. 처음 두 단계는 일반적으로 Neptune 관련 단계 `NeptuneGraphQueryStep` 및 `NeptuneTraverserConverterStep`입니다.

`NeptuneGraphQueryStep`은 Neptune 엔진에서 기본적으로 변환 및 실행할 수 있는 전체 순회 부분의 실행 시간을 나타냅니다.

`NeptuneTraverserConverterStep`은 변환된 단계의 출력을 TinkerPop Traverser로 변환하는 프로세스를 나타냅니다. TinkerPop Traverser는 변환할 수 없는 단계가 있는 경우, 이를 처리하거나 결과를 TinkerPop 호환 형식으로 반환할 수 있습니다.

위의 예에는 변환되지 않은 단계가 여러 개 있으므로, 각 TinkerPop 단계(`ProjectStep`, `PathStep`)가 표에 행으로 표시되는 것을 볼 수 있습니다.

표의 두 번째 열인 `Count`는 단계를 통과한 *표현된* 순회 수를 보고하고, 세 번째 열인 `Traversers`는 [TinkerPop 프로필 단계 설명서](https://tinkerpop.apache.org/docs/current/reference/#profile-step)에 나와 있는 대로 해당 단계를 통과한 순회 수를 보고합니다.

이 예제에서는 `NeptuneGraphQueryStep`에서 3,856개의 버텍스와 3,856개의 Traverser가 반환되며, 이 수치는 나머지 처리 과정에서 동일하게 유지됩니다. `ProjectStep` 및 `PathStep`은 결과를 필터링하는 것이 아니라 형식을 지정하기 때문입니다.

**참고**  
TinkerPop과 달리 Neptune 엔진은 `NeptuneGraphQueryStep` 및 `NeptuneTraverserConverterStep` 단계를 *대량 확장해서* 성능을 최적화하지 않습니다. 대량 확장은 TinkerPop 연산으로 동일한 버텍스에 Traverser를 결합하여 운영 오버헤드를 줄이는 것으로, 이 때문에 `Count` 및 `Traversers` 숫자가 달라집니다. 대량 확장은 Neptune이 TinkerPop에 위임한 단계에서만 발생하고 Neptune이 기본적으로 처리하는 단계에서는 발생하지 않기 때문에 `Count` 및 `Traverser` 열이 흡사합니다.

시간 열에는 해당 단계에 걸린 시간(밀리초)이 표시되고, `% Dur` 열에는 해당 단계에 소요된 총 처리 시간의 백분율이 표시됩니다. 이는 가장 많은 시간이 소요된 단계를 표시하여 조정 작업을 어디에 집중해야 하는지를 알려주는 지표입니다.

### Neptune `profile` API 출력의 인덱스 작업 지표
인덱스 작업

Neptune 프로필 API 출력의 또 다른 지표 세트는 인덱스 작업입니다.

```
Index Operations
================
Query execution:
    # of statement index ops: 23191
    # of unique statement index ops: 5960
    Duplication ratio: 3.89
    # of terms materialized: 0
```

이 지표는 다음을 보고합니다.
+ 인덱스 총 조회 수.
+ 수행된 고유 인덱스 조회 수.
+ 전체 인덱스 조회와 고유 인덱스 조회의 비율. (비율이 낮을수록 중복성 감소)
+ 용어 딕셔너리에서 구체화된 용어 수.

### Neptune `profile` API 출력의 반복 지표
반복 지표

순회에 위 예와 같은 `repeat()` 단계를 사용하는 경우 반복 지표가 포함된 섹션이 `profile` 출력에 나타납니다.

```
Repeat Metrics
==============
Iteration  Visited   Output    Until     Emit     Next
------------------------------------------------------
        0        2        0        0        0        2
        1       53        0        0        0       53
        2     3856     3856     3856        0        0
------------------------------------------------------
              3911     3856     3856        0       55
```

이 지표는 다음을 보고합니다.
+ 행의 루프 수(`Iteration` 열).
+ 루프가 방문한 요소 수(`Visited` 열).
+ 루프가 출력하는 요소 수(`Output` 열).
+ 루프가 출력하는 마지막 요소(`Until` 열).
+ 루프에서 내보내는 요소 수(`Emit` 열).
+ 루프에서 다음 루프로 전달된 요소 수(`Next` 열).

이러한 반복 지표는 순회의 분기 요인을 이해하고 데이터베이스에서 수행되고 있는 작업의 양을 파악하는 데 매우 유용합니다. 이 수치를 사용하여 성능 문제를 진단할 수 있습니다. 특히 동일한 순회라도 파라미터에 따라 성능이 크게 달라질 경우 더욱 그렇습니다.

### Neptune `profile` API 출력의 전체 텍스트 검색 지표
전체 텍스트 검색 지표

위의 예와 같이 순회에서 [전체 텍스트 검색](full-text-search.md) 조회를 사용하는 경우 전체 텍스트 검색(FTS) 지표가 포함된 섹션이 `profile` 출력에 나타납니다.

```
FTS Metrics
==============
SearchNode[(idVar=?1, query=Anchora~, field=city) . project ?1 .],
    {endpoint=your-OpenSearch-endpoint-URL, incomingSolutionsThreshold=1000, estimatedCardinality=INFINITY,
    remoteCallTimeSummary=[total=65, avg=32.500000, max=37, min=28],
    remoteCallTime=65, remoteCalls=2, joinTime=0, indexTime=0, remoteResults=2}

    2 result(s) produced from SearchNode above
```

이는 ElasticSearch(ES) 클러스터로 전송된 쿼리를 보여주고, 전체 텍스트 검색과 관련된 성능 문제를 정확히 찾아내는 데 도움이 될 수 있는 ElasticSearch와의 상호 작용에 대한 몇 가지 지표를 보고합니다.
+ ElasticSearch 인덱스 호출에 대한 요약 정보:
  + 모든 remoteCall이 쿼리를 충족하는 데 필요한 밀리초 단위의 총 시간(`total`).
  + remoteCall에 소요된 밀리초 단위의 평균 시간(`avg`).
  + remoteCall에 소요된 밀리초 단위의 최소 시간(`min`).
  + remoteCall에 소요된 밀리초 단위의 최대 시간(`max`).
+ ElasticSearch에 대한 remoteCall에 소요된 총 시간(`remoteCallTime`).
+ ElasticSearch에 대한 remoteCall 수(`remoteCalls`).
+ ElasticSearch 결과의 조인에 소요된 밀리초 단위의 시간(`joinTime`).
+ 인덱스 조회에 소요된 밀리초 단위의 시간(`indexTime`).
+ ElasticSearch에서 반환한 총 결과 수(`remoteResults`).

# Amazon Neptune의 네이티브 Gremlin 단계 지원
Gremlin 단계 지원

[Gremlin 쿼리 조정](gremlin-traversal-tuning.md)에서 설명한 것처럼 Amazon Neptune 엔진은 현재 모든 Gremlin 단계를 기본적으로 완벽하게 지원하지는 않습니다. 현재 지원은 4가지 범주로 나뉩니다.
+ [항상 네이티브 Neptune 엔진 작업으로 변환할 수 있는 Gremlin 단계](#gremlin-steps-always)
+ [경우에 따라 네이티브 Neptune 엔진 작업으로 변환할 수 있는 Gremlin 단계](#gremlin-steps-sometimes) 
+ [네이티브 Neptune 엔진 작업으로 변환되지 않는 Gremlin 단계](#gremlin-steps-never) 
+ [Neptune에서 전혀 지원되지 않는 Gremlin 단계](#neptune-gremlin-steps-unsupported) 

## 항상 네이티브 Neptune 엔진 작업으로 변환할 수 있는 Gremlin 단계
항상 변환됨

다음 조건을 충족하는 경우 많은 Gremlin 단계를 네이티브 Neptune 엔진 작동으로 변환할 수 있습니다.
+ 쿼리에서 이들 단계 앞에는 변환할 수 없는 단계가 포함되지 않습니다.
+ 상위 단계(있는 경우)를 변환할 수 있습니다.
+ 모든 하위 순회(있는 경우)를 변환할 수 있습니다.

다음 Gremlin 단계는 해당 조건을 충족하는 경우 항상 네이티브 Neptune 엔진 작업으로 변환됩니다.
+ [and( )](http://tinkerpop.apache.org/docs/current/reference/#and-step)
+ [as( )](http://tinkerpop.apache.org/docs/current/reference/#as-step)
+ [count( )](http://tinkerpop.apache.org/docs/current/reference/#count-step)
+ [E( )](http://tinkerpop.apache.org/docs/current/reference/#graph-step)
+ [emit( )](http://tinkerpop.apache.org/docs/current/reference/#emit-step)
+ [explain( )](http://tinkerpop.apache.org/docs/current/reference/#explain-step)
+ [group( )](http://tinkerpop.apache.org/docs/current/reference/#group-step)
+ [groupCount( )](http://tinkerpop.apache.org/docs/current/reference/#groupcount-step)
+ [identity( )](http://tinkerpop.apache.org/docs/current/reference/#identity-step)
+ [is( )](http://tinkerpop.apache.org/docs/current/reference/#is-step)
+ [key( )](http://tinkerpop.apache.org/docs/current/reference/#key-step)
+ [label( )](http://tinkerpop.apache.org/docs/current/reference/#label-step)
+ [limit( )](http://tinkerpop.apache.org/docs/current/reference/#limit-step)
+ [local( )](http://tinkerpop.apache.org/docs/current/reference/#local-step)
+ [loops( )](http://tinkerpop.apache.org/docs/current/reference/#loops-step)
+ [not( )](http://tinkerpop.apache.org/docs/current/reference/#not-step)
+ [or( )](http://tinkerpop.apache.org/docs/current/reference/#or-step)
+ [profile( )](http://tinkerpop.apache.org/docs/current/reference/#profile-step)
+ [properties( )](http://tinkerpop.apache.org/docs/current/reference/#properties-step)
+ [subgraph( )](http://tinkerpop.apache.org/docs/current/reference/#subgraph-step)
+ [until( )](http://tinkerpop.apache.org/docs/current/reference/#until-step)
+ [V( )](http://tinkerpop.apache.org/docs/current/reference/#graph-step)
+ [value( )](http://tinkerpop.apache.org/docs/current/reference/#value-step)
+ [valueMap( )](http://tinkerpop.apache.org/docs/current/reference/#valuemap-step)
+ [values( )](http://tinkerpop.apache.org/docs/current/reference/#values-step)

## 경우에 따라 네이티브 Neptune 엔진 작업으로 변환할 수 있는 Gremlin 단계
일부 변환됨

일부 Gremlin 단계는 상황에 따라 네이티브 Neptune 엔진 작업으로 변환될 수 있지만, 다른 상황에서는 변환되지 않습니다.
+ [addE( )](http://tinkerpop.apache.org/docs/current/reference/#addedge-step)   –   순회를 키로 포함하는 `property()` 단계가 바로 뒤에 오는 경우를 제외하고는 일반적으로 `addE()` 단계를 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
+ [addV( )](http://tinkerpop.apache.org/docs/current/reference/#addvertex-step)   –  순회를 키로 포함하는 `property()` 단계가 바로 뒤에 오거나 여러 레이블이 할당되지 않는 한, 일반적으로 `addV()` 단계를 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
+ [aggregate( )](http://tinkerpop.apache.org/docs/current/reference/#store-step)   –   단계가 하위 순회 또는 하위 수준 순회에 사용되지 않는 경우 또는 저장되는 값이 버텍스, 엣지, id, 레이블 또는 속성값이 아닌 경우에는 일반적으로 `aggregate()` 단계를 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.

  아래 예에서는 `aggregate()`가 하위 순회에 사용되어 변환되지 않습니다.

  ```
  g.V().has('code','ANC').as('a')
       .project('flights').by(select('a')
       .outE().aggregate('x'))
  ```

  이 예제에서는 저장된 값이 값의 `min()`이므로, aggregate()가 변환되지 않습니다.

  ```
  g.V().has('code','ANC').outE().aggregate('x').by(values('dist').min())
  ```
+ [barrier( )](http://tinkerpop.apache.org/docs/current/reference/#barrier-step)   –   다음 단계가 변환되지 않는 한, `barrier()` 단계는 일반적으로 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
+ [cap( )](http://tinkerpop.apache.org/docs/current/reference/#cap-step)   –   `cap()` 단계를 `unfold()` 단계와 결합하여 버텍스, 엣지, id 또는 속성값 집합을 펼친 버전을 반환하는 경우에만 단계가 변환됩니다. 이 예제에서 `cap()`는 `.unfold()` 다음에 오기 때문에 변환됩니다.

  ```
  g.V().has('airport','country','IE').aggregate('airport').limit(2)
       .cap('airport').unfold()
  ```

  그러나 `.unfold()`를 제거하면 `cap()`은 변환되지 않습니다.

  ```
  g.V().has('airport','country','IE').aggregate('airport').limit(2)
       .cap('airport')
  ```
+ [coalesce( )](http://tinkerpop.apache.org/docs/current/reference/#coalesce-step)   –   `coalesce()` 단계는 [TinkerPop 레시피 페이지](http://tinkerpop.apache.org/docs/current/recipes/)에서 권장하는 [업서트 패턴](http://tinkerpop.apache.org/docs/current/recipes/#element-existence)을 따르는 경우에만 변환됩니다. 다른 coalesce() 패턴은 허용되지 않습니다. 모든 하위 순회가 변환될 수 있고, 모든 하위 순회가 출력값과 동일한 유형(버텍스, 엣지, id, 값, 키 또는 레이블)을 생성하고, 전부 새 요소를 순회하고, `repeat()` 단계를 포함하지 않는 경우로 변환이 제한됩니다.
+ [constant( )](http://tinkerpop.apache.org/docs/current/reference/#constant-step)   –   constant() 단계는 현재 다음과 같이 일정한 값을 할당하기 위해 순회의 `sack().by()` 부분 내에서 사용되는 경우에만 변환됩니다.

  ```
  g.V().has('code','ANC').sack(assign).by(constant(10)).out().limit(2)
  ```
+ [cyclicPath( )](http://tinkerpop.apache.org/docs/current/reference/#cyclicpath-step)   –   단계가 `by()`, `from()` 또는 `to()` 복조기와 함께 사용되지 않는 한, 일반적으로 `cyclicPath()` 단계는 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다. 예를 들어, 다음 쿼리에서는 `cyclicPath()`가 변환되지 않습니다.

  ```
  g.V().has('code','ANC').as('a').out().out().cyclicPath().by('code')
  g.V().has('code','ANC').as('a').out().out().cyclicPath().from('a')
  g.V().has('code','ANC').as('a').out().out().cyclicPath().to('a')
  ```
+ [drop( )](http://tinkerpop.apache.org/docs/current/reference/#drop-step)   –   `sideEffect(`) 또는 `optional()` 단계 내에서 단계를 사용하지 않는 한, `drop()` 단계는 일반적으로 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
+ [fold( )](http://tinkerpop.apache.org/docs/current/reference/#fold-step)   –   fold() 단계를 변환할 수 있는 상황은 두 가지뿐입니다. 즉, [TinkerPop 레시피 페이지](http://tinkerpop.apache.org/docs/current/recipes/)에서 권장하는 [업서트 패턴](http://tinkerpop.apache.org/docs/current/recipes/#element-existence)에서 사용되는 경우와 다음과 같은 `group().by()` 컨텍스트에서 사용되는 경우입니다.

  ```
  g.V().has('code','ANC').out().group().by().by(values('code', 'city').fold())
  ```
+  [has( )](http://tinkerpop.apache.org/docs/current/reference/#has-step) - `has()` 단계는 일반적으로 `T`를 사용하는 쿼리가 술어 `P.eq`, `P.neq` 또는 `P.contains`를 사용할 경우 Neptune 엔진의 네이티브 연산으로 변환될 수 있습니다. `has()`의 변형들도 `P` 인스턴스가 네이티브로 변환됨을 암시하므로, `hasId(‘id1234’)`와 같이 `has(eq, T.id, ‘id1234’)`와 동등한 경우도 네이티브로 변환됩니다.
+ [id( )](http://tinkerpop.apache.org/docs/current/reference/#id-step)   –   다음과 같이 속성에 사용하지 않는 한 `id()` 단계가 변환됩니다.

  ```
  g.V().has('code','ANC').properties('code').id()
  ```
+  [mergeE()](https://tinkerpop.apache.org/docs/current/reference/#mergeedge-step) - `mergeE()` 단계는 파라미터(병합 조건, `onCreate` 및 `onMatch`)가 상수(`null`, 상수 `Map` 또는 `select()`의 `Map`)인 경우 Neptune 엔진의 네이티브 작업으로 변환될 수 있습니다. [업서트 엣지](https://docs.aws.amazon.com//neptune/latest/userguide/gremlin-efficient-upserts.html#gremlin-upserts-edges)의 모든 예제를 변환할 수 있습니다.
+  [mergeV()](https://tinkerpop.apache.org/docs/current/reference/#mergevertex-step) - `onCreate` 단계는 파라미터(병합 조건, `onMatch` 및 `null`)가 상수( 상수 `Map` 또는 `Map`의 `select()`)인 경우 Neptune 엔진의 네이티브 작업으로 변환될 수 있습니다. [업서트 버텍스](https://docs.aws.amazon.com//neptune/latest/userguide/gremlin-efficient-upserts.html#gremlin-upserts-vertices)의 모든 예제를 변환할 수 있습니다.
+ [order( )](http://tinkerpop.apache.org/docs/current/reference/#order-step)   –   다음 중 하나에 해당하지 않는 한 `order()` 단계를 일반적으로 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
  + 이 `order()` 단계는 다음과 같이 중첩된 하위 순회 내에 있습니다.

    ```
    g.V().has('code','ANC').where(V().out().order().by(id))
    ```
  + 가령 `order(local)`와 같이 로컬 주문이 사용되고 있습니다.
  + 사용자 지정 비교기가 `by()` 변조에서 순서를 지정하는 데 사용됩니다. `sack()`을 사용하는 경우를 예로 들 수 있습니다.

    ```
    g.withSack(0).
      V().has('code','ANC').
          repeat(outE().sack(sum).by('dist').inV()).times(2).limit(10).
          order().by(sack())
    ```
  + 동일한 요소에 여러 개의 순서가 있습니다.
+ [project( )](http://tinkerpop.apache.org/docs/current/reference/#project-step)   –   다음과 같이 `project()` 뒤에 오는 `by()` 문 수가 지정된 레이블 수와 일치하지 않는 경우를 제외하고는 일반적으로 `project()` 단계를 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.

  ```
  g.V().has('code','ANC').project('x', 'y').by(id)
  ```
+ [range( )](http://tinkerpop.apache.org/docs/current/reference/#range-step)   –   범위의 하한이 0인 경우(예: `range(0,3)`)에만 `range()` 단계가 변환됩니다.
+ [repeat( )](http://tinkerpop.apache.org/docs/current/reference/#repeat-step)   –   `repeat()` 단계는 다음과 같이 다른 `repeat()` 단계에 중첩되지 않는 한 일반적으로 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.

  ```
  g.V().has('code','ANC').repeat(out().repeat(out()).times(2)).times(2)
  ```
+ [sack( )](http://tinkerpop.apache.org/docs/current/reference/#sack-step)   –   `sack()` 단계는 다음과 같은 경우를 제외하고 일반적으로 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
  + 숫자가 아닌 sack 연산자를 사용하는 경우.
  + `+`, `-`, `mult`, `div`, `min`, `max` 이외의 숫자형 sack 연산자를 사용하는 경우.
  + 다음과 같이 sack 값을 기준으로 필터링하는 `where()` 단계 내에서 `sack()`을 사용하는 경우.

    ```
    g.V().has('code','ANC').sack(assign).by(values('code')).where(sack().is('ANC'))
    ```
+ [sum( )](http://tinkerpop.apache.org/docs/current/reference/#sum-step)   –   `sum()` 단계는 일반적으로 네이티브 Neptune 엔진 작업으로 변환될 수 있지만, 다음과 같이 전역 합계를 계산하는 데 사용할 때는 변환되지 않습니다.

  ```
  g.V().has('code','ANC').outE('routes').values('dist').sum()
  ```
+ [union( )](http://tinkerpop.apache.org/docs/current/reference/#union-step)   –   `union()` 단계는 터미널 단계를 제외하고 쿼리의 마지막 단계인 경우 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
+ [unfold( )](http://tinkerpop.apache.org/docs/current/reference/#unfold-step)   –   [TinkerPop 레시피 페이지](http://tinkerpop.apache.org/docs/current/recipes/)에서 권장하는 [업서트 패턴](http://tinkerpop.apache.org/docs/current/recipes/#element-existence)으로 사용하고 다음과 같이 `cap()`을 함께 사용하는 경우에만 `unfold()` 단계를 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.

  ```
  g.V().has('airport','country','IE').aggregate('airport').limit(2)
       .cap('airport').unfold()
  ```
+ [where( )](http://tinkerpop.apache.org/docs/current/reference/#where-step)   –   `where()` 단계는 다음과 같은 경우를 제외하고 일반적으로 네이티브 Neptune 엔진 작업으로 변환할 수 있습니다.
  + 다음과 같이 by() 변조를 사용하는 경우.

    ```
    g.V().hasLabel('airport').as('a')
         .where(gt('a')).by('runways')
    ```
  + `eq`, `neq`, `within`, `without` 이외의 비교 연산자를 사용하는 경우.
  + 사용자 제공 집계를 사용하는 경우.

## 네이티브 Neptune 엔진 작업으로 변환되지 않는 Gremlin 단계
변환되지 않음

다음 Gremlin 단계는 Neptune에서 지원되지만, 네이티브 Neptune 엔진 작업으로 변환되지는 않습니다. 대신 Gremlin 서버에서 실행됩니다.
+ [choose( )](http://tinkerpop.apache.org/docs/current/reference/#choose-step)
+ [coin( )](http://tinkerpop.apache.org/docs/current/reference/#coin-step)
+ [inject( )](http://tinkerpop.apache.org/docs/current/reference/#inject-step)
+ [match( )](http://tinkerpop.apache.org/docs/current/reference/#match-step)
+ [math( )](http://tinkerpop.apache.org/docs/current/reference/#math-step)
+ [max( )](http://tinkerpop.apache.org/docs/current/reference/#max-step)
+ [mean( )](http://tinkerpop.apache.org/docs/current/reference/#mean-step)
+ [min( )](http://tinkerpop.apache.org/docs/current/reference/#min-step)
+ [option( )](http://tinkerpop.apache.org/docs/current/reference/#option-step)
+ [optional( )](http://tinkerpop.apache.org/docs/current/reference/#optional-step)
+ [path( )](http://tinkerpop.apache.org/docs/current/reference/#path-step)
+ [propertyMap( )](http://tinkerpop.apache.org/docs/current/reference/#propertymap-step)
+ [sample( )](http://tinkerpop.apache.org/docs/current/reference/#sample-step)
+ [skip( )](http://tinkerpop.apache.org/docs/current/reference/#skip-step)
+ [tail( )](http://tinkerpop.apache.org/docs/current/reference/#tail-step)
+ [timeLimit( )](http://tinkerpop.apache.org/docs/current/reference/#timelimit-step)
+ [tree( )](http://tinkerpop.apache.org/docs/current/reference/#tree-step)

## Neptune에서 전혀 지원되지 않는 Gremlin 단계
전혀 지원되지 않음

다음 Gremlin 단계는 Neptune에서 전혀 지원되지 않습니다. 대부분의 경우 `GraphComputer`가 필요하기 때문인데, Neptune은 이를 현재 지원하지 않습니다.
+ [connectedComponent( )](http://tinkerpop.apache.org/docs/current/reference/#connectedcomponent-step)
+ [io( )](http://tinkerpop.apache.org/docs/current/reference/#io-step)
+ [shortestPath( )](http://tinkerpop.apache.org/docs/current/reference/#shortestpath-step)
+ [withComputer( )](http://tinkerpop.apache.org/docs/current/reference/#with-step)
+ [pageRank( )](http://tinkerpop.apache.org/docs/current/reference/#pagerank-step)
+ [peerPressure( )](http://tinkerpop.apache.org/docs/current/reference/#peerpressure-step)
+ [program( )](http://tinkerpop.apache.org/docs/current/reference/#program-step)

`io()` 단계는 URL에서 `read()`에는 사용할 수 있지만, `write()`에는 사용할 수 없다는 점을 고려하면 부분적으로 지원된다고 할 수 있습니다.

# Neptune DFE 쿼리 엔진과 함께 Gremlin 사용
Gremlin과 DFE

`neptune_lab_mode` DB 클러스터 파라미터를 `DFEQueryEngine=enabled`로 설정하여 [랩 모드](features-lab-mode.md)에서 DFE라고 하는 Neptune [대체 쿼리 엔진](neptune-dfe-engine.md)을 완전히 활성화하면, Neptune은 읽기 전용 Gremlin 쿼리/순회를 중간 논리적 표현으로 변환하여 가능한 경우 DFE 엔진에서 실행합니다.

하지만 DFE는 아직 모든 Gremlin 단계를 지원하지는 않습니다. DFE에서 기본적으로 단계를 실행할 수 없는 경우 Neptune은 TinkerPop으로 폴백하여 단계를 실행합니다. `explain` 및 `profile` 보고서에는 이러한 상황이 발생할 경우 경고를 표시합니다.

# DFE의 Gremlin 단계 적용 범위


 Gremlin DFE는 랩모드 기능이며 클러스터 파라미터를 활성화하거나 `Neptune#useDFE` 쿼리 힌트를 사용하여 사용할 수 있습니다. 자세한 내용은 [Neptune DFE 쿼리 엔진과 함께 Gremlin 사용](https://docs.aws.amazon.com//neptune/latest/userguide/gremlin-with-dfe.html)을 참조하세요.

 다음 단계는 Gremlin DFE에서 사용할 수 있습니다.

## 경로 및 순회 단계:


 [asDate()](https://tinkerpop.apache.org/docs/current/reference/#asDate-step), [barrier()](https://tinkerpop.apache.org/docs/current/reference/#barrier-step), [call()](https://tinkerpop.apache.org/docs/current/reference/#call-step), [cap()](https://tinkerpop.apache.org/docs/current/reference/#cap-step), [dateAdd()](https://tinkerpop.apache.org/docs/current/reference/#dateadd-step), [dateDiff()](https://tinkerpop.apache.org/docs/current/reference/#datediff-step), [disjunct()](https://tinkerpop.apache.org/docs/current/reference/#disjunct-step), [drop()](https://tinkerpop.apache.org/docs/current/reference/#drop-step), [fail()](https://tinkerpop.apache.org/docs/current/reference/#fail-step), [filter()](https://tinkerpop.apache.org/docs/current/reference/#filter-step), [flatMap()](https://tinkerpop.apache.org/docs/current/reference/#flatmap-step), [id()](https://tinkerpop.apache.org/docs/current/reference/#id-step), [identity()](https://tinkerpop.apache.org/docs/current/reference/#identity-step), [index()](https://tinkerpop.apache.org/docs/current/reference/#index-step), [intersect()](https://tinkerpop.apache.org/docs/current/reference/#intersect-step), [inject()](https://tinkerpop.apache.org/docs/current/reference/#inject-step), [label()](https://tinkerpop.apache.org/docs/current/reference/#label-step), [length()](https://tinkerpop.apache.org/docs/current/reference/#length-step), [루프()](https://tinkerpop.apache.org/docs/current/reference/#loops-step), [map()](https://tinkerpop.apache.org/docs/current/reference/#map-step), [order()](https://tinkerpop.apache.org/docs/current/reference/#order-step), [order(로컬)](https://tinkerpop.apache.org/docs/current/reference/#order-step), [path()](https://tinkerpop.apache.org/docs/current/reference/#path-step), [project()](https://tinkerpop.apache.org/docs/current/reference/#project-step), [range()](https://tinkerpop.apache.org/docs/current/reference/#range-step), [repeat()](https://tinkerpop.apache.org/docs/current/reference/#repeat-step), [reverse()](https://tinkerpop.apache.org/docs/current/reference/#reverse-step), [sack()](https://tinkerpop.apache.org/docs/current/reference/#sack-step), [sample()](https://tinkerpop.apache.org/docs/current/reference/#sample-step), [select()](https://tinkerpop.apache.org/docs/current/reference/#select-step), [sideEffect()](https://tinkerpop.apache.org/docs/current/reference/#sideeffect-step), [split()](https://tinkerpop.apache.org/docs/current/reference/#split-step), [unfold()](https://tinkerpop.apache.org/docs/current/reference/#unfold-step), [union()](https://tinkerpop.apache.org/docs/current/reference/#union-step) 

## 집계 및 수집 단계:


 [aggregate(global)](https://tinkerpop.apache.org/docs/current/reference/#aggregate-step), [combine()](https://tinkerpop.apache.org/docs/current/reference/#combine-step), [count()](https://tinkerpop.apache.org/docs/current/reference/#count-step), [dedup()](https://tinkerpop.apache.org/docs/current/reference/#dedup-step), [dedup(local)](https://tinkerpop.apache.org/docs/current/reference/#dedup-step), [fold()](https://tinkerpop.apache.org/docs/current/reference/#fold-step), [group()](https://tinkerpop.apache.org/docs/current/reference/#group-step), [groupCount()](https://tinkerpop.apache.org/docs/current/reference/#groupcount-step),

## 수학 단계:


 [max()](https://tinkerpop.apache.org/docs/current/reference/#max-step), [mean()](https://tinkerpop.apache.org/docs/current/reference/#mean-step), [min()](https://tinkerpop.apache.org/docs/current/reference/#min-step), [sum()](https://tinkerpop.apache.org/docs/current/reference/#sum-step) 

## 요소 단계:


 [otherV()](https://tinkerpop.apache.org/docs/current/reference/#otherv-step), [elementMap()](https://tinkerpop.apache.org/docs/current/reference/#elementmap-step), [element()](https://tinkerpop.apache.org/docs/current/reference/#element-step), [v()](https://tinkerpop.apache.org/docs/current/reference/#graph-step), [ out(), in(), both(), outE(), inE(), bothE(), outV(), inV(), bothV(), otherV()](https://tinkerpop.apache.org/docs/current/reference/#vertex-step) 

## 속성 단계:


 [properties()](https://tinkerpop.apache.org/docs/current/reference/#properties-step), [key()](https://tinkerpop.apache.org/docs/current/reference/#key-step), [valueMap()](https://tinkerpop.apache.org/docs/current/reference/#propertymap-step), [value()](https://tinkerpop.apache.org/docs/current/reference/#value-step) 

## 필터 단계:


 [and()](https://tinkerpop.apache.org/docs/current/reference/#and-step), [coalesce()](https://tinkerpop.apache.org/docs/current/reference/#coalesce-step), [coin()](https://tinkerpop.apache.org/docs/current/reference/#coin-step), [has()](https://tinkerpop.apache.org/docs/current/reference/#has-step), [is()](https://tinkerpop.apache.org/docs/current/reference/#is-step), [local()](https://tinkerpop.apache.org/docs/current/reference/#local-step), [none()](https://tinkerpop.apache.org/docs/current/reference/#none-step), [not()](https://tinkerpop.apache.org/docs/current/reference/#not-step), [or()](https://tinkerpop.apache.org/docs/current/reference/#or-step), [where()](https://tinkerpop.apache.org/docs/current/reference/#where-step) 

## 문자열 조작 단계:


 [concat()](https://tinkerpop.apache.org/docs/current/reference/#concat-step), [lTrim()](https://tinkerpop.apache.org/docs/current/reference/#lTrim-step), [rTrim()](https://tinkerpop.apache.org/docs/current/reference/#rtrim-step), [substring()](https://tinkerpop.apache.org/docs/current/reference/#substring-step), [toLower()](https://tinkerpop.apache.org/docs/current/reference/#toLower-step), [toUpper()](https://tinkerpop.apache.org/docs/current/reference/#toUpper-step), [trim()](https://tinkerpop.apache.org/docs/current/reference/#trim-step) 

## 조건자:

+  [ 비교: eq, neq, lt, lte, gt, gte](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 
+  [포함: within, without](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 
+  [ TextP: endingWith, containing, notStartingWith, notEndingWith, notContaining](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 
+  [ P: and, or, between, outside, inside](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 

## 제한 사항


 제한으로 반복, 반복 순회 및 중복 제거 내의 레이블은 아직 DFE에서 지원되지 않습니다.

```
// With Limit inside the repeat traversal
  g.V().has('code','AGR').repeat(out().limit(5)).until(has('code','FRA'))
  
  // With Labels inside the repeat traversal
  g.V().has('code','AGR').repeat(out().as('a')).until(has('code','FRA'))
  
  // With Dedup inside the repeat traversal
  g.V().has('code','AGR').repeat(out().dedup()).until(has('code','FRA'))
```

 중첩 반복 또는 분기 단계가 있는 경로는 아직 지원되지 않습니다.

```
// Path with branching steps
  g.V().has('code','AGR').union(identity, outE().inV()).path().by('code')
  
  
  // With nested repeat
  g.V().has('code','AGR').repeat(out().union(identity(), out())).path().by('code')
```

## 쿼리 계획 인터리빙
인터리빙

변환 프로세스에서 해당하는 네이티브 DFE 연산자가 없는 Gremlin 단계를 발견하면 Tinkerpop을 사용하도록 폴백하기 전에 DFE 엔진에서 기본적으로 실행할 수 있는 다른 중간 쿼리 부분을 찾으려고 합니다. 이는 최상위 순회에 인터리빙 로직을 적용하여 수행합니다. 결과적으로 지원되는 단계는 가능한 모든 곳에서 사용됩니다.

이러한 모든 중간 비접두사 쿼리 변환은 `explain` 및 `profile` 출력에서 `NeptuneInterleavingStep`을 사용하여 표현됩니다.

성능을 비교할 목적이라면 DFE 엔진을 사용하여 접두사 부분을 실행하면서 쿼리의 인터리빙을 설정 해제하는 것이 좋습니다. 아니면 비접두사 쿼리 실행에는 TinkerPop 엔진만 사용하세요. 이를 위해 `disableInterleaving` 쿼리 힌트를 사용할 수 있습니다.

값이 `false`인 [useDFE](gremlin-query-hints-useDFE.md) 쿼리 힌트가 DFE에서 쿼리가 아예 실행되지 않도록 하는 것처럼, 값이 `true`인 `disableInterleaving` 쿼리 힌트는 쿼리 변환에 대한 DFE 인터리빙을 설정 해제합니다. 예제:

```
g.with('Neptune#disableInterleaving', true)
 .V().has('genre','drama').in('likes')
```

## Gremlin `explain` 및 `profile` 출력 업데이트
Explain 및 Profile 차이점

Gremlin [Explain](gremlin-explain.md)은 Neptune이 쿼리를 실행하는 데 사용하는 최적화된 순회에 대한 세부 정보를 제공합니다. DFE 엔진이 활성화되었을 때 `explain` 출력이 어떻게 보이는지에 대한 예제는 [샘플 DFE `explain` 출력](gremlin-explain-api.md#gremlin-explain-dfe)을 참조하세요.

[Gremlin `profile` API](gremlin-profile-api.md)는 지정된 Gremlin 순회를 실행하고, 실행에 대한 다양한 지표를 수집하며, 자세한 최적화된 쿼리 계획 및 다양한 연산자의 런타임 통계 정보가 포함된 프로필 보고서를 생성합니다. DFE 엔진이 활성화되었을 때 `profile` 출력이 어떻게 보이는지에 대한 예제는 [샘플 DFE `profile` 출력](gremlin-profile-api.md#gremlin-profile-sample-dfe-output)을 참조하세요.

**참고**  
DFE 엔진은 랩 모드에서 출시된 실험용 기능이기 때문에 `explain` 및 `profile` 출력의 정확한 형식은 변경될 수 있습니다.

# openCypher를 사용하여 Neptune 그래프에 액세스
openCypher

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 비교: 유사점 및 차이점
Gremlin과 openCypher 비교

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 사용 시작하기
openCypher 사용

로드 방식에 관계없이 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 상태 서블릿 및 상태 엔드포인트
상태 엔드포인트

openCypher 상태 엔드포인트는 현재 서버에서 실행 중이거나 실행 대기 중인 쿼리 정보에 대한 액세스를 제공합니다. 또한 해당 쿼리를 취소할 수 있도록 지원합니다. 엔드포인트는 다음과 같습니다.

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

HTTP `GET` 및 `POST` 메서드를 사용하여 서버에서 현재 상태를 가져오거나 쿼리를 취소할 수 있습니다. `DELETE` 메서드를 사용하여 실행 중이거나 대기 중인 쿼리를 취소할 수도 있습니다.

## 상태 요청 파라미터
파라미터

**상태 쿼리 파라미터**
+ **`includeWaiting`**(`true` 또는 `false`)   –   `true`로 설정되고 다른 파라미터가 없으면 대기 중인 쿼리와 실행 중인 쿼리에 대한 상태 정보가 반환됩니다.
+ **`cancelQuery`**   –   취소 요청임을 나타내기 위해 `GET` 및 `POST` 메서드와 함께 활용하는 경우에만 사용됩니다. `DELETE` 메서드에는 이 파라미터가 필요하지 않습니다.

  `cancelQuery` 파라미터 값은 사용되지 않지만, `cancelQuery`가 있는 경우 취소할 쿼리를 식별하는 데 `queryId` 파라미터가 필요합니다.
+ **`queryId`**   –   특정 쿼리의 ID를 포함합니다.

  `GET` 또는 `POST` 메서드와 함께 사용하고 `cancelQuery` 파라미터가 없으면 `queryId`는 식별한 특정 쿼리에 대한 상태 정보를 반환합니다. `cancelQuery` 파라미터가 있는 경우 `queryId`에서 식별하는 특정 쿼리가 취소됩니다.

  `DELETE` 메서드와 함께 사용할 경우 `queryId`는 항상 특정 쿼리를 취소해야 함을 나타냅니다.
+ **`silent`**   –   쿼리를 취소할 때만 사용됩니다. `true`로 설정하면 취소가 자동으로 수행됩니다.

## 상태 요청 응답 필드
응답 필드

**상태 응답 필드(특정 쿼리의 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`는 취소되지 않았음을 나타냅니다.

## 상태 요청 및 응답의 예
샘플 요청 및 응답
+ **대기 중인 쿼리를 포함한 모든 쿼리의 상태 요청:**

  ```
  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 엔드포인트
HTTPS 엔드포인트

**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
쿼리 읽기 및 쓰기

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 결과 형식
OpenCypher 결과 형식

다음 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 후행 헤더
선택적 HTTP 후행 헤더

 이 기능은 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 쿼리 실행
AWS SDK

 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 예제
예제

다음 예제에서는 `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 쿼리 생성
Bolt 프로토콜 사용

[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에 연결
Bolt Java 예제

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 쿼리 예제
Python Bolt 예제

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 쿼리 예제
.NET Bolt 예제

.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 쿼리 예제
Java IAM 인증 Bolt 예제

아래 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 쿼리 예제
Python IAM 인증 Bolt 예제

아래의 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 예제
IAM 인증을 사용하는 Node.js

아래 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 쿼리 예제
.NET IAM 인증 Bolt 예제

.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 쿼리 예제
Go IAM 인증 Bolt 예제

아래의 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 연결 동작
Bolt 연결 동작

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 파라미터화 쿼리의 예제
파라미터화된 예제

Neptune은 파라미터화된 openCypher 쿼리를 지원합니다. 이렇게 하면 인수가 다른 동일한 쿼리 구조를 여러 번 사용할 수 있습니다. 쿼리 구조는 변경되지 않으므로, Neptune은 추상 구문 트리(AST)를 여러 번 구문 분석하지 않고도 캐싱할 수 있습니다.

## HTTPS 엔드포인트를 사용하는 openCypher 파라미터화 쿼리의 예제
HTTPS 엔드포인트 예제

다음은 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 파라미터화 쿼리의 예제
Bolt 예제

다음은 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 데이터 모델
데이터 모델

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` 기능
openCypher `explain`

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`에 대한 제한
제한 사항

openCypher Explain의 현재 릴리스에는 다음과 같은 제한 사항이 있습니다.
+ Explain 계획은 현재 읽기 전용 작업을 수행하는 쿼리에만 사용할 수 있습니다. `CREATE`, `DELETE`, `MERGE`, `SET` 등과 같은 모든 종류의 변형을 수행하는 쿼리는 지원되지 않습니다.
+ 특정 계획의 연산자 및 출력은 향후 릴리스에서 변경될 수 있습니다.

## openCypher `explain` 출력의 DFE 연산자
DFE 연산자

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` 출력의 열
열 헤더

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 출력의 기본 예제
기본 예제

다음은 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` 출력 예제
예제: 관계 조회

이 쿼리는 `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` 출력 예제
예제: 값 함수

함수는 다음과 같습니다.

```
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` 출력 예제
예제: 수학 표현식

이 예제에서 `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` 출력 예제
예제: 가변 길이 경로

다음은 가변 길이 경로 쿼리를 처리하기 위한 보다 복잡한 쿼리 계획의 예제입니다. 이 예제에서는 명확성을 위해 `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에서의 트랜잭션
트랜잭션

Amazon Neptune의 openCypher 구현은 [Neptune에서 정의한 트랜잭션 시맨틱](transactions-neptune.md)을 사용하지만, Bolt 드라이버에서 제공하는 격리 수준은 아래 섹션에 설명된 것처럼 Bolt 트랜잭션 시맨틱에 몇 가지 특정한 영향을 미칩니다.

## 읽기 전용 Bolt 트랜잭션 쿼리
읽기 전용 트랜잭션

다음과 같이 여러 트랜잭션 모델 및 격리 수준을 사용하여 읽기 전용 쿼리를 처리할 수 있는 다양한 방법이 있습니다.

### 암시적 읽기 전용 트랜잭션 쿼리
암시적 읽기 전용 트랜잭션

다음은 읽기 전용 암시적 트랜잭션의 예제입니다.

```
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)로 평가합니다.

오류가 발생할 경우 기본적으로 읽기 암시적 트랜잭션이 재시도됩니다.

### 읽기 전용 트랜잭션 쿼리 자동 커밋
읽기 전용 트랜잭션 자동 커밋

다음은 읽기 전용 자동 커밋 트랜잭션의 예제입니다.

```
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`로 액세스 모드를 명시적으로 설정하는 세션 구성을 전달하는 것이 중요합니다.

실패하는 경우 읽기 전용 자동 커밋 쿼리는 다시 시도되지 않습니다.

### 명시적 읽기 전용 트랜잭션 쿼리
명시적 읽기 전용 트랜잭션

다음은 명시적 읽기 전용 트랜잭션의 예제입니다.

```
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 트랜잭션 쿼리
변형 트랜잭션

읽기 전용 쿼리와 마찬가지로, 다음과 같이 여러 트랜잭션 모델 및 격리 수준을 사용하여 다양한 방법으로 변형 쿼리를 처리할 수 있습니다.

### 암시적 변형 트랜잭션 쿼리
암시적 변형 트랜잭션

다음은 암시적 변형 트랜잭션 예제입니다.

```
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)을 참조하세요.

### 자동 커밋 변형 트랜잭션 쿼리
자동 커밋 변형 트랜잭션

변형 자동 커밋 쿼리는 변형 암시적 트랜잭션과 동일한 동작을 상속합니다.

세션 구성을 전달하지 않으면 트랜잭션은 기본적으로 쓰기 트랜잭션으로 처리됩니다.

실패 시 변형 자동 커밋 쿼리는 자동으로 재시도되지 않습니다.

### 명시적 변형 트랜잭션 쿼리
명시적 변형 트랜잭션

다음은 명시적 변형 트랜잭션의 예제입니다.

```
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 쿼리 힌트
쿼리 힌트

**중요**  
 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 쿼리 계획 캐시 힌트
쿼리 계획 캐시

 쿼리 수준 쿼리 힌트 `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 힌트


 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 쿼리 제한 시간 힌트
쿼리 제한 시간

 쿼리 제한 시간 동작은 쿼리 수준 쿼리 힌트를 사용하여 쿼리별로 구성할 수 있습니다`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 제한
제한 사항

Amazon Neptune 릴리스의 openCypher에서는 [openCypher 사양 규정 준수](feature-opencypher-compliance.md)에서 자세히 설명하는 것처럼 여전히 [Query 쿼리 언어 참조, 버전 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf)에 나와 있는 모든 내용을 지원하지는 않습니다. 향후 릴리스에서는 이러한 제한 사항 중 상당수가 해결될 것으로 예상됩니다.

# Neptune openCypher 예외
예외

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 확장
확장 프로그램

 Amazon Neptune은 openCypher 사양 참조 버전 9를 지원합니다. 자세한 내용은 Amazon Neptune의 [Amazon Neptune에 적용되는 openCypher 사양 규정 준수](feature-opencypher-compliance.md) 섹션을 참조하세요. 또한 Amazon Neptune은 여기에 나열된 기능을 지원합니다. 특정 버전이 언급되지 않는 한이 기능은 Neptune 데이터베이스 및 Neptune Analytics에서 사용할 수 있습니다.

## 쿼리 시간 S3 데이터 액세스


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()` 함수
`join()` 함수

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

Neptune은 openCypher 사양에 없는 `join()` 함수를 구현합니다. 문자열 리터럴 목록과 문자열 구분 기호를 사용하여 문자열 리터럴을 생성합니다. 2가지 인수를 사용합니다.
+ 첫 번째 인수는 문자열 리터럴 목록입니다.
+ 두 번째 인수는 0자, 1자 또는 2자 이상의 문자로 구성될 수 있는 구분 기호 문자열입니다.

예제:

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

## Neptune 전용 `removeKeyFromMap()` 함수
`removeKeyFromMap()` 함수

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 값
사용자 지정 ID 값

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`를 속성으로 사용 중인 경우 취해야 할 조치
기존 `~id` 속성 수정

[엔진 릴리스 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 하위 쿼리 지원


 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 하위 쿼리 내의 변수 범위


 CALL 하위 쿼리 내에서 사용되는 `CALL` 하위 쿼리 앞의 절에서 온 변수는 초기 `WITH` 절에 의해 가져와야 합니다. 일반적인 `WITH` 절과 달리, 이 절은 변수 목록만 포함할 수 있으며 별칭을 허용하지 않으며 `DISTINCT`, `ORDER BY`, `WHERE`, `SKIP`, `LIMIT`와 함께 사용할 수 없습니다.

### CALL 하위 쿼리에서 반환된 변수


 `CALL` 하위 쿼리에서 내보내는 변수는 최종 `RETURN` 절로 지정됩니다. 내보낸 변수는 `CALL` 하위 쿼리 이전의 변수와 겹칠 수 없습니다.

### 제한 사항


 현재 `CALL` 하위 쿼리 내의 업데이트는 지원되지 않습니다.

## Neptune openCypher 함수


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

## 정렬 함수


 다음 섹션에서는 컬렉션을 정렬하는 함수를 정의합니다. 이러한 함수는 정렬 키 및/또는 정렬 방향을 정의하는 `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"
          }
        }]
    }]
}
```

## 시간 함수


 임시 함수는 Neptune 버전 [1.4.5.0](https://docs.aws.amazon.com/releases/release-1.4.5.0.xml) 이상에서 사용할 수 있습니다.

### day


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

 `datetime` 또는 `date` 값에서 `day`를 반환합니다. `datetime`의 경우: 값은 날짜를 추출하기 전에 입력을 기반으로 UTC로 정규화됩니다. `date`의 경우: 요일은 시간대에 따라 추출됩니다.

 `datetime` 입력은 Neptune 데이터베이스와 Neptune Analytics 모두에서 사용할 수 있습니다.

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

 여기서 `datetime`은 UTC로 정규화되므로 \$108:00은 6월 2일로 다시 전환됩니다.

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

 `date` 입력은 Neptune Analytics에서만 사용할 수 있습니다.

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

 `date`는 시간대를 보존하며 6월 3일을 유지합니다.

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

### 개월


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

### 년


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


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

#### reduce()


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


 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() 모범 사례


Neptune S3 읽기 작업은 메모리 집약적일 수 있습니다. [Amazon Neptune의 인스턴스 유형 선택에 설명된 대로 프로덕션 워크로드에 적합한 인스턴스 유형을](instance-types.md) 사용하세요.

`neptune.read()` 요청의 메모리 사용량 및 성능은 파일 크기, 열 수, 행 수, 파일 형식과 같은 다양한 요인의 영향을 받습니다. 구조에 따라 작은 파일(예: CSV 파일 100MB 이하, Parquet 파일 20MB 이하)은 대부분의 프로덕션에 적합한 인스턴스 유형에서 안정적으로 작동할 수 있는 반면, 큰 파일은 작은 인스턴스 유형이 제공할 수 없는 상당한 메모리가 필요할 수 있습니다.

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

# parquet를 사용한 쿼리 예제


다음 예제 쿼리는 지정된 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 열 유형


**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 출력


다음과 같은 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를 사용한 쿼리 예제


이 예제에서 쿼리는 지정된 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로 쿼리를 시작하는 것이 좋습니다.

## 속성 열 헤더


다음 구문을 사용하여 속성 열(`:`)을 지정할 수 있습니다. 유형 이름은 대/소문자를 구분하지 않습니다. 콜론이 속성 이름 내에 나타나는 경우 백슬래시를 사용하여 콜론 앞에 이스케이프 처리해야 합니다`\:`.

```
propertyname:type
```

**참고**  
열 헤더에는 공백, 쉼표, 캐리지 리턴 및 줄 바꿈 문자가 허용되지 않으므로 속성 이름에 이러한 문자를 포함할 수 없습니다.
유형에 `[]`를 추가하여 어레이 유형의 열을 지정할 수 있습니다.  

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

  ```
  age:Int
  ```

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

## 지원되는 CSV 열 유형

+ **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 출력


다음 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()에 대한 권한 관리
neptune.read() 권한

## 필수 IAM 정책


를 사용하는 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"
        ]
      }
  }
}
```

## 중요 사전 조건


이러한 권한 및 사전 조건은 적절한 액세스 제어 및 데이터 보호 조치를 유지하면서 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).

# 공간 데이터
공간 데이터

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 쿼리 언어로 사용할 수 있습니다.

## 좌표계


Neptune에는 전체 데이터베이스에 대해 하나의 공간 참조 식별자(SRID)가 있습니다. 좌표계의 동질성은 쿼리 시 사용자 오류를 줄이고 데이터베이스 성능을 개선합니다. 첫 번째 릴리스(1.4.7.0)는 SRID 0이라고도 하는 데카르트 좌표계를 지원합니다.

SRID 0의 Neptune 구현은 경도 및 위도 값과 호환됩니다. `ST_DistanceSpheroid`를 사용하여 WGS84/SRID 4326을 기반으로 거리를 계산합니다.

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

## 위치 데이터 저장


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)

## 공간 데이터를 대량으로 로드


데이터를 대량 로드할 때 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) 참조하세요.

## 공간 데이터 쿼리


다음 쿼리 예제에서는 [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 드라이버 사용


대부분의 쿼리 메서드는 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);
}
```

# 공간 함수
공간 함수

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


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


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


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


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


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


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


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


입력 지오메트리의 경우 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


두 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


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


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

# SPARQL을 사용하여 Neptune 그래프에 액세스
SPARQL

SPARQL은 웹용으로 설계된 그래프 데이터 형식인 리소스 기술 프레임워크(RDF)의 쿼리 언어입니다. Amazon Neptune은 SPARQL 1.1과 호환됩니다. 따라서 Neptune DB 인스턴스에 연결하고 [SPARQL 1.1 쿼리 언어](https://www.w3.org/TR/sparql11-query/) 사양에서 설명하는 쿼리 언어를 사용하여 그래프를 쿼리할 수 있습니다.

 SPARQL의 쿼리는 반환하는 변수를 지정하는 `SELECT` 절과 그래프에서 일치시킬 데이터를 지정하는 `WHERE` 절로 구성됩니다. SPARQL 쿼리에 익숙하지 않은 경우 [SPARQL 1.1 쿼리 언어](https://www.w3.org/TR/sparql11-query/)의 [단순 쿼리 작성](https://www.w3.org/TR/sparql11-query/#WritingSimpleQueries)을 참조하세요.

**중요**  
데이터를 로드할 때 `SPARQL UPDATE INSERT`로 소량 데이터 세트 작업이 가능하지만 파일에서 상당량의 데이터를 로드해야 할 경우에는 [Amazon Neptune 대량 로더를 사용하여 데이터 수집](bulk-load.md)를 참조하십시오.

Neptune의 SPARQL 구현 세부 사항에 대한 자세한 내용은 [SPARQL 표준 규정 준수](feature-sparql-compliance.md) 섹션을 참조하세요.

시작하기 전에 다음을 완비해야 합니다.
+ Neptune DB 인스턴스. Neptune DB 인스턴스 생성에 대한 자세한 내용은 [Amazon Neptune 클러스터 생성](get-started-create-cluster.md) 섹션을 참조하세요.
+ 사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스입니다.

**Topics**
+ [

# RDF4J 콘솔을 사용하여 Neptune DB 인스턴스에 연결
](access-graph-sparql-rdf4j-console.md)
+ [

# RDF4J 워크벤치를 사용하여 Neptune DB 인스턴스에 연결
](access-graph-sparql-rdf4j-workbench.md)
+ [

# Java를 사용하여 Neptune DB 인스턴스에 연결
](access-graph-sparql-java.md)
+ [

# SPARQL HTTP API
](sparql-api-reference.md)
+ [

# SPARQL 쿼리 힌트
](sparql-query-hints.md)
+ [

# 기본 그래프와 관련된 SPARQL DESCRIBE 동작
](sparql-default-describe.md)
+ [

# SPARQL 쿼리 상태 API
](sparql-api-status.md)
+ [

# SPARQL 쿼리 취소
](sparql-api-status-cancel.md)
+ [

# Amazon Neptune에서 SPARQL 1.1 그래프 스토어 HTTP 프로토콜(GSP) 사용
](sparql-graph-store-protocol.md)
+ [

# SPARQL `explain`을 사용하여 Neptune 쿼리 실행 분석
](sparql-explain.md)
+ [

# `SERVICE` 확장을 사용하는 Neptune의 SPARQL 페더레이션된 쿼리
](sparql-service.md)

# RDF4J 콘솔을 사용하여 Neptune DB 인스턴스에 연결
RDF4J 콘솔



RDF4J 콘솔을 사용하여 REPL(read-eval-print loop) 환경에서 리소스 기술 프레임워크(RDF) 그래프 및 쿼리를 실험할 수 있습니다.

원격 그래프 데이터베이스를 리포지토리로 추가하고 RDF4J 콘솔에서 쿼리할 수 있습니다. 이 섹션에서는 RDF4J 콘솔을 구성하여 원격으로 Neptune DB 인스턴스에 연결하는 방법을 살펴봅니다.

**RDF4J 콘솔을 사용하여 Neptune에 연결하려면**

1. RDF4J 웹사이트의 [다운로드 페이지](http://rdf4j.org/download/)에서 RDF4J SDK를 다운로드합니다.

1. RDF4J SDK zip 파일의 압축을 풉니다.

1. 터미널에서 RDF4J SDK 디렉터리로 이동한 후 다음 명령을 입력하여 RDF4J 콘솔을 실행합니다.

   ```
   bin/console.sh
   ```

   다음과 유사한 출력 화면이 표시되어야 합니다.

   ```
   14:11:51.126 [main] DEBUG o.e.r.c.platform.PlatformFactory - os.name = linux
   14:11:51.130 [main] DEBUG o.e.r.c.platform.PlatformFactory - Detected Posix platform
   Connected to default data directory
   RDF4J Console 3.6.1
   
   3.6.1
   Type 'help' for help.
   >
   ```

   이제 `>` 프롬프트가 표시됩니다. 이것은 RDF4J 콘솔의 일반 프롬프트입니다. 이 프롬프트를 사용하여 리포지토리 및 기타 작업을 설정합니다. 리포지토리에는 쿼리를 실행하는 자체 프롬프트가 있습니다.

1. `>` 프롬프트에서 다음을 입력하여 Neptune DB 인스턴스용 SPARQL 리포지토리를 생성합니다.

    

   ```
   create sparql
   ```

1. RDF4J 콘솔에는 SPARQL 엔드포인트에 연결할 때 필요한 변수 값을 묻는 메시지가 표시됩니다.

   ```
   Please specify values for the following variables:
   ```

   다음 값을 지정하세요.    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/access-graph-sparql-rdf4j-console.html)

   사용자의 Neptune DB 인스턴스 주소를 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   작업에 성공하면 다음 메시지가 표시됩니다.

    

   ```
   Repository created
   ```

1. `>` 프롬프트에 다음을 입력하여 Neptune DB 인스턴스에 연결합니다.

   ```
   open neptune
   ```

   작업에 성공하면 다음 메시지가 표시됩니다.

    

   ```
   Opened repository 'neptune'
   ```

   이제 `neptune>` 프롬프트가 표시됩니다. 이 프롬프트에서 Neptune 그래프를 기준으로 쿼리를 실행할 수 있습니다.

    
**참고**  
리포지토리가 추가되었으므로, 다음에 `bin/console.sh`를 실행할 때 `open neptune` 명령을 즉시 실행하여 Neptune DB 인스턴스에 연결할 수 있습니다.

1. `neptune>` 프롬프트에 다음을 입력하고 10개 제한이 있는 `?s ?p ?o` 쿼리를 사용하여 그래프에서 최대 10개의 트리플(subject-predicate-object)을 반환하는 SPARQL 쿼리를 실행합니다. 다른 것을 쿼리하려면 `sparql` 명령 뒤의 텍스트를 다른 SPARQL 쿼리로 바꿉니다.

   ```
   sparql select ?s ?p ?o where {?s ?p ?o} limit 10
   ```

# RDF4J 워크벤치를 사용하여 Neptune DB 인스턴스에 연결
RDF4J 워크벤치

이 섹션에서는 RDF4J 워크벤치와 RDF4J 서버를 사용하여 Amazon Neptune DB 인스턴스에 연결하는 방법을 설명합니다. RDF4J 서버가 필요한 이유는 Neptune SPARQL HTTP REST 엔드포인트와 RDF4J 워크벤치 사이에서 프록시 역할을 하기 때문입니다.

RDF4J 워크벤치는 로컬 파일 로드를 포함해 그래프를 간단하게 실험할 수 있는 인터페이스를 제공합니다. 자세한 내용은 RDF4J 설명서의 [섹션 추가](https://rdf4j.org/documentation/tools/server-workbench/#add)를 참조하십시오.

**사전 조건**  
시작하기 전에 다음을 수행하십시오.
+ Java 1.8 이상을 설치합니다.
+ RDF4J 서버와 RDF4J 워크벤치를 설치합니다. 자세한 내용은 [RDF4J 서버 및 RDF4J 워크벤치 설치](https://rdf4j.org/documentation/tools/server-workbench/#installing-rdf4j-server-and-rdf4j-workbench)를 참조하십시오.

**RDF4J 워크벤치를 사용하여 Neptune에 연결하려면**

1. 웹 브라우저에서 RDF4J 워크벤치 웹 앱이 배포되는 URL로 이동합니다. 예를 들어, Apache Tomcat을 사용한다면 URL은 [https://*ec2\$1hostname*:8080/rdf4j-workbench/](http://localhost:8080/rdf4j-workbench/)입니다.

1. **Connect to RDF4J Server(RDF4J 서버에 연결)**가 나타나면 **RDF4J Server**가 설치되고 실행되어 서버 URL이 정확한지 확인하십시오. 그 다음에 다음 단계를 진행하십시오.

1. 왼쪽 창에서 **New repository(새 리포지토리)**를 선택합니다.

   **New repository(새 리포지토리)**에서
   + **Type(유형)** 드롭다운 목록에서 **SPARQL endpoint proxy(SPARQL 엔드포인트 프록시)**를 선택합니다.
   + **ID**에는 **neptune**을 입력합니다.
   + **제목**에는 **Neptune DB 인스턴스**를 입력합니다.

   **다음**을 선택합니다.

1. **New repository(새 리포지토리)**에서
   + **SPARQL query endpoint URL(SPARQL 쿼리 엔드포인트 URL)**에는 `https://your-neptune-endpoint:port/sparql`을 입력합니다.
   + **SPARQL update endpoint URL(SPARQL 업데이트 엔드포인트 URL)**에는 `https://your-neptune-endpoint:port/sparql`을 입력합니다.

   사용자의 Neptune DB 인스턴스 주소를 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   **생성(Create)**을 선택합니다.

1. 그러면 **neptune** 리포지토리가 리포지토리 목록에 표시됩니다. 새 리포지토리를 사용하려면 몇 분 걸릴 수 있습니다.

1. 테이블의 **Id** 열에서 **neptune** 링크를 선택합니다.

1. 왼쪽 창에서 **Query(쿼리)**를 선택합니다.

    
**참고**  
**Explore(탐색)**의 메뉴 항목이 비활성화되어 있으면 RDF4J 서버에 다시 연결하고 **neptune** 리포지토리를 다시 선택해야 합니다.  
오른쪽 상단 모서리에서 **[change]** 링크를 사용하면 됩니다.

1. 쿼리 필드에서 다음 SPARQL 쿼리를 입력한 후 **Execute(실행)**을 선택합니다.

    

   ```
   select ?s ?p ?o where {?s ?p ?o} limit 10
   ```

    

앞의 예제에서는 10개 제한이 있는 `?s ?p ?o` 쿼리를 사용하여 그래프에서 최대 10개의 트리플(subject-predicate-object)을 반환했습니다.

# Java를 사용하여 Neptune DB 인스턴스에 연결
Java

이 섹션에서는 Amazon Neptune DB 인스턴스에 연결하고 SPARQL 쿼리를 수행하는 완전한 Java 샘플을 실행하는 절차를 안내합니다.

사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

**Java를 사용하여 Neptune에 연결하려면**

1. EC2 인스턴스에 Apache Maven을 설치합니다. Amazon Linux 2023(기본 설정)을 사용하는 경우 다음을 사용합니다.

   ```
   sudo dnf update -y
   sudo dnf install maven -y
   ```

   Amazon Linux 2를 사용하는 경우 [https://maven.apache.org/download.cgi](https://maven.apache.org/download.cgi:)에서 최신 바이너리를 다운로드합니다.

   ```
   sudo yum remove maven -y
   wget https://dlcdn.apache.org/maven/maven-3/ <version>/binaries/apache-maven-<version>-bin.tar.gz
   sudo tar -xzf apache-maven-<version>-bin.tar.gz -C /opt/
   sudo ln -sf /opt/apache-maven-<version> /opt/maven
   echo 'export MAVEN_HOME=/opt/maven' >> ~/.bashrc
   echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> ~/.bashrc
   source ~/.bashrc
   ```

1. 이 예제는 Java 8에 대해서만 테스트되었습니다. 다음을 입력하여 사용자의 EC2 인스턴스에 Java 8을 설치합니다.

   ```
   sudo yum install java-1.8.0-devel
   ```

1. 다음을 입력하여 사용자의 EC2 인스턴스에 Java 8을 기본 실행 시간으로 설정합니다.

   ```
   sudo /usr/sbin/alternatives --config java
   ```

   메시지가 표시되면 Java 8에 대한 숫자를 입력합니다.

1. 다음을 입력하여 사용자의 EC2 인스턴스에 Java 8을 기본 컴파일러로 설정합니다.

   ```
   sudo /usr/sbin/alternatives --config javac
   ```

   메시지가 표시되면 Java 8에 대한 숫자를 입력합니다.

1. 새 디렉터리에서 `pom.xml` 파일을 생성하고 텍스트 편집기에서 엽니다.

1. 다음을 `pom.xml` 파일에 복사하여 저장합니다. 일반적으로 버전 번호를 안정적인 최신 버전으로 조정할 수 있습니다.

   ```
   <project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.amazonaws</groupId>
     <artifactId>RDFExample</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>RDFExample</name>
     <url>https://maven.apache.org</url>
     <dependencies>
       <dependency>
         <groupId>org.eclipse.rdf4j</groupId>
         <artifactId>rdf4j-runtime</artifactId>
         <version>3.6</version>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
             <groupId>org.codehaus.mojo</groupId>
             <artifactId>exec-maven-plugin</artifactId>
             <version>1.2.1</version>
             <configuration>
               <mainClass>com.amazonaws.App</mainClass>
             </configuration>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <configuration>
             <source>1.8</source>
             <target>1.8</target>
           </configuration>
         </plugin>
       </plugins>
     </build>
   </project>
   ```
**참고**  
기존 Maven 프로젝트를 수정할 경우 필요한 종속성이 이전 코드에서 강조 표시됩니다.

1. 예제 소스 코드(`src/main/java/com/amazonaws/`)의 하위 디렉터리를 생성하려면 명령줄에 다음을 입력합니다.

   ```
   mkdir -p src/main/java/com/amazonaws/
   ```

1. `src/main/java/com/amazonaws/` 디렉터리에서 `App.java` 파일을 생성한 다음 텍스트 편집기에서 엽니다.

1. 다음을 `App.java` 파일로 복사합니다. *your-neptune-endpoint*를 Neptune DB 인스턴스의 주소로 바꿉니다.
**참고**  
사용자의 Neptune DB 인스턴스 호스트 이름을 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

   ```
   package com.amazonaws;
   
   import org.eclipse.rdf4j.repository.Repository;
   import org.eclipse.rdf4j.repository.http.HTTPRepository;
   import org.eclipse.rdf4j.repository.sparql.SPARQLRepository;
   
   import java.util.List;
   import org.eclipse.rdf4j.RDF4JException;
   import org.eclipse.rdf4j.repository.RepositoryConnection;
   import org.eclipse.rdf4j.query.TupleQuery;
   import org.eclipse.rdf4j.query.TupleQueryResult;
   import org.eclipse.rdf4j.query.BindingSet;
   import org.eclipse.rdf4j.query.QueryLanguage;
   import org.eclipse.rdf4j.model.Value;
   
   public class App
   {
       public static void main( String[] args )
       {
           String sparqlEndpoint = "https://your-neptune-endpoint:port/sparql";
           Repository repo = new SPARQLRepository(sparqlEndpoint);
           repo.initialize();
   
           try (RepositoryConnection conn = repo.getConnection()) {
              String queryString = "SELECT ?s ?p ?o WHERE { ?s ?p ?o } limit 10";
   
              TupleQuery tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, queryString);
   
              try (TupleQueryResult result = tupleQuery.evaluate()) {
                 while (result.hasNext()) {  // iterate over the result
                      BindingSet bindingSet = result.next();
   
                      Value s = bindingSet.getValue("s");
                      Value p = bindingSet.getValue("p");
                      Value o = bindingSet.getValue("o");
   
                      System.out.print(s);
                      System.out.print("\t");
                      System.out.print(p);
                      System.out.print("\t");
                      System.out.println(o);
                 }
              }
           }
       }
   }
   ```

1. 다음 Maven 명령을 사용하여 샘플을 컴파일하고 실행합니다.

   ```
   mvn compile exec:java
   ```

앞의 예제에서는 10개 제한이 있는 `?s ?p ?o` 쿼리를 사용하여 그래프에서 최대 10개의 트리플(subject-predicate-object)을 반환했습니다. 다른 것을 쿼리하려면 해당 쿼리를 다른 SPARQL 쿼리로 바꿉니다.

예제에서 반복 결과가 반환된 각 변수의 값을 인쇄합니다. `Value` 객체가 `String`으로 변환된 후 인쇄됩니다. 쿼리의 `SELECT` 부분을 변경할 경우에는 코드를 수정해야 합니다.

# SPARQL HTTP API
HTTP API

SPARQL HTTP 요청은 다음 엔드포인트에서 수락됩니다. `https://your-neptune-endpoint:port/sparql` 

SPARQL을 사용한 Amazon Neptune 연결에 대한 자세한 내용은 [SPARQL을 사용하여 Neptune 그래프에 액세스](access-graph-sparql.md) 섹션을 참조하세요.

SPARQL 프로토콜 및 쿼리 언어에 대한 자세한 내용은 [SPARQL 1.1 Protocol](https://www.w3.org/TR/sparql11-protocol/#protocol) 및 [SPARQL 1.1 Query Language](https://www.w3.org/TR/sparql11-query/) 사양을 참조하십시오.

다음 주제에서는 SPARQL RDF 직렬화 형식 및 SPARQL HTTP API를 Neptune에서 사용하는 방법에 대한 정보를 제공합니다.

**Contents**
+ [

# HTTP REST 엔드포인트를 사용하여 Neptune DB 인스턴스에 연결
](access-graph-sparql-http-rest.md)
+ [

# 멀티파트 SPARQL 응답을 위한 선택적 HTTP 후행 헤더
](access-graph-sparql-http-trailing-headers.md)
+ [

# Neptune에서 SPARQL이 사용하는 RDF 미디어 유형
](sparql-media-type-support.md)
  + [

## Neptune SPARQL이 사용하는 RDF 직렬화 형식
](sparql-media-type-support.md#sparql-serialization-formats)
  + [

## Neptune SPARQL에서 사용되는 SPARQL 결과 직렬화 형식
](sparql-media-type-support.md#sparql-serialization-formats-neptune-output)
  + [

## Neptune에서 RDF 데이터를 가져올 때 사용할 수 있는 미디어 유형
](sparql-media-type-support.md#sparql-serialization-formats-input)
  + [

## Neptune에서 쿼리 결과를 내보내는 데 사용할 수 있는 미디어 유형
](sparql-media-type-support.md#sparql-serialization-formats-output)
+ [

# SPARQL UPDATE LOAD를 사용하여 데이터를 Neptune으로 가져오기
](sparql-api-reference-update-load.md)
+ [

# SPARQL UPDATE UNLOAD를 사용하여 Neptune에서 데이터 삭제
](sparql-api-reference-unload.md)

# HTTP REST 엔드포인트를 사용하여 Neptune DB 인스턴스에 연결
HTTP REST

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

다음은 **curl** 명령을 사용하여 SPARQL 엔드포인트에 연결하고, HTTPS를 통해 연결하고, HTTP 구문을 사용하는 방법입니다. 사용자의 Neptune DB 인스턴스와 동일한 Virtual Private Cloud(VPC)에 있는 Amazon EC2 인스턴스에서 이러한 지침을 따라야 합니다.

Neptune DB 인스턴스의 SPARQL 쿼리용 HTTP 엔드포인트는 `https://your-neptune-endpoint:port/sparql`입니다.

**참고**  
사용자의 Neptune DB 인스턴스 호스트 이름을 찾는 방법은 [Amazon Neptune 엔드포인트에 연결](feature-overview-endpoints.md) 섹션을 참조하세요.

Amazon Neptune에서는 SPARQL 쿼리용 HTTP 엔드포인트를 제공합니다. REST 인터페이스는 SPARQL 버전 1.1과 호환됩니다.

**HTTP POST를 사용하는 쿼리**  
다음 예제에서는 **curl**을 사용하여 HTTP **POST**를 통해 SPARQL **`QUERY`**를 제출합니다.

```
curl -X POST --data-binary 'query=select ?s ?p ?o where {?s ?p ?o} limit 10' https://your-neptune-endpoint:port/sparql
```

앞의 예제에서는 10개 제한이 있는 `?s ?p ?o` 쿼리를 사용하여 그래프에서 최대 10개의 트리플(subject-predicate-object)을 반환했습니다. 다른 것을 쿼리하려면 다른 SPARQL 쿼리로 바꿉니다.

**참고**  
`SELECT` 및 `ASK` 쿼리에 대한 응답의 기본 MIME 미디어 유형은 `application/sparql-results+json`입니다.  
응답의 기본 MIME 유형이 `CONSTRUCT` 및 `DESCRIBE` 쿼리는 `application/n-quads`입니다.  
Neptune에서 직렬화에 대해 사용하는 미디어 유형 목록은 [Neptune SPARQL이 사용하는 RDF 직렬화 형식](sparql-media-type-support.md#sparql-serialization-formats) 섹션을 참조하세요.

**HTTP POST를 사용하는 업데이트**  
다음 예제에서는 **curl**을 사용하여 HTTP **POST**를 통해 SPARQL **`UPDATE`**를 제출합니다.

```
curl -X POST --data-binary 'update=INSERT DATA { <https://test.com/s> <https://test.com/p> <https://test.com/o> . }' https://your-neptune-endpoint:port/sparql
```

앞 예제에서는 다음 트리플을 SPARQL 기본 그래프에 삽입했습니다(`<https://test.com/s> <https://test.com/p> <https://test.com/o>`).

# 멀티파트 SPARQL 응답을 위한 선택적 HTTP 후행 헤더
선택적 HTTP 후행 헤더

SPARQL 쿼리 및 업데이트에 대한 HTTP 응답은 종종 2개 이상의 부분 또는 청크로 반환됩니다. 쿼리 또는 업데이트가 이러한 청크를 보내기 시작한 후 발생하는 오류를 진단하기 어려울 수 있습니다. 첫 번째 청크가 `200` HTTP 상태 코드로 도착하기 때문에 더욱 그렇습니다.

후행 헤더를 명시적으로 요청하지 않으면 Neptune은 보통 손상된 메시지 본문에 오류 메시지를 추가하는 방식으로만 오류를 보고합니다.

이러한 종류의 문제를 더 쉽게 감지하고 진단할 수 있도록 요청에 전송 인코딩(TE) 후행 헤더(`te: trailers`)를 포함할 수 있습니다(예: [TE 요청 헤더에 대한 MDN 페이지](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE) 참조). 이렇게 하면 Neptune이 응답 청크의 후행 헤더에 2개의 새 헤더 필드를 포함하게 됩니다.
+ `X-Neptune-Status`   –   응답 코드와 짧은 이름이 차례로 들어 있습니다. 예를 들어, 성공하면 후행 헤더는 `X-Neptune-Status: 200 OK`와 같습니다. 장애가 발생한 경우 응답 코드는 `X-Neptune-Status: 500 TimeLimitExceededException`과 같은 [Neptune 엔진 오류 코드](errors-engine-codes.md)가 됩니다.
+ `X-Neptune-Detail`   –   요청이 성공하면 비어 있습니다. 오류가 발생한 경우 JSON 오류 메시지가 포함됩니다. HTTP 헤더 값에는 ASCII 문자만 사용할 수 있으므로, JSON 문자열은 URL로 인코딩됩니다. 오류 메시지는 계속해서 응답 메시지 본문에 추가됩니다.

# Neptune에서 SPARQL이 사용하는 RDF 미디어 유형
RDF 미디어 유형 지원

RDF(리소스 기술 프레임워크) 데이터는 여러 방식으로 직렬화할 수 있으며, 대부분 SPARQL에서 사용하거나 출력할 수 있습니다.

## Neptune SPARQL이 사용하는 RDF 직렬화 형식
RDF 직렬화 형식
+ **RDF/XML**  –   [RDF 1.1 XML Syntax](https://www.w3.org/TR/rdf-syntax-grammar/)에 정의된 RDF의 XML 직렬화입니다. 미디어 유형: `application/rdf+xml`. 일반 파일 확장명: `.rdf`
+ **N-Triples**  –   [RDF 1.1 N-Triples](https://www.w3.org/TR/n-triples/)에 정의된 RDF 그래프 인코딩을 위한 라인 기반 일반 텍스트 형식입니다. 미디어 유형: `application/n-triples`, `text/turtle` 또는 `text/plain`. 일반 파일 확장명: `.nt`
+ **N-Quads**  –   [RDF 1.1 N-Quads](https://www.w3.org/TR/n-quads/)에 정의된 RDF 그래프 인코딩을 위한 라인 기반 일반 텍스트 형식입니다. N-Triples의 확장명입니다. 미디어 유형: 7비트 US-ASCII로 인코딩될 때 `application/n-quads` 또는 `text/x-nquads`. 일반 파일 확장명: `.nq`
+ **Turtle**  –   RDF 그래프를 공통 사용 패턴 및 데이터 유형에 대한 약어를 사용하여 간단한 자연 텍스트 형식으로 완전하게 작성할 수 있는[ RDF 1.1 Turtle](https://www.w3.org/TR/turtle/)에 정의된 RDF에 대한 텍스트 구문입니다. Turtle은 N-Triples 형식은 물론 SPARQL의 트리플 패턴 구문과의 호환성 레벨을 제공합니다. 미디어 유형: `text/turtle`일반 파일 확장명: `.ttl`
+ **TriG**  –   RDF 그래프를 공통 사용 패턴 및 데이터 유형에 대한 약어를 사용하여 간단한 자연 텍스트 형식으로 완전하게 작성할 수 있는[ RDF 1.1 TriG](https://www.w3.org/TR/trig/)에 정의된 RDF에 대한 텍스트 구문입니다. TriG는 Turtle 형식의 확장명입니다. 미디어 유형: `application/trig`. 일반 파일 확장명: `.trig`
+ **N3(Notation3)**  –   [Notation3 (N3): A readable RDF syntax](https://www.w3.org/TeamSubmission/n3/)에 정의된 어설션 및 논리 언어입니다. N3은 공식(자체가 그래프인 리터럴), 변수, 논리적 암시 및 기능 조건자를 추가하여 RDF 데이터 모델을 확장하고 RDF/XML에 텍스트 구문 대체를 제공합니다. 미디어 유형: `text/n3`. 일반 파일 확장명: `.n3`
+ **JSON-LD**  –   [JSON-LD 1.0](https://www.w3.org/TR/json-ld/)에 정의된 데이터 직렬화 및 메시징 형식입니다. 미디어 유형: `application/ld+json`. 일반 파일 확장명: `.jsonld`
+ **TriX**  –   [TriX: RDF Triples in XML](https://www.hpl.hp.com/techreports/2004/HPL-2004-56.html)에 정의된 XML의 RDF 직렬화입니다. 미디어 유형: `application/trix`. 일반 파일 확장명: `.trix`
+ **SPARQL JSON Results**  –   [SPARQL 1.1 Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json)을 사용하는 RDF의 직렬화입니다. 미디어 유형: `application/sparql-results+json`. 일반 파일 확장명: `.srj`
+ **RDF4J Binary Format**  –   [RDF4J Binary RDF Format](https://rdf4j.org/documentation/reference/rdf4j-binary)에서 문서화된 RDF 데이터 인코딩을 위한 이진 형식입니다. 미디어 유형: `application/x-binary-rdf`.

## Neptune SPARQL에서 사용되는 SPARQL 결과 직렬화 형식
SPARQL 결과 형식
+ **SPARQL XML Results**  –   [SPARQL Query Results XML Format (Second Edition)](https://www.w3.org/TR/rdf-sparql-XMLres/)에 정의된 SPARQL 쿼리 언어에서 제공하는 변수 바인딩 및 부울 결과 형식에 대한 XML 형식입니다. 미디어 유형: `application/sparql-results+xml`. 일반 파일 확장명: `.srx`
+ **SPARQL CSV and TSV Results**  –   쉼표로 구분된 값과 탭으로 구분된 값을 사용하여 `SELECT` 쿼리의 SPARQL 쿼리 결과를 표현하는 것으로, [SPARQL 1.1 Query Results CSV and TSV Formats](https://www.w3.org/TR/sparql11-results-csv-tsv/)에 정의되어 있습니다. 미디어 유형: 쉼표로 구분된 값의 경우 `text/csv`, 탭으로 구분된 값의 경우 `text/tab-separated-values`. 일반 파일 확장: 쉼표로 구분된 값의 경우 `.csv`, 탭으로 구분된 값의 경우 `.tsv`
+ **Binary Results Table**  –   SPARQL 쿼리 출력 인코딩을 위한 이진 형식입니다. 미디어 유형: `application/x-binary-rdf-results-table`.
+ **SPARQL JSON Results**  –   [SPARQL 1.1 Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/)을 사용하는 RDF의 직렬화입니다. 미디어 유형: `application/sparql-results+json`.

## Neptune에서 RDF 데이터를 가져올 때 사용할 수 있는 미디어 유형
Neptune 입력 형식

**[Neptune 대량 로더](bulk-load.md)에서 지원하는 미디어 유형**
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [N-Quads](https://www.w3.org/TR/n-quads/)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [Turtle](https://www.w3.org/TR/turtle/)

**SPARQL UPDATE LOAD에서 가져올 수 있는 미디어 유형**
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [N-Quads](https://www.w3.org/TR/n-quads/)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [Turtle](https://www.w3.org/TR/turtle/)
+ [TriG](https://www.w3.org/TR/trig/)
+ [N3](https://www.w3.org/TeamSubmission/n3/)
+ [JSON-LD](https://www.w3.org/TR/json-ld/)

## Neptune에서 쿼리 결과를 내보내는 데 사용할 수 있는 미디어 유형
Neptune 출력 미디어 유형

SPARQL 쿼리 응답에 대한 출력 형식을 지정하려면 쿼리 요청과 함께 `"Accept: media-type"` 헤더를 보냅니다. 예제:

```
curl -H "Accept: application/nquads" ...
```

**SPARQL SELECT가 Neptune에서 출력할 수 있는 RDF 미디어 유형**
+ [SPARQL JSON Results](https://www.w3.org/TR/sparql11-results-json)(기본값)
+ [SPARQL XML Results](https://www.w3.org/TR/rdf-sparql-XMLres/)
+ **Binary Results Table**(미디어 유형: `application/x-binary-rdf-results-table`)
+ [쉼표로 구분된 값(CSV)](https://www.w3.org/TR/sparql11-results-csv-tsv/)
+ [탭으로 구분된 값(TSV)](https://www.w3.org/TR/sparql11-results-csv-tsv/)

**SPARQL ASK가 Neptune에서 출력할 수 있는 RDF 미디어 유형**
+ [SPARQL JSON Results](https://www.w3.org/TR/sparql11-results-json)(기본값)
+ [SPARQL XML Results](https://www.w3.org/TR/rdf-sparql-XMLres/)
+ **부울**(미디어 유형: `text/boolean`, "true" 또는 "false"를 의미함)

**SPARQL CONSTRUCT가 Neptune에서 출력할 수 있는 RDF 미디어 유형**
+ [N-Quads](https://www.w3.org/TR/n-quads/)(기본값)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [JSON-LD](https://www.w3.org/TR/json-ld/)
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [Turtle](https://www.w3.org/TR/turtle/)
+ [N3](https://www.w3.org/TeamSubmission/n3/)
+ [TriX](https://www.hpl.hp.com/techreports/2004/HPL-2004-56.html)
+ [TriG](https://www.w3.org/TR/trig/)
+ [SPARQL JSON Results](https://www.w3.org/TR/sparql11-results-json)
+ [RDF4J Binary RDF Format](https://rdf4j.org/documentation/reference/rdf4j-binary)

**SPARQL DESCRIBE가 Neptune에서 출력할 수 있는 RDF 미디어 유형**
+ [N-Quads](https://www.w3.org/TR/n-quads/)(기본값)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [JSON-LD](https://www.w3.org/TR/json-ld/)
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [Turtle](https://www.w3.org/TR/turtle/)
+ [N3](https://www.w3.org/TeamSubmission/n3/)
+ [TriX](https://www.hpl.hp.com/techreports/2004/HPL-2004-56.html)
+ [TriG](https://www.w3.org/TR/trig/)
+ [SPARQL JSON Results](https://www.w3.org/TR/sparql11-results-json)
+ [RDF4J Binary RDF Format](https://rdf4j.org/documentation/reference/rdf4j-binary)

# SPARQL UPDATE LOAD를 사용하여 데이터를 Neptune으로 가져오기
SPARQL UPDATE LOAD

SPARQL UPDATE LOAD 명령의 구문은 [SPARQL 1.1 업데이트 권장 사항](https://www.w3.org/TR/sparql11-update/#load)에 지정되어 있습니다.

```
LOAD SILENT (URL of data to be loaded) INTO GRAPH (named graph into which to load the data)
```
+ **`SILENT`**   –   (*선택 사항*) 처리 중에 오류가 발생한 경우에도 작업이 성공을 반환하도록 합니다.

  이는 단일 트랜잭션에 `"LOAD ...; LOAD ...; UNLOAD ...; LOAD ...;"`와 같이 여러 문이 포함되어 있고 일부 원격 데이터를 처리할 수 없는데 트랜잭션을 완료하려는 경우에 유용할 수 있습니다.
+ *로드할 데이터 URL*   –   (*필수*) 그래프에 로드할 데이터가 들어 있는 원격 데이터 파일을 지정합니다.

  원격 파일의 확장자는 다음 중 하나여야 합니다.
  + NTriples에 대해 `.nt`
  + NQuads에 대해 `.nq`
  + Trig에 대해 `.trig`
  + RDF/XML에 대해 `.rdf`
  + Turtle에 대해 `.ttl`
  + N3에 대해 `.n3`
  + JSON-LD에 대해 `.jsonld`
+ **`INTO GRAPH`***(데이터를 로드할 명명된 그래프)*  –   (*선택 사항*) 데이터를 로드해야 하는 그래프를 지정합니다.

  Neptune은 트리플마다 이름이 있는 그래프를 연결합니다. 다음과 같이 폴백 명명된 그래프 URI(`http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`)를 사용하여 기본 명명된 그래프를 지정할 수 있습니다.

  ```
  INTO GRAPH <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>
  ```

**참고**  
많은 데이터를 로드해야 하는 경우 UPDATE LOAD 대신 Neptune 대량 로더를 사용하는 것이 좋습니다. 대량 로더에 대한 자세한 내용은 [Amazon Neptune 대량 로더를 사용하여 데이터 수집](bulk-load.md) 섹션을 참조하세요.

`SPARQL UPDATE LOAD`를 사용하여 Amazon S3에서 직접 데이터를 로드하거나 자체 호스팅한 웹 서버에서 가져온 파일에서 데이터를 로드할 수 있습니다. 로드할 리소스는 Neptune 서버와 동일한 리전에 있어야 하며 리소스에 대한 엔드포인트는 VPC에서 허용되어야 합니다. Amazon S3 엔드포인트 생성에 관한 자세한 내용은 [Amazon S3 VPC 엔드포인트 생성](bulk-load-data.md#bulk-load-prereqs-s3) 섹션을 참조하세요.

모든 `SPARQL UPDATE LOAD` URI는 `https://`로 시작해야 합니다. Amazon S3 URL도 포함됩니다.

Neptune 대량 로더와 달리 `SPARQL UPDATE LOAD`에 대한 호출은 완전한 트랜잭션입니다.

**SPARQL UPDATE LOAD를 사용하여 Amazon S3에서 Neptune으로 직접 파일 로드**

Neptune에서는 SPARQL UPDATE LOAD를 사용할 때 Amazon S3에 IAM 역할을 전달할 수 없으므로, 문제의 Amazon S3 버킷을 공개하거나 LOAD 쿼리에서 [미리 서명된 Amazon S3 URL](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)을 사용해야 합니다.

Amazon S3 파일에 대해 미리 서명된 URL을 생성하려면 다음과 같은 AWS CLI 명령을 사용할 수 있습니다.

```
aws s3 presign --expires-in (number of seconds) s3://(bucket name)/(path to file of data to load)
```

그러면 생성된 사전 서명된 URL을 `LOAD` 명령에 사용할 수 있습니다.

```
curl https://(a Neptune endpoint URL):8182/sparql \
  --data-urlencode 'update=load (pre-signed URL of the remote Amazon S3 file of data to be loaded) \
                           into graph (named graph)'
```

자세한 내용은 [요청 인증: 쿼리 파라미터 사용](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)을 참조하십시오. [Boto3 문서](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html)는 Python 스크립트를 사용하여 미리 서명된 URL을 생성하는 방법을 보여줍니다.

또한 로드할 파일의 콘텐츠 유형을 올바르게 설정해야 합니다.

1. `-metadata` 파라미터를 사용하여 파일을 Amazon S3에 업로드할 때 해당 파일의 콘텐츠 유형을 다음과 같이 설정합니다.

   ```
   aws s3 cp test.nt s3://bucket-name/my-plain-text-input/test.nt --metadata Content-Type=text/plain
   aws s3 cp test.rdf s3://bucket-name/my-rdf-input/test.rdf --metadata Content-Type=application/rdf+xml
   ```

1. 미디어 유형 정보가 실제로 제공되는지 확인합니다. 실행합니다.

   ```
   curl -v bucket-name/folder-name
   ```

   이 명령의 출력은 파일을 업로드할 때 설정한 미디어 유형 정보를 표시해야 합니다.

1. 그러면 `SPARQL UPDATE LOAD` 명령을 사용하여 이러한 파일을 Neptune으로 가져올 수 있습니다.

   ```
   curl https://your-neptune-endpoint:port/sparql \
     -d "update=LOAD <https://s3.amazonaws.com/bucket-name/my-rdf-input/test.rdf>"
   ```

위의 단계는 퍼블릭 Amazon S3 버킷 또는 LOAD 쿼리에서 [미리 서명된 Amazon S3 URL](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)을 사용하여 액세스하는 버킷에 대해서만 작동합니다.

 아래와 같이 프라이빗 Amazon S3 버킷에서 로드하도록 웹 프록시 서버를 설정할 수도 있습니다.

**웹 서버에서 SPARQL UPDATE LOAD를 사용하여 파일을 Neptune에 로드**

1. Neptune 및 로드할 파일을 호스팅하는 VPC 내에서 실행하는 시스템에 웹 서버를 설치합니다. 예를 들어 Amazon Linux를 사용하는 경우 다음과 같이 Apache를 설치할 수 있습니다.

   ```
   sudo yum install httpd mod_ssl
   sudo /usr/sbin/apachectl start
   ```

1. 로드할 RDF 파일 콘텐츠의 MIME 유형을 정의합니다. SPARQL은 웹 서버에서 보낸 `Content-type` 헤더를 사용하여 콘텐츠의 입력 형식을 결정하므로 웹 서버에 적합한 MIME 유형을 정의해야 합니다.

   예를 들어 다음 파일 확장명을 사용하여 파일 형식을 식별한다고 가정하겠습니다.
   + NTriples에 대해 `.nt`
   + NQuads에 대해 `.nq`
   + Trig에 대해 `.trig`
   + RDF/XML에 대해 `.rdf`
   + Turtle에 대해 `.ttl`
   + N3에 대해 `.n3`
   + JSON-LD에 대해 `.jsonld`

   Apache 2를 웹 서버로 사용하는 경우 `/etc/mime.types` 파일을 편집하고 다음 유형을 추가합니다.

   ```
    text/plain nt
    application/n-quads nq
    application/trig trig
    application/rdf+xml rdf
    application/x-turtle ttl
    text/rdf+n3 n3
    application/ld+json jsonld
   ```

1. MIME 유형의 매핑이 작동하는지 확인합니다. 웹 서버가 작동하고 실행 중이며 RDF 파일을 선택한 형식으로 호스팅하면 로컬 호스트에서 웹 서버로 요청을 보내 구성을 테스트할 수 있습니다.

   예를 들어 다음과 같이 요청을 보낼 수 있습니다.

   ```
   curl -v http://localhost:80/test.rdf
   ```

   그러면 `curl`의 자세한 출력에 다음과 같은 라인이 표시되어야 합니다.

   ```
   Content-Type: application/rdf+xml
   ```

   이는 콘텐츠 유형 매핑이 성공적으로 정의되었음을 보여줍니다.

1. 이제 SPARQL UPDATE 명령을 사용하여 데이터를 로드할 준비가 되었습니다.

   ```
   curl https://your-neptune-endpoint:port/sparql \
       -d "update=LOAD <http://web_server_private_ip:80/test.rdf>"
   ```

**참고**  
`SPARQL UPDATE LOAD`를 사용하면 로드 중인 소스 파일이 클 때 웹 서버에서 시간 초과를 트리거할 수 있습니다. Neptune은 스트리밍되는 파일 데이터를 처리하고 서버에 구성된 제한 시간보다 오래 걸릴 수 있는 큰 파일을 처리합니다. 이로 인해 서버가 연결을 종료하여 Neptune의 스트림에서 예기치 않은 EOF가 발생하면 다음과 같은 오류 메시지가 나타날 수 있습니다.  

```
{
  "detailedMessage":"Invalid syntax in the specified file",
  "code":"InvalidParameterException"
}
```
이 메시지가 나타나는데 소스 파일에 잘못된 구문이 포함되어 있지 않은 경우 웹 서버의 시간 초과 설정을 늘려 보십시오. 서버에서 디버그 로그를 활성화하고 시간 초과를 찾아 문제를 진단할 수도 있습니다.

# SPARQL UPDATE UNLOAD를 사용하여 Neptune에서 데이터 삭제
SPARQL UPDATE UNLOAD

Neptune은 원격 소스에서 지정된 데이터를 제거하기 위한 사용자 지정 SPARQL 작업 `UNLOAD`도 제공합니다. `UNLOAD`는 `LOAD` 작업에 대응하는 것으로 생각하면 됩니다. 구문은 다음과 같습니다.

```
UNLOAD SILENT (URL of the remote data to be unloaded) FROM GRAPH (named graph from which to remove the data)
```
+ **`SILENT`**   –   (*선택 사항*) 데이터를 처리할 때 오류가 발생했더라도 작업이 성공을 반환하도록 합니다.

  이는 단일 트랜잭션에 `"LOAD ...; LOAD ...; UNLOAD ...; LOAD ...;"`와 같이 여러 문이 포함되어 있고 일부 원격 데이터를 처리할 수 없는데 트랜잭션을 완료하려는 경우에 유용할 수 있습니다.
+ *언로드할 원격 데이터의 URL*   –   (*필수*) 그래프에서 언로드할 데이터가 들어 있는 원격 데이터 파일을 지정합니다.

  원격 파일의 확장자는 다음 중 하나를 가져야 합니다(UPDATE-LOAD에서 지원하는 형식과 동일).
  + NTriples에 대해 `.nt`
  + NQuads에 대해 `.nq`
  + Trig에 대해 `.trig`
  + RDF/XML에 대해 `.rdf`
  + Turtle에 대해 `.ttl`
  + N3에 대해 `.n3`
  + JSON-LD에 대해 `.jsonld`

  이 파일에 포함된 모든 데이터는 `UNLOAD` 작업에 의해 DB 클러스터에서 제거됩니다.

  데이터를 언로드하려면 모든 Amazon S3 인증이 URL에 포함되어야 합니다. Amazon S3 파일에 미리 서명한 후 결과로 생성된 URL을 사용하여 안전하게 액세스할 수 있습니다. 예제:

  ```
  aws s3 presign --expires-in (number of seconds) s3://(bucket name)/(path to file of data to unload)
  ```

  그런 후에 다음 단계를 수행합니다.

  ```
  curl https://(a Neptune endpoint URL):8182/sparql \
    --data-urlencode 'update=unload (pre-signed URL of the remote Amazon S3 data to be unloaded) \
                             from graph (named graph)'
  ```

  자세한 내용은 [요청 인증: 쿼리 파라미터 사용](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)을 참조하십시오.
+ **`FROM GRAPH `***(데이터를 제거할 명명된 그래프)*   –   (*선택 사항*) 원격 데이터를 언로드해야 하는 명명된 그래프를 지정합니다.

  Neptune은 트리플마다 이름이 있는 그래프를 연결합니다. 다음과 같이 폴백 명명된 그래프 URI(`http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`)를 사용하여 기본 명명된 그래프를 지정할 수 있습니다.

  ```
  FROM GRAPH <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>
  ```

`LOAD`가 `INSERT DATA { (inline data) }`에 해당하듯이, `UNLOAD`는 `DELETE DATA { (inline data) }`에 해당합니다. `DELETE DATA`과 같이 `UNLOAD`는 빈 노드를 포함한 데이터에는 사용할 수 없습니다.

로컬 웹 서버가 다음 2개의 트리플이 포함된 `data.nt` 이름의 파일을 제공하는 경우를 예로 들어 보겠습니다.

```
<http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#b> .
<http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#c> .
```

다음 `UNLOAD` 명령은 명명된 `<http://example.org/graph1>` 그래프에서 이 두 트리플을 삭제합니다.

```
UNLOAD <http://localhost:80/data.nt> FROM GRAPH <http://example.org/graph1>
```

이렇게 하면 다음 `DELETE DATA` 명령을 사용하는 것과 같은 효과가 나타납니다.

```
DELETE DATA {
  GRAPH <http://example.org/graph1> {
    <http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#b> .
    <http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#c> .
  }
}
```

**`UNLOAD` 명령으로 인한 예외 발생**
+ **`InvalidParameterException`**   –   데이터에 빈 노드가 있었습니다. *HTTP 상태*: 400 Bad Request.

  *메시지*: ` Blank nodes are not allowed for UNLOAD`

   
+ **`InvalidParameterException`**   –   데이터 구문이 깨졌습니다. *HTTP 상태*: 400 Bad Request.

  *메시지*: `Invalid syntax in the specified file.`

   
+ **`UnloadUrlAccessDeniedException `**   –   액세스가 거부되었습니다. *HTTP 상태*: 400 Bad Request.

  *메시지*: `Update failure: Endpoint (Neptune endpoint) reported access denied error. Please verify access.`

   
+ **`BadRequestException `**   –   원격 데이터를 검색할 수 없습니다. *HTTP 상태*: 400 Bad Request.

  *메시지*: *(HTTP 응답에 따라 다름)*

# SPARQL 쿼리 힌트
쿼리 힌트

쿼리 힌트를 사용하여 Amazon Neptune에서 특정 SPARQL 쿼리에 대한 최적화 및 평가 전략을 지정할 수 있습니다.

쿼리 힌트는 SPARQL 쿼리에 다음과 같은 부분으로 포함된 추가 트리플 패턴을 사용하여 표현됩니다.

```
scope hint value
```
+ *범위* – 쿼리 또는 전체 쿼리의 특정 그룹과 같이 쿼리 힌트가 적용되는 쿼리의 부분을 결정합니다.
+ *힌트* – 적용할 힌트 유형을 식별합니다.
+ *값* – 고려하는 시스템 측면의 동작을 결정합니다.

쿼리 힌트 및 범위는 Amazon Neptune 네임스페이스 `http://aws.amazon.com/neptune/vocab/v01/QueryHints#`의 미리 정의된 용어로 표시됩니다. 이 단원의 예제는 쿼리에 정의되고 포함되는 `hint` 접두사로 네임스페이스를 포함합니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
```

예를 들어 다음은 `SELECT` 쿼리에 `joinOrder` 힌트를 포함하는 방법을 보여줍니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ... {
 hint:Query hint:joinOrder "Ordered" .
 ...
}
```

앞의 쿼리는 Neptune 엔진이 *주어진* 순서에서 쿼리의 조인을 평가하고 모든 자동 재정렬을 비활성화하도록 지시합니다.

쿼리 힌트를 사용할 때는 다음 사항을 고려하십시오.
+ 단일 쿼리에 다른 쿼리 힌트를 결합할 수 있습니다. 예를 들어 `bottomUp` 쿼리 힌트를 사용하여 상향식 평가를 위해 하위 쿼리에 주석을 추가하고 `joinOrder` 쿼리 힌트를 사용하여 하위 쿼리 내부에서 조인 순서를 수정할 수 있습니다.
+ 다른 중복되지 않은 범위에서 동일한 쿼리 힌트를 여러 번 사용할 수 있습니다.
+ 쿼리 힌트가 힌트입니다. 쿼리 엔진은 일반적으로 주어진 쿼리 힌트를 고려하기 위한 것이지만 무시할 수도 있습니다.
+ 쿼리 힌트는 보존되는 의미 체계입니다. 쿼리 힌트를 추가해도 쿼리 출력은 변경되지 않습니다. 순서 보장이 제공되지 않는 경우, 즉 ORDER BY를 사용하여 결과 순서를 명시적으로 적용하지 않는 경우의 잠재적인 결과 순서는 예외입니다.

다음 섹션에서는 Neptune의 사용 가능한 쿼리 힌트 및 해당 사용에 대한 추가 정보를 제공합니다.

**Topics**
+ [

## Neptune의 SPARQL 쿼리 힌트 범위
](#sparql-query-hints-scope)
+ [

# `joinOrder` SPARQL 쿼리 힌트
](sparql-query-hints-joinOrder.md)
+ [

# `evaluationStrategy` SPARQL 쿼리 힌트
](sparql-query-hints-evaluationStrategy.md)
+ [

# `queryTimeout` SPARQL 쿼리 힌트
](sparql-query-hints-queryTimeout.md)
+ [

# `rangeSafe` SPARQL 쿼리 힌트
](sparql-query-hints-rangeSafe.md)
+ [

# `queryId` SPARQL 쿼리 힌트
](sparql-query-hints-queryId.md)
+ [

# `useDFE` SPARQL 쿼리 힌트
](sparql-query-hints-useDFE.md)
+ [

# DESCRIBE와 함께 사용되는 SPARQL 쿼리 힌트
](sparql-query-hints-for-describe.md)

## Neptune의 SPARQL 쿼리 힌트 범위
쿼리 힌트 범위

다음 표에는 Amazon Neptune의 SPARQL 쿼리 힌트에 대한 사용 가능한 범위, 관련 힌트 및 설명이 나와 있습니다. 이러한 항목의 `hint` 접두사는 힌트에 대한 Neptune 네임스페이스를 나타냅니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
```


| Scope | 지원되는 힌트 | 설명 | 
| --- | --- | --- | 
| hint:Query | [joinOrder](sparql-query-hints-joinOrder.md) | 쿼리 힌트는 전체 쿼리에 적용됩니다. | 
| hint:Query | [queryTimeout](sparql-query-hints-queryTimeout.md) | 전체 쿼리에 제한 시간 값이 적용됩니다. | 
| hint:Query | [rangeSafe](sparql-query-hints-rangeSafe.md) | 전체 쿼리에 대해 유형 승격이 비활성화됩니다. | 
| hint:Query | [queryId](sparql-query-hints-queryId.md) | 전체 쿼리에 쿼리 ID 값이 적용됩니다. | 
| hint:Query | [useDFE](sparql-query-hints-useDFE.md) | 전체 쿼리에 대해 DFE 사용이 활성화 또는 비활성화되었습니다. | 
| hint:Group | [joinOrder](sparql-query-hints-joinOrder.md) | 쿼리 힌트는 지정된 그룹의 최상위 요소에는 적용되지만 중첩 요소(예: 하위 쿼리) 또는 상위 요소에는 적용되지 않습니다. | 
| hint:SubQuery | [evaluationStrategy](sparql-query-hints-evaluationStrategy.md) | 힌트가 지정되어 중첩된 SELECT 하위 쿼리에 적용됩니다. 하위 쿼리는 하위 쿼리보다 먼저 계산된 솔루션을 고려하지 않고 독립적으로 평가됩니다. | 

# `joinOrder` SPARQL 쿼리 힌트
joinOrder

SPARQL 쿼리를 제출하면 Amazon Neptune 쿼리 엔진이 쿼리의 구조를 조사합니다. 쿼리의 일부를 재정렬하고 평가 및 쿼리 응답 시간에 필요한 작업량을 최소화하려고 시도합니다.

예를 들어, 연결된 트리플 패턴의 시퀀스는 일반적으로 지정된 순서로 평가되지 않습니다. 개별 패턴의 선택성 및 공유 변수를 통한 연결 방법과 같은 휴리스틱 및 통계를 사용하여 재정렬됩니다. 또한 쿼리에 하위 쿼리, FILTER 또는 복잡한 OPTIONAL 또는 MINUS 블록과 같은 더 복잡한 패턴이 포함된 경우 Neptune 쿼리 엔진은 효율적인 평가 순서를 목표로 가능한 경우 해당 패턴을 재정렬합니다.

보다 복잡한 쿼리의 경우 Neptune이 쿼리를 평가하도록 선택하는 순서가 항상 최적이 아닐 수도 있습니다. 예를 들어, Neptune은 쿼리 평가 중에 나타나는 인스턴스 데이터 관련 특성(예: 그래프의 히팅 파워 노드 등)을 간과할 수 있습니다.

데이터의 정확한 특성을 알고 있고 쿼리 실행 순서를 수동으로 지정하려는 경우 Neptune `joinOrder` 쿼리 힌트를 사용하여 쿼리가 주어진 순서대로 평가되도록 지정합니다.

## `joinOrder` SPARQL 힌트 구문
구문

`joinOrder` 쿼리 힌트는 SPARQL 쿼리에 포함된 트리플 패턴으로 지정됩니다.

명료함을 위해 다음 구문에서는 쿼리에 정의되고 포함된 `hint` 접두사를 사용하여 Neptune 쿼리 힌트 네임스페이스를 지정합니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
scope hint:joinOrder "Ordered" .
```

**사용 가능한 범위**
+ `hint:Query`
+ `hint:Group`

쿼리 힌트 범위에 대한 자세한 내용은 [Neptune의 SPARQL 쿼리 힌트 범위](sparql-query-hints.md#sparql-query-hints-scope) 단원을 참조하십시오.

## `joinOrder` SPARQL 힌트 예제
예제

이 단원에서는 `joinOrder` 쿼리 힌트 및 관련 최적화를 사용하거나 사용하지 않고 작성된 쿼리를 보여줍니다.

이 예제의 경우 데이터 세트에 다음 내용이 포함되어 있다고 가정합니다.
+ `John`이라는 한 사람이 `Jane`을 포함하여 `:likes` 1,000명을 포함합니다.
+ `Jane`이라는 한 사람이 `John`을 포함하여 `:likes` 10명을 포함합니다.

**쿼리 힌트 없음**  
다음 SPARQL 쿼리는 소셜 네트워킹 데이터 세트에서 서로를 좋아하는 `John` 및 `Jane`라고 명명된 모든 쌍의 사람들을 추출합니다.

```
PREFIX : <https://example.com/>
SELECT ?john ?jane {
  ?person1 :name "Jane" .
  ?person1 :likes ?person2 .
  ?person2 :name "John" .
  ?person2 :likes ?person1 .
}
```

Neptune 쿼리 엔진은 서술문과 다른 순서로 문을 평가할 수 있습니다. 예를 들어 다음 순서로 평가할 수도 있습니다.

1. `John`라는 모든 사람을 검색합니다.

1. `:likes` 엣지 기준으로 `John`에 연결된 모든 사람을 검색합니다.

1. 이 집합을 `Jane`이라는 사람들로 필터링합니다.

1. 이 집합을 `:likes` 엣지 기준으로 `John`에 연결된 사람으로 필터링합니다.

데이터 세트에 따라 이 순서로 평가하면 1,000개의 개체가 두 번째 단계에서 추출됩니다. 세 번째 단계는 이를 단일 노드 `Jane`로 좁힙니다. 마지막 단계는 `Jane` 또한 `:likes` `John` 노드를 결정합니다.

**쿼리 힌트**  
그녀는 단 10개의 발신 `:likes` 엣지만 있기 때문에 `Jane` 노드로 시작하는 것이 유리할 것입니다. 이렇게 하면 두 번째 단계에서 1,000개의 개체를 추출하지 않아도 쿼리를 평가하는 동안 작업량이 줄어듭니다.

다음 예제에서는 **joinOrder** 쿼리 힌트를 사용하여 쿼리에 대한 자동 조인 재정렬을 모두 비활성화하여 `Jane` 노드와 해당 발신 엣지가 먼저 처리되도록 합니다.

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ?john ?jane {
  hint:Query hint:joinOrder "Ordered" .
  ?person1 :name "Jane" .
  ?person1 :likes ?person2 .
  ?person2 :name "John" .
  ?person2 :likes ?person1 .
}
```

적용 가능한 실제 시나리오는 네트워크에 연결된 사람들이 연결이 많은 인플루언서 또는 연결이 적은 일반 사용자로 분류되는 소셜 네트워크 애플리케이션일 수 있습니다. 이러한 시나리오에서는 이전 예제와 같은 쿼리에서 일반 사용자(`Jane`)가 인플루언서(`John`)보다 먼저 처리되도록 할 수 있습니다.

**쿼리 힌트 및 재정렬**  
이 예제를 한 단계 더 나갈 수 있습니다. `:name`속성이 단일 노드에 대해 고유하다는 것을 알고 있다면 `joinOrder` 쿼리 힌트를 재정렬하고 사용하여 쿼리의 속도를 높일 수 있습니다. 이 단계에서는 고유한 노드가 먼저 추출되도록 합니다.

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ?john ?jane {
  hint:Query hint:joinOrder "Ordered" .
  ?person1 :name "Jane" .
  ?person2 :name "John" .
  ?person1 :likes ?person2 .
  ?person2 :likes ?person1 .
}
```

이 경우 각 단계에서 다음 단일 작업으로 쿼리를 줄일 수 있습니다.

1. `:name` `Jane`을 사용하여 단일 사람 노드를 검색합니다.

1. `:name` `John`을 사용하여 단일 사람 노드를 검색합니다.

1. `:likes` 엣지를 사용하여 첫 번째 노드가 두 번째 노드에 연결되어 있는지 점검합니다.

1. `:likes` 엣지를 사용하여 두 번째 노드가 첫 번째 노드에 연결되어 있는지 점검합니다.



**중요**  
잘못된 순서를 선택하면 `joinOrder` 쿼리 힌트로 인해 성능이 크게 떨어질 수 있습니다. 예를 들어 앞의 예는 `:name` 속성이 고유하지 않은 경우 비효율적입니다. 100개의 모든 노드가 `Jane`으로 명명되고 1,000개의 모든 노드가 `John`으로 명명된 경우 쿼리는 결국 `:likes` 엣지에 대해 1,000 \$1 100(100,000) 페어를 검사하게 됩니다.

# `evaluationStrategy` SPARQL 쿼리 힌트
evaluationStrategy

`evaluationStrategy` 쿼리 힌트는 Amazon Neptune 쿼리 엔진에 주석이 달린 쿼리 조각을 독립적인 단위로 상향식으로 평가해야 함을 알려줍니다. 이는 이전 평가 단계의 솔루션을 사용하여 쿼리 조각을 계산하지 않음을 의미합니다. 쿼리 조각은 독립 단위로 평가되며 생성된 솔루션은 계산 후 나머지 쿼리와 조인됩니다.

`evaluationStrategy` 쿼리 힌트를 사용하는 것은 차단(파이프라인되지 않은) 쿼리 계획을 의미합니다. 즉, 쿼리 힌트가 주석으로 달려 있는 조각의 솔루션이 실체를 갖추고 주 메모리에 버퍼링된다는 뜻입니다. 이 쿼리 힌트를 사용하면 특히 주석 처리된 쿼리 조각이 많은 수의 결과를 계산할 경우 쿼리를 평가하는 데 필요한 주 메모리의 양이 크게 늘어날 수 있습니다.

## `evaluationStrategy` SPARQL 힌트 구문
구문

`evaluationStrategy` 쿼리 힌트는 SPARQL 쿼리에 포함된 트리플 패턴으로 지정됩니다.

명료함을 위해 다음 구문에서는 쿼리에 정의되고 포함된 `hint` 접두사를 사용하여 Neptune 쿼리 힌트 네임스페이스를 지정합니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
hint:SubQuery hint:evaluationStrategy "BottomUp" .
```

**사용 가능한 범위**
+ `hint:SubQuery`

**참고**  
이 쿼리 힌트는 중첩된 하위 쿼리에서만 지원됩니다.

쿼리 힌트 범위에 대한 자세한 내용은 [Neptune의 SPARQL 쿼리 힌트 범위](sparql-query-hints.md#sparql-query-hints-scope) 단원을 참조하십시오.

## `evaluationStrategy` SPARQL 힌트 예제
예제



이 단원에서는 `evaluationStrategy` 쿼리 힌트 및 관련 최적화를 사용하거나 사용하지 않고 작성된 쿼리를 보여줍니다.

이 예제의 경우 데이터 세트에 다음 특성이 포함되어 있다고 가정합니다.
+ `:connectedTo`이라는 레이블로 지정된 1,000개의 엣지를 포함합니다.
+ 각 `component` 노드는 평균 100개의 다른 `component` 노드에 연결됩니다.
+ 노드 사이의 4홉 주기의 연결 수는 약100입니다.

전형적인 예로 `evaluationStrategy` 힌트는 사이클을 포함하는 쿼리 패턴을 최적화하는 데 도움이 될 수 있습니다.

**쿼리 힌트 없음**  
다음 SPARQL 쿼리는 4개의 홉을 통해 서로 주기적으로 연결된 모든 `component` 노드를 추출합니다.

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
  ?component1 :connectedTo ?component2 .
  ?component2 :connectedTo ?component3 .
  ?component3 :connectedTo ?component4 .
  ?component4 :connectedTo ?component1 .
}
```

Neptune 쿼리 엔진의 접근 방식은 다음 단계를 사용하여 이 쿼리를 평가하는 것입니다.
+ 그래프에서 1,000개의 `connectedTo` 엣지를 모두 추출합니다.
+ 100x(component2에서 발신 `connectedTo` 엣지의 수)로 확장합니다.

  중간 결과: 100,000개 노드
+ 100x(component3에서 발신 `connectedTo` 엣지의 수)로 확장합니다.

  중간 결과: 10,000,000개 노드
+ 주기가 끝날 때까지 10,000,000개의 노드를 스캔합니다.

이 결과 주 메모리가 일정한 스트리밍 쿼리 계획이 생성됩니다.

**쿼리 힌트 및 하위 쿼리**  
계산을 가속화하기 위해 메인 메모리 공간의 균형을 유지하고 싶을 수도 있습니다. `evaluationStrategy` 쿼리 힌트로 쿼리를 다시 작성하여 엔진이 더 작고 구체화된 하위 집합 두 개 사이의 조인을 계산하도록 할 수 있습니다.

```
PREFIX : <https://example.com/>
          PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
  {
    SELECT * WHERE {
      hint:SubQuery hint:evaluationStrategy "BottomUp" .
      ?component1 :connectedTo ?component2 .
      ?component2 :connectedTo ?component3 .
    }
  }
  {
    SELECT * WHERE {
      hint:SubQuery hint:evaluationStrategy "BottomUp" .
      ?component3 :connectedTo ?component4 .
      ?component4 :connectedTo ?component1 .
    }
  }
}
```

다음 트리플 패턴의 결과를 반복적으로 사용하면서 트리플 패턴을 순서대로 평가하는 대신 `evaluationStrategy` 힌트를 사용하면 두 개의 하위 쿼리를 독립적으로 평가할 수 있습니다. 두 하위 쿼리 모두 중간 결과를 위해 100,000개의 노드를 생성한 후에는 최종 결과를 형성하기 위해 함께 조인됩니다.

특히 더 큰 인스턴스 유형에서 Neptune을 실행할 때 이 2개의 100,000개 하위 세트를 주 메모리에 임시로 저장하면 평가 속도가 크게 빨라져 메모리 사용이 늘어납니다.

# `queryTimeout` SPARQL 쿼리 힌트
queryTimeout

`queryTimeout` 쿼리 힌트는 DB 파라미터 그룹에 설정된 `neptune_query_timeout` 값보다 작은 제한 시간을 지정합니다.

이 힌트의 결과로 쿼리가 종료되면 `Operation terminated (deadline exceeded)` 메시지와 함께 `TimeLimitExceededException`이 발생합니다.

## `queryTimeout` SPARQL 힌트 구문
구문

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ... WHERE {
    hint:Query hint:queryTimeout 10 .
    # OR
    hint:Query hint:queryTimeout "10" .
    # OR
    hint:Query hint:queryTimeout "10"^^xsd:integer .
 ...
}
```

제한 시간 값은 밀리초로 표시됩니다.

제한 시간 값은 DB 파라미터 그룹에 설정된 `neptune_query_timeout` 값보다 작아야 합니다. 그렇지 않으면 `Malformed query: Query hint 'queryTimeout' must be less than neptune_query_timeout DB Parameter Group` 메시지와 함께 `MalformedQueryException` 예외가 발생합니다.

`queryTimeout` 쿼리 힌트는 아래 예제에 표시된 대로 주 쿼리의 `WHERE` 절 또는 하위 쿼리 중 하나의 `WHERE` 절에 지정되어야 합니다.

모든 쿼리/하위 쿼리 및 SPARQL 업데이트 섹션(예: INSERT 및 DELETE)에서 한 번만 설정해야 합니다. 그렇지 않으면 `Malformed query: Query hint 'queryTimeout' must be set only once` 메시지와 함께 `MalformedQueryException` 예외가 발생합니다.

**사용 가능한 범위**

`queryTimeout` 힌트는 SPARQL 쿼리 및 업데이트에 모두 적용할 수 있습니다.
+ SPARQL 쿼리에서는 기본 쿼리 또는 하위 쿼리의 WHERE 절에 나타날 수 있습니다.
+ SPARQL 업데이트에서 INSERT, DELETE 또는 WHERE 절에서 설정할 수 있습니다. 여러 개의 업데이트 절이 있는 경우 그 중 하나에만 설정할 수 있습니다.

쿼리 힌트 범위에 대한 자세한 내용은 [Neptune의 SPARQL 쿼리 힌트 범위](sparql-query-hints.md#sparql-query-hints-scope) 단원을 참조하십시오.

## `queryTimeout` SPARQL 힌트 예제
예제

다음은 `UPDATE` 쿼리의 주 `WHERE` 절에서 `hint:queryTimeout`을 사용하는 방법에 대한 예제입니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
INSERT {
    ?s ?p ?o
} WHERE {
    hint:Query hint:queryTimeout 100 .
    ?s ?p ?o .
}
```

여기서 `hint:queryTimeout`은 하위 쿼리의 `WHERE` 절입니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
   ?s ?p ?o .
   {
      SELECT ?s WHERE {
         hint:Query hint:queryTimeout 100 .
         ?s ?p1 ?o1 .
      }
   }
}
```

# `rangeSafe` SPARQL 쿼리 힌트
rangeSafe

이 쿼리 힌트를 사용하여 SPARQL 쿼리의 유형 승격을 비활성화할 수 있습니다.

숫자 값 또는 범위에 걸쳐 `FILTER`가 포함된 SPARQL 쿼리를 제출할 때 Neptune 쿼리 엔진은 쿼리 실행 시 일반적으로 유형 승격을 사용해야 합니다. 즉, 필터링 대상 값을 포함할 수 있는 모든 유형의 값을 검사해야 합니다.

예를 들어, 55와 같은 값을 필터링하는 경우 엔진은 55와 같은 정수, 55L의 긴 정수, 55.0과 같은 부동 소수점 등을 검색해야 합니다. 각 유형 승격에는 스토리지에 대한 추가 검색이 필요하므로, 겉보기에 간단한 쿼리를 완료하는 데 예상하지 못하게 시간이 오래 걸릴 수 있습니다.

특정 유형의 값만 찾으면 된다는 사실을 미리 알고 있기 때문에 유형 승격이 불필요한 경우가 많습니다. 이 경우 `rangeSafe` 쿼리 힌트를 사용하여 유형 승격을 끄면 쿼리 속도를 크게 높일 수 있습니다.

## `rangeSafe` SPARQL 힌트 구문
구문

`rangeSafe` 쿼리 힌트는 `true` 값을 취해 유형 승격을 비활성화합니다. 또한 `false`(기본값) 값도 사용할 수 있습니다.

**예제.** 다음 예제는 1보다 큰 `o` 정수 값을 필터링할 때 유형 승격을 비활성화하는 방법을 보여줍니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
   ?s ?p ?o .
   hint:Prior hint:rangeSafe 'true' .
   FILTER (?o > '1'^^<http://www.w3.org/2001/XMLSchema#int>)
```

# `queryId` SPARQL 쿼리 힌트
queryId

이 쿼리 힌트를 사용해 SPARQL 쿼리에 자체 queryld 값을 할당합니다.

예제:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * WHERE {
  hint:Query hint:queryId "4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
  {?s ?p ?o}}
```

할당한 값은 Neptune DB의 모든 쿼리에서 고유해야 합니다.

# `useDFE` SPARQL 쿼리 힌트
useDFE

이 쿼리 힌트를 사용하면 DFE를 사용하여 쿼리를 실행할 수 있습니다. 기본적으로 Neptune은 이 쿼리 힌트를 `true`로 설정하지 않으면 DFE를 사용하지 않습니다. 이는 [neptune\$1dfe\$1query\$1engine](parameters.md#parameters-instance-parameters-neptune_dfe_query_engine) 인스턴스 파라미터의 기본값이 `viaQueryHint`로 설정되기 때문입니다. 인스턴스 파라미터를 `enabled`로 설정하면 `useDFE` 쿼리 힌트가 `false`로 설정된 쿼리를 제외한 모든 쿼리에 DFE 엔진이 사용됩니다.

쿼리에 DFE를 사용하도록 설정하는 예제:

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>

SELECT ?john ?jane
{
  hint:Query hint:useDFE true .
  ?person1 :name "Jane" .
  ?person1 :likes ?person2 .
  ?person2 :name "John" .
  ?person2 :likes ?person1 .
}
```

# DESCRIBE와 함께 사용되는 SPARQL 쿼리 힌트
DESCRIBE 쿼리 힌트

SPARQL `DESCRIBE` 쿼리는 리소스 설명을 요청하는 유연한 메커니즘을 제공합니다. 그러나 SPARQL 사양에서는 `DESCRIBE`의 정확한 의미를 정의하지 않습니다.

[엔진 릴리스 1.2.0.2](engine-releases-1.2.0.2.md)부터 Neptune은 다양한 상황에 적합한 여러 `DESCRIBE` 모드와 알고리즘을 지원합니다.

이 샘플 데이터 세트는 다양한 모드를 설명하는 데 도움이 될 수 있습니다.

```
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix : <https://example.com/> .

:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JohnDoe :firstName "John" .
:JaneDoe :knows _:b1 .
_:b1 :knows :RichardRoe .

:RichardRoe :knows :JaneDoe .
:RichardRoe :firstName "Richard" .

_:s1 rdf:type rdf:Statement .
_:s1 rdf:subject :JaneDoe .
_:s1 rdf:predicate :knows .
_:s1 rdf:object :JohnDoe .
_:s1 :knowsFrom "Berlin" .

:ref_s2 rdf:type rdf:Statement .
:ref_s2 rdf:subject :JaneDoe .
:ref_s2 rdf:predicate :knows .
:ref_s2 rdf:object :JohnDoe .
:ref_s2 :knowsSince 1988 .
```

아래 예제에서는 다음과 같은 SPARQL 쿼리를 사용하여 리소스 `:JaneDoe`에 대한 설명을 요청한다고 가정합니다.

```
DESCRIBE <https://example.com/JaneDoe>
```

## `describeMode` SPARQL 쿼리 힌트
`describeMode`

`hint:describeMode` SPARQL 쿼리 힌트는 Neptune에서 지원하는 다음 SPARQL `DESCRIBE` 모드 중 하나를 선택하는 데 사용됩니다.

### `ForwardOneStep` DESCRIBE 모드
`ForwardOneStep` 모드

다음과 같이 `describeMode` 쿼리 힌트를 사용하여 `ForwardOneStep` 모드를 간접적으로 호출합니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "ForwardOneStep"
}
```

이 `ForwardOneStep` 모드는 설명할 리소스의 속성과 전달 링크만 반환합니다. 이 예제에서는 다음과 같이 설명할 리소스인 `:JaneDoe`를 보유한 트리플을 반환합니다.

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b301990159 .
```

참고로 DESCRIBE 쿼리는 입력 데이터 세트와 비교하여 매번 ID가 다른 빈 노드가 있는 트리플을 반환할 수 있습니다(예: `_:b301990159`).

### `SymmetricOneStep` DESCRIBE 모드
`SymmetricOneStep` 모드

`SymmetricOneStep`은 쿼리 힌트를 제공하지 않는 경우의 기본 DESCRIBE 모드입니다. 다음과 같이 `describeMode` 쿼리 힌트를 사용하여 명시적으로 간접 호출할 수도 있습니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "SymmetricOneStep"
}
```

`SymmetricOneStep` 시맨틱에서 `DESCRIBE`는 설명할 리소스의 속성, 정방향 링크 및 역방향 링크를 반환합니다.

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b318767375 .

_:b318767631 rdf:subject :JaneDoe .

:RichardRoe :knows :JaneDoe .

:ref_s2 rdf:subject :JaneDoe .
```

### 간결한 경계 설명(`CBD`) DESCRIBE 모드
`CBD` 모드

간결한 경계 설명(`CBD`) 모드는 다음과 같은 `describeMode` 쿼리 힌트를 사용하여 간접적으로 호출됩니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "CBD"
}
```

`CBD` 시맨틱에 따라 `DESCRIBE`는 설명할 리소스의 간결한 경계 설명([W3C에서 정의한](http://www.w3.org/Submission/CBD) 대로)을 반환합니다.

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b285212943 .
_:b285212943 :knows :RichardRoe .

_:b285213199 rdf:subject :JaneDoe .
_:b285213199 rdf:type rdf:Statement .
_:b285213199 rdf:predicate :knows .
_:b285213199 rdf:object :JohnDoe .
_:b285213199 :knowsFrom "Berlin" .

:ref_s2 rdf:subject :JaneDoe .
```

RDF 리소스, 즉 RDF 그래프의 노드에 대한 간결한 경계 설명은 단독으로 사용할 수 있는 해당 노드를 중심으로 하는 가장 작은 하위 그래프입니다. 실제로 이는 그래프를 루트로 지정된 노드를 사용하는 트리라고 생각하면 트리에 잎이 있는 것처럼 빈 노드(bnode)가 없다는 의미입니다. bnode는 외부에서 주소를 지정하거나 후속 쿼리에 사용할 수 없으므로, 현재 노드에서 다음 단일 홉을 찾기 위해 그래프를 탐색하는 것만으로는 충분하지 않습니다. 또한 후속 쿼리에 사용할 수 있는 항목(bnode 이외의 항목)을 충분히 찾아내야 합니다.

#### CBD 컴퓨팅
CBD 계산

소스 RDF 그래프의 특정 노드(시작 노드 또는 루트)가 주어지면 해당 노드의 CBD는 다음과 같이 계산됩니다.

1. 문의 *주제*가 시작 노드인 소스 그래프의 모든 문을 하위 그래프에 포함하세요.

1. 재귀적으로, 지금까지 빈 노드 *객체*가 있는 하위 그래프의 모든 문에 대해서는 문의 *주제*가 빈 노드이고 아직 하위 그래프에 포함되지 않은 소스 그래프의 모든 문을 하위 그래프에 포함합니다.

1. 재귀적으로, 지금까지 하위 그래프에 포함된 모든 문의 경우 소스 그래프에 있는 이러한 문의 모든 구체화에는 각 구체화의 `rdf:Statement` 노드에서 시작하는 CBD가 포함됩니다.

그러면 *객체* 노드가 IRI 참조 또는 리터럴이거나 빈 노드가 그래프에서 문의 *주제*로 사용되지 않는 하위 그래프가 생성됩니다. 단, 단일 SPARQL SELECT 또는 CONSTRUCT 쿼리로는 CBD를 계산할 수 없습니다.

### 간결한 대칭적 경계 설명(`SCBD`) DESCRIBE 모드
`SCBD` 모드

간결한 대칭적 경계 설명(`SCBD`) 모드는 다음과 같은 `describeMode` 쿼리 힌트를 사용하여 간접적으로 호출됩니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "SCBD"
}
```

`SCBD` 시맨틱에 따라 `DESCRIBE`는 W3C가 [VoID 어휘를 사용하여 연결된 데이터 세트 설명](http://www.w3.org/TR/void/)에서 정의한 대로 리소스의 간결한 대칭적 경계 설명을 반환합니다.

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b335544591 .
_:b335544591 :knows :RichardRoe .

:RichardRoe :knows :JaneDoe .

_:b335544847 rdf:subject :JaneDoe .
_:b335544847 rdf:type rdf:Statement .
_:b335544847 rdf:predicate :knows .
_:b335544847 rdf:object :JohnDoe .
_:b335544847 :knowsFrom "Berlin" .

:ref_s2 rdf:subject :JaneDoe .
```

`ForwardOneStep` 및 `SymmetricOneStep` 모드에 비해 CBD와 SCBD의 장점은 빈 노드가 항상 해당 표현을 포함하도록 확장된다는 것입니다. SPARQL을 사용하여 빈 노드를 쿼리할 수 없기 때문에 이는 중요한 이점일 수 있습니다. 또한 CBD 및 SCBD 모드에서는 구체화도 고려합니다.

참고로 `describeMode` 쿼리 힌트는 `WHERE` 절의 일부일 수도 있습니다.

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE ?s
WHERE {
  hint:Query hint:describeMode "CBD" .
  ?s rdf:type <https://example.com/Person>
}
```

## `describeIterationLimit` SPARQL 쿼리 힌트
`describeIterationLimit`

`hint:describeIterationLimit` SPARQL 쿼리 힌트는 CBD 및 SCBD와 같은 반복적 DESCRIBE 알고리즘에 대해 수행할 최대 반복 확장 횟수에 대한 **선택적** 제약 조건을 제공합니다.

DESCRIBE 제한은 AND로 연결됩니다. 따라서 반복 제한과 문 제한을 둘 다 지정하는 경우 DESCRIBE 쿼리를 끊기 전에 두 제한을 모두 충족해야 합니다.

이 값의 기본값은 5입니다. 이 값을 0으로 설정하여 반복 확장 횟수에 제한을 두지 않도록 지정할 수 있습니다.

## `describeStatementLimit` SPARQL 쿼리 힌트
`describeStatementLimit`

`hint:describeStatementLimit` SPARQL 쿼리 힌트는 DESCRIBE 쿼리 응답에 존재할 수 있는 최대 문 수에 대한 **선택적** 제약 조건을 제공합니다. CBD 및 SCBD와 같은 반복적인 DESCRIBE 알고리즘에만 적용됩니다.

DESCRIBE 제한은 AND로 연결됩니다. 따라서 반복 제한과 문 제한을 둘 다 지정하는 경우 DESCRIBE 쿼리를 끊기 전에 두 제한을 모두 충족해야 합니다.

이 값의 기본값은 5,000입니다. 반환되는 문 수에 제한을 두지 않도록 지정하려면 이 값을 0으로 설정하면 됩니다.

# 기본 그래프와 관련된 SPARQL DESCRIBE 동작
DESCRIBE 및 기본 그래프

SPARQL [https://www.w3.org/TR/sparql11-query/#describe](https://www.w3.org/TR/sparql11-query/#describe) 쿼리 양식을 사용하면 데이터 구조를 알거나 쿼리를 작성할 필요 없이 리소스에 대한 정보를 검색할 수 있습니다. 이 정보를 조합하는 방법은 SPARQL 구현에 달려 있습니다. Neptune은 `DESCRIBE`가 사용할 수 있는 다양한 모드와 알고리즘을 간접적으로 호출하는 [몇 가지 쿼리 힌트](sparql-query-hints-for-describe.md)를 제공합니다.

Neptune 구현에서 `DESCRIBE`은 모드에 관계없이 [SPARQL 기본 그래프](feature-sparql-compliance.md#sparql-default-graph)에 존재하는 데이터만 사용합니다. 이는 SPARQL이 데이터 세트를 처리하는 방식과 일치합니다(SPARQL 사양에 [RDF 데이터 세트 지정](https://www.w3.org/TR/sparql11-query/#specifyingDataset) 참조).

Neptune에서 기본 그래프에는 `FROM` 및/또는 `FROM NAMED` 절을 사용하여 명명된 특정 그래프를 지정하지 않는 한 데이터베이스 내 명명된 전체 그래프의 조합에 있는 모든 고유 트리플이 포함됩니다. Neptune의 모든 RDF 데이터는 명명된 그래프에 저장됩니다. 이름이 지정된 그래프 컨텍스트 없이 트리플이 삽입되면 Neptune은 `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`로 지정된 명명 그래프에 트리플을 저장합니다.

`FROM` 절을 사용하여 명명된 그래프를 하나 이상 지정하는 경우 기본 그래프는 명명된 그래프에 있는 모든 고유한 트리플의 조합이 됩니다. `FROM` 절이 없고 `FROM NAMED` 절이 하나 이상 있는 경우 기본 그래프는 비어 있습니다.

## SPARQL `DESCRIBE` 예제
예제

다음 데이터를 고려하세요.

```
PREFIX ex: <https://example.com/>

GRAPH ex:g1 {
    ex:s ex:p1 "a" .
    ex:s ex:p2 "c" .
}

GRAPH ex:g2 {
    ex:s ex:p3 "b" .
    ex:s ex:p2 "c" .
}

ex:s ex:p3 "d" .
```

이 쿼리의 경우는 다음과 같습니다.

```
PREFIX ex: <https://example.com/>
DESCRIBE ?s
FROM ex:g1
FROM NAMED ex:g2
WHERE {
  GRAPH ex:g2 { ?s ?p "b" . }
}
```

Neptune은 다음을 반환합니다.

```
ex:s ex:p1 "a" .
ex:s ex:p2 "c" .
```

여기서는 `GRAPH ex:g2 { ?s ?p "b" }` 그래프 패턴을 먼저 평가하여 `?s`에 대한 바인딩을 생성한 다음, `DESCRIBE` 부분은 기본 그래프를 통해 평가됩니다(현재는 `ex:g1`임),

하지만 이 쿼리의 경우는 다음과 같습니다.

```
PREFIX ex: <https://example.com/>
DESCRIBE ?s 
FROM NAMED ex:g1 
WHERE { 
  GRAPH ex:g1 { ?s ?p "a" . } 
}
```

Neptune은 아무것도 반환하지 않습니다. `FROM` 절이 없는 `FROM NAMED` 절이 있으면 기본 그래프가 비어 있기 때문입니다.

다음 쿼리에서 `DESCRIBE`는 `FROM` 또는 `FROM NAMED` 절이 없는 상태로 사용됩니다.

```
PREFIX ex: <https://example.com/>
DESCRIBE ?s 
WHERE { 
  GRAPH ex:g1 { ?s ?p "a" . } 
}
```

이 경우 기본 그래프는 데이터베이스의 모든 명명된 그래프를 합친 모든 고유 트리플로 구성되므로(형식적으로는 RDF 병합), Neptune은 다음을 반환합니다.

```
ex:s ex:p1 "a" . 
ex:s ex:p2 "c" . 
ex:s ex:p3 "b" .
ex:s ex:p3 "d" .
```

# SPARQL 쿼리 상태 API
쿼리 상태

SPARQL 쿼리의 상태를 가져오려면 HTTP `GET` 또는 `POST`를 사용하여 `https://your-neptune-endpoint:port/sparql/status` 엔드포인트에 대한 요청을 생성합니다.

## SPARQL 쿼리 상태 요청 파라미터
요청 파라미터

**queryId(선택 사항)**  
실행 중인 SPARQL 쿼리의 ID입니다. 지정된 쿼리의 상태만 표시합니다.

## SPARQL 쿼리 상태 응답 구문
응답 구문

```
{
    "acceptedQueryCount": integer,
    "runningQueryCount": integer,
    "queries": [
      {
        "queryId":"guid",
        "queryEvalStats":
          {
            "subqueries": integer,
            "elapsed": integer,
            "cancelled": boolean
          },
        "queryString": "string"
      }
    ]
}
```

## SPARQL 쿼리 상태 응답 값
응답 값

**acceptedQueryCount**  
Neptune 엔진을 마지막으로 다시 시작한 이후 허용되는 쿼리 수입니다.

**runningQueryCount**  
현재 실행 중인 SPARQL 쿼리의 수입니다.

**쿼리**  
현재 SPARQL 쿼리의 목록입니다.

**queryId**  
쿼리의 GUID id. Neptune이 ID 값을 각 쿼리에 자동 할당하거나 사용자가 자체 ID를 할당할 수 있습니다([Neptune Gremlin 또는 SPARQL 쿼리에 사용자 지정 ID 주입](features-query-id.md) 참조).

**queryEvalStats**  
이 쿼리에 대한 통계.

**하위 쿼리**  
이 쿼리에 있는 하위 쿼리의 수.

**Elapsed**  
지금까지 쿼리가 실행된 시간(단위: 밀리초).

**cancelled**  
True는 쿼리가 취소되었음을 나타냅니다.

**queryString**  
제출된 쿼리.

## SPARQL 쿼리 상태 예제
예제

다음은 상태 명령 실행 시 `curl` 및 HTTP `GET`을 사용한 예입니다.

```
curl https://your-neptune-endpoint:port/sparql/status
```

이 출력은 실행 중인 쿼리 한 개를 보여줍니다.

```
{
    "acceptedQueryCount":9,
    "runningQueryCount":1,
    "queries": [
        {
            "queryId":"fb34cd3e-f37c-4d12-9cf2-03bb741bf54f",
            "queryEvalStats":
                {
                    "subqueries": 0,
                    "elapsed": 29256,
                    "cancelled": false
                },
            "queryString": "SELECT ?s ?p ?o WHERE {?s ?p ?o}"
        }
    ]
}
```

# SPARQL 쿼리 취소
쿼리 취소

SPARQL 쿼리의 상태를 가져오려면 HTTP `GET` 또는 `POST`를 사용하여 `https://your-neptune-endpoint:port/sparql/status` 엔드포인트에 대한 요청을 생성합니다.

## SPARQL 쿼리 취소 요청 파라미터
요청 파라미터

**cancelQuery**  
(필수) 상태 명령을 해서 쿼리를 취소합니다. 이 파라미터는 값을 갖지 않습니다.

**queryId**  
(필수) 취소하려는 실행 중 SPARQL 쿼리의 ID.

**적용 안 됨**  
(선택 사항) `silent=true`이면 실행 중인 쿼리가 취소되고 HTTP 응답 코드가 200이 됩니다. `silent`가 존재하지 않거나 `silent=false`인 경우에는 HTTP 500 상태 코드를 통해 쿼리가 취소됩니다.

## SPARQL 쿼리 취소 예제
예제

**예제 1: `silent=false`을 통한 취소**  
다음은 `silent` 파라미터가 `false`으로 설정된 상태에서 쿼리를 취소하기 위해 `curl`을 사용하는 상태 명령의 예제입니다.

```
curl https://your-neptune-endpoint:port/sparql/status \
  -d "cancelQuery" \
  -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47" \
  -d "silent=false"
```

쿼리가 이미 결과 스트리밍을 시작한 경우가 아니라면, 취소된 쿼리는 다음과 같은 응답으로 HTTP 500 코드를 반환할 것입니다.

```
{
  "code": "CancelledByUserException",
  "requestId": "4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47",
  "detailedMessage": "Operation terminated (cancelled by user)"
}
```

쿼리가 이미 HTTP 200 코드(OK)를 반환하고 취소되기 전에 결과를 스트리밍하기 시작한 경우, 제한 시간 예외 정보가 일반 출력 스트림으로 전송됩니다.

**예제 2: `silent=true`을 통한 취소**  
다음은 `silent` 파라미터가 `true`로 설정된 경우를 제외하고 위와 동일한 상태 명령의 예제입니다.

```
curl https://your-neptune-endpoint:port/sparql/status \
  -d "cancelQuery" \
  -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47" \
  -d "silent=true"
```

이 명령은 `silent=false`일 때와 동일한 응답을 반환하지만, 취소된 쿼리는 이와 같은 응답과 함께 HTTP 200 코드를 반환합니다.

```
{
  "head" : {
    "vars" : [ "s", "p", "o" ]
  },
  "results" : {
    "bindings" : [ ]
  }
}
```

# Amazon Neptune에서 SPARQL 1.1 그래프 스토어 HTTP 프로토콜(GSP) 사용
그래프 스토어 프로토콜

[SPARQL 1.1 그래프 스토어 HTTP 프로토콜](https://www.w3.org/TR/sparql11-http-rdf-update/) 권장 사항에서 W3C는 RDF 그래프 관리를 위한 HTTP 프로토콜을 정의했습니다. RDF 그래프 콘텐츠를 제거, 생성, 교체하는 작업과 기존 콘텐츠에 RDF 문을 추가하는 작업을 정의합니다.

그래프 스토어 프로토콜(GSP)은 복잡한 SPARQL 쿼리를 작성하지 않고도 전체 그래프를 조작할 수 있는 편리한 방법을 제공합니다.

Neptune은이 프로토콜을 완전히 지원합니다.

그래프 스토어 프로토콜(GSP)의 엔드포인트는 다음과 같습니다.

```
https://your-neptune-cluster:port/sparql/gsp/
```

GSP를 사용하여 기본 그래프에 액세스하려면 다음을 사용하세요.

```
https://your-neptune-cluster:port/sparql/gsp/?default
```

GSP를 사용하여 명명된 그래프에 액세스하려면 다음을 사용하세요.

```
https://your-neptune-cluster:port/sparql/gsp/?graph=named-graph-URI
```

## Neptune GSP 구현에 대한 특별 세부 정보
구현 세부 정보

Neptune은 GSP를 정의하는 [W3C 권장 사항](https://www.w3.org/TR/sparql11-http-rdf-update/)을 완벽하게 구현합니다. 하지만 사양에서 다루지 않는 몇 가지 상황이 있습니다.

그중 하나는 `PUT` 또는 `POST` 요청이 요청 본문에 요청 URL로 지정된 그래프와 다른 명명된 그래프를 하나 이상 지정하는 경우입니다. 이는 요청 본문 RDF 형식이 명명된 그래프를 지원하는 경우(예: `Content-Type: application/n-quads` 또는 `Content-Type: application/trig` 사용)에만 발생할 수 있습니다.

이 경우 Neptune은 본문에 있는 모든 명명된 그래프와 URL에 지정된 명명된 그래프를 추가하거나 업데이트합니다.

예를 들어, 빈 데이터베이스에서 시작하여 투표를 3개의 그래프로 정리해 달라는 `PUT` 요청을 보낸다고 가정해 보겠습니다. `urn:votes`라는 이름의 1개에는 모든 선거 연도의 모든 투표가 포함되어 있습니다. `urn:votes:2005` 및 `urn:votes:2019`라는 이름의 다른 2개에는 특정 선거 연도의 투표가 포함되어 있습니다. 요청과 페이로드는 다음과 같습니다.

```
PUT "http://your-Neptune-cluster:port/sparql/gsp/?graph=urn:votes"
  Host: example.com
  Content-Type: application/n-quads

  PAYLOAD:

  <urn:JohnDoe> <urn:votedFor> <urn:Labour> <urn:votes:2005>
  <urn:JohnDoe> <urn:votedFor> <urn:Conservative> <urn:votes:2019>
  <urn:JaneSmith> <urn:votedFor> <urn:LiberalDemocrats> <urn:votes:2005>
  <urn:JaneSmith> <urn:votedFor> <urn:Conservative> <urn:votes:2019>
```

요청이 실행된 후 데이터베이스의 데이터는 다음과 같습니다.

```
<urn:JohnDoe>   <urn:votedFor> <urn:Labour>           <urn:votes:2005>
<urn:JohnDoe>   <urn:votedFor> <urn:Conservative>     <urn:votes:2019>
<urn:JaneSmith> <urn:votedFor> <urn:LiberalDemocrats> <urn:votes:2005>
<urn:JaneSmith> <urn:votedFor> <urn:Conservative>     <urn:votes:2019>
<urn:JohnDoe>   <urn:votedFor> <urn:Labour>           <urn:votes>
<urn:JohnDoe>   <urn:votedFor> <urn:Conservative>     <urn:votes>
<urn:JaneSmith> <urn:votedFor> <urn:LiberalDemocrats> <urn:votes>
<urn:JaneSmith> <urn:votedFor> <urn:Conservative>     <urn:votes>
```

또 다른 모호한 상황은 `PUT`, `POST`, `GET`, `DELETE` 중 하나를 사용하여 요청 URL 자체에 둘 이상의 그래프를 지정하는 경우입니다. 예제:

```
POST "http://your-Neptune-cluster:port/sparql/gsp/?graph=urn:votes:2005&graph=urn:votes:2019"
```

또는 다음과 같습니다.

```
GET "http://your-Neptune-cluster:port/sparql/gsp/?default&graph=urn:votes:2019"
```

이 경우 Neptune은 요청 URL에 그래프를 하나만 지정할 수 있다는 메시지와 함께 HTTP 400을 반환합니다.

# SPARQL `explain`을 사용하여 Neptune 쿼리 실행 분석
SPARQL `explain`

Amazon Neptune에는 *Explain*이라는 SPARQL 기능이 추가되었습니다. 이 기능은 Neptune 엔진에 의해 수행되는 실행 접근 방식을 이해하기 위한 셀프 서비스 도구입니다. SPARQL 쿼리를 제출하는 HTTP 호출에 `explain` 파라미터를 추가하여 이 도구를 호출합니다.

`explain` 기능은 쿼리 실행 계획의 논리 구조에 대한 정보를 제공합니다. 이 정보를 사용하여 잠재적 평가 및 실행 병목 현상을 파악합니다. 그런 다음 [쿼리 힌트](sparql-query-hints.md)를 사용하여 쿼리 실행 계획을 개선할 수 있습니다.

**Topics**
+ [

# Neptune에서 SPARQL 쿼리 엔진 작동 방식
](sparql-explain-engine.md)
+ [

# SPARQL `explain`을 사용하여 Neptune 쿼리 실행을 분석하는 방법
](sparql-explain-using.md)
+ [

# Neptune에서 SPARQL `explain`을 간접 호출하는 예제
](sparql-explain-examples.md)
+ [

# Neptune SPARQL `explain` 연산자
](sparql-explain-operators.md)
+ [

# Neptune에서 SPARQL `explain` 제한
](sparql-explain-limitations.md)

# Neptune에서 SPARQL 쿼리 엔진 작동 방식
SPARQL 쿼리 엔진

SPARQL `explain` 기능이 제공하는 정보를 사용하려면 Amazon Neptune SPARQL 쿼리 엔진이 작동하는 방식에 대한 몇 가지 세부 정보를 이해해야 합니다.

이 엔진은 모든 SPARQL 쿼리를 연산자의 파이프라인으로 변환합니다. 첫 번째 연산자부터 시작하여 *바인딩 목록*이라는 중간 솔루션이 이 연산자 파이프라인을 통해 진행됩니다. 바인딩 목록을 테이블 헤더가 쿼리에 사용된 변수의 하위 집합인 테이블이라고 할 수 있습니다. 테이블의 각 행은 평가 지점까지의 결과를 나타냅니다.

데이터에 대해 두 개의 네임스페이스 접두사가 정의되어 있다고 가정해 보겠습니다.

```
  @prefix ex:   <http://example.com> .
  @prefix foaf: <http://xmlns.com/foaf/0.1/> .
```

다음은 이 컨텍스트에서 간단한 바인딩 목록의 예입니다.

```
  ?person       | ?firstName
  ------------------------------------------------------
  ex:JaneDoe    | "Jane"
  ex:JohnDoe    | "John"
  ex:RichardRoe | "Richard"
```

세 명의 사람 각각에 대해 이 목록은 `?person` 변수를 사람의 식별자에 바인딩하고, `?firstName` 변수를 사람의 이름에 바인딩합니다.

일반적으로 데이터에 값이 없는 쿼리에 변수의 `OPTIONAL` 선택 항목이 있는 경우 변수를 언바운드 상태로 둘 수 있습니다.

`PipelineJoin` 연산자는 `explain` 출력에 있는 Neptune 쿼리 엔진 연산자의 예입니다. 이 연산자는 이전 연산자에서 수신 바인딩 집합을 입력으로 가져와 트리플 패턴에 조인합니다(`(?person, foaf:lastName, ?lastName)`). 이 작업은 입력 스트림에서 `?person` 변수에 대한 바인딩을 사용하고, 트리플 패턴으로 대체하고, 데이터베이스에서 트리플을 찾습니다.

이전 테이블의 수신 바인딩 컨텍스트에서 실행될 때 `PipelineJoin`은 다음과 같은 3개의 조회를 평가합니다.

```
  (ex:JaneDoe,    foaf:lastName, ?lastName)
  (ex:JohnDoe,    foaf:lastName, ?lastName)
  (ex:RichardRoe, foaf:lastName, ?lastName)
```

이 접근 방식은 *바인딩된 상태(as-bound)* 평가라고 합니다. 이 평가 프로세스의 솔루션은 수신 솔루션에 다시 조인되며, 수신 솔루션에서 감지된 `?lastName`을 패딩합니다. 세 명의 사람들에 대한 성을 모두 찾은 경우 연산자는 다음과 같은 발신 바인딩 목록을 생성합니다.

```
  ?person       | ?firstName | ?lastName
  ---------------------------------------
  ex:JaneDoe    | "Jane"     | "Doe"
  ex:JohnDoe    | "John"     | "Doe"
  ex:RichardRoe | "Richard"  | "Roe"
```

이 발신 바인딩 목록은 파이프라인에서 다음 연산자에 대한 입력 역할을 합니다. 마지막으로 파이프라인에서 마지막 연산자의 출력은 쿼리 결과를 정의합니다.

연산자 파이프라인은 모든 연산자가 단일 연결 연산자에 대한 솔루션을 출력한다는 점에서 종종 선형입니다. 그러나 경우에 따라 구조가 더 복잡할 수 있습니다. 예를 들어, SPARQL 쿼리의 `UNION` 연산자는 `Copy` 작업에 매핑됩니다. 이 작업은 바인딩을 복제하고 복사본을 두 개의 하위 계획으로 전달합니다. 하나는 `UNION`의 왼쪽에 대한 것이며 다른 하나는 오른쪽에 대한 것입니다.

연산자에 대한 자세한 내용은 [Neptune SPARQL `explain` 연산자](sparql-explain-operators.md) 단원을 참조하십시오.

# SPARQL `explain`을 사용하여 Neptune 쿼리 실행을 분석하는 방법
SPARQL `explain` 사용

SPARQL `explain` 기능은 Neptune 엔진에 의해 수행되는 실행 접근 방식을 이해하는 데 도움이 되는 Amazon Neptune의 셀프 서비스 도구입니다. `explain`을 호출하려면 `explain=mode` 형식으로 파라미터를 HTTP 또는 HTTPS 요청에 전달합니다.

모드 값은 `static`, `dynamic`, `details` 중 하나일 수 있습니다.
+ *정적* 모드에서 `explain`은 쿼리 계획의 정적 구조만 인쇄합니다.
+ *동적* 모드에서 `explain`은 쿼리 계획의 동적 측면도 포함합니다. 이러한 측면에는 연산자를 통해 진행되는 중간 바인딩의 수, 수신 바인딩과 발신 바인딩의 비율, 연산자에 소요된 총 시간이 포함됩니다.
+ *세부* 모드에서 `explain`은 `dynamic` 모드로 표시된 정보와 조인 연산자의 기본 패턴에 대한 실제 SPARQL 쿼리 문자열 및 예상 범위 수와 같은 추가 세부 정보를 인쇄합니다.

Neptune은 다음과 같이 [W3C SPARQL 1.1 프로토콜](https://www.w3.org/TR/sparql11-protocol/#query-operation) 사양에 나열된 3가지 SPARQL 쿼리 액세스 프로토콜과 함께 `explain`을 사용할 수 있도록 지원합니다.

1. HTTP GET

1. URL로 인코딩한 파라미터를 사용하는 HTTP POST

1. 텍스트 파라미터를 사용하는 HTTP POST

SPARQL 쿼리 엔진에 대한 자세한 내용은 [Neptune에서 SPARQL 쿼리 엔진 작동 방식](sparql-explain-engine.md) 단원을 참조하십시오.

SPARQL `explain` 호출을 통해 생성된 출력의 종류에 대한 자세한 내용은 [Neptune에서 SPARQL `explain`을 간접 호출하는 예제](sparql-explain-examples.md) 단원을 참조하십시오.

# Neptune에서 SPARQL `explain`을 간접 호출하는 예제
`explain` 예제

이 섹션의 예제는 Amazon Neptune에서 쿼리 실행을 분석하기 위해 SPARQL `explain` 기능을 간접적으로 호출하여 생성할 수 있는 다양한 종류의 출력을 보여줍니다.

**Topics**
+ [

## Explain 출력 이해
](#sparql-explain-example-output)
+ [

## 세부 모드 출력의 예제
](#sparql-explain-example-details)
+ [

## 정적 모드 출력의 예제
](#sparql-explain-example-static)
+ [

## 다양한 파라미터 인코딩 방법
](#sparql-explain-example-parameters)
+ [

## 텍스트/일반 이외의 기타 출력 유형
](#sparql-explain-output-options)
+ [

## DFE가 활성화된 경우의 SPARQL `explain` 출력 예제
](#sparql-explain-output-dfe)

## Explain 출력 이해
Explain 출력

이 예제에서 Jane Doe는 John Doe와 Richard Roe라는 두 사람을 알고 있습니다.

```
@prefix ex: <http://example.com> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

ex:JaneDoe foaf:knows ex:JohnDoe .
ex:JohnDoe foaf:firstName "John" .
ex:JohnDoe foaf:lastName "Doe" .
ex:JaneDoe foaf:knows ex:RichardRoe .
ex:RichardRoe foaf:firstName "Richard" .
ex:RichardRoe foaf:lastName "Roe" .
.
```

Jane Doe가 알고 있는 모든 사람들의 성을 확인하기 위해 다음 쿼리를 작성할 수 있습니다.

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -H "Accept: text/csv"
```

이 간단한 쿼리는 다음을 반환합니다.

```
firstName
John
Richard
```

그런 다음 `-d "explain=dynamic"`을 추가하고 `text/csv` 대신 기본 출력 유형을 사용하여 `explain`을 호출하도록 `curl` 명령을 변경합니다.

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -d "explain=dynamic"
```

이제 쿼리에서 출력이 기본 출력 유형인 가독성 좋게 꾸민 ASCII 형식(HTTP 콘텐츠 유형 `text/plain`)으로 반환됩니다.

```
╔════╤════════╤════════╤═══════════════════╤═══════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ 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      │ -      │ PipelineJoin      │ pattern=distinct(ex:JaneDoe, foaf:knows, ?person)     │ -        │ 1        │ 2         │ 2.00  │ 1         ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person]                          │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ PipelineJoin      │ pattern=distinct(?person, foaf:firstName, ?firstName) │ -        │ 2        │ 2         │ 1.00  │ 1         ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person, ?firstName]              │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ Projection        │ vars=[?firstName]                                     │ retain   │ 2        │ 2         │ 1.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ -      │ -      │ TermResolution    │ vars=[?firstName]                                     │ id2value │ 2        │ 2         │ 1.00  │ 1         ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

`Name` 열의 작업과 해당 인수에 대한 자세한 내용은 [EXPLAIN 연산자](sparql-explain-operators.md) 단원을 참조하십시오.

다음은 각 행의 출력에 대한 설명입니다.

1. 기본 쿼리의 첫 번째 단계에서는 항상 `SolutionInjection` 연산자를 사용하여 솔루션을 삽입합니다. 그런 다음 이 솔루션이 평가 프로세스를 통해 최종 결과로 확장됩니다.

   이 예제의 경우 범용 솔루션이라는 `{ }`를 삽입합니다. `VALUES` 절 또는 `BIND`가 있는 경우 이 단계에서는 시작할 더 복잡한 변수 바인딩을 삽입할 수도 있습니다.

   `Units Out` 열은 이 단일 솔루션이 연산자에서 유출됨을 나타냅니다. `Out #1` 열은 이 연산자가 결과를 제공할 연산자를 지정합니다. 이 예제의 경우 모든 연산자가 테이블에 있는 연산자에 연결되어 있습니다.

1. 두 번째 단계는 `PipelineJoin`입니다. 이전 연산자로부터 생성된 단일 범용(완전히 제약되지 않음) 솔루션이 입력으로 제공됩니다(`Units In := 1`). 이 솔루션을 `pattern` 인수로 정의된 튜플 패턴에 조인합니다. 이는 패턴에 대한 간단한 조회에 해당합니다. 이 경우 트리플 패턴은 다음과 같이 정의됩니다.

   ```
   distinct( ex:JaneDoe, foaf:knows, ?person )
   ```

   `joinType := join` 인수는 이 작업이 정상 조인임을 나타냅니다(다른 유형에는 `optional` 조인, `existence check` 조인 등이 포함됨).

   `distinct := true` 인수는 데이터베이스에서 완전히 구분되는 일치 항목만 추출하며(중복되지 않음), 구분되는 일치 항목을 중복되지 않은 변수 `joinProjectionVars := ?person`에 바인딩함을 나타냅니다.

   `Units Out` 열 값이 2이면 두 개의 솔루션이 유출됨을 나타냅니다. 특히 `?person` 변수에 대한 바인딩이며, Jane Doe가 알고 있음을 데이터가 보여주는 두 명의 사람들을 반영합니다.

   ```
    ?person
    -------------
    ex:JohnDoe
    ex:RichardRoe
   ```

1. 2단계의 두 솔루션은 입력(`Units In := 2`)으로 두 번째 `PipelineJoin`으로 이동합니다. 이 연산자는 이전의 두 솔루션을 다음 트리플 패턴에 조인합니다.

   ```
   distinct(?person, foaf:firstName, ?firstName)
   ```

   `?person` 변수는 연산자의 수신 솔루션을 기준으로 `ex:JohnDoe` 또는 `ex:RichardRoe`에 바인딩된다고 알려져 있습니다. 따라서, `PipelineJoin`은 이름인 John과 Richard를 추출합니다. 두 개의 발신 솔루션(Units Out := 2)은 다음과 같습니다.

   ```
    ?person       | ?firstName
    ---------------------------
    ex:JohnDoe    | John
    ex:RichardRoe | Richard
   ```

1. 다음 프로젝션 연산자는 3단계에서 두 솔루션을 입력으로 가져와(`Units In := 2`) `?firstName` 변수에 프로젝션합니다. 이렇게 하면 매핑의 다른 모든 변수 바인딩이 제거되며, 두 바인딩에 전달됩니다(`Units Out := 2`).

   ```
    ?firstName
    ----------
    John
    Richard
   ```

1. 성능을 개선하기 위해 Neptune은 가능한 경우 문자열 자체가 아니라 URI 및 문자열 리터럴 등의 조건에 할당하는 내부 식별자에 작동합니다. 최종 연산자 `TermResolution`은 이러한 내부 식별자의 매핑을 해당하는 조건 문자열로 다시 수행합니다.

   일반(비 Explain) 쿼리 평가의 경우 최종 연산자에 의해 계산된 결과는 요청된 직렬화 형식으로 직렬화되고 클라이언트로 스트리밍됩니다.

## 세부 모드 출력의 예제
세부 모드 출력

*동적* 모드 대신 *세부* 모드에서 이전 쿼리와 동일한 쿼리를 실행한다고 가정해 보겠습니다.

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -d "explain=details"
```

이 예에서 볼 수 있듯이 출력은 출력 맨 위의 쿼리 문자열 및 `PipelineJoin` 연산자의 `patternEstimate` 개수와 같은 몇 가지 추가 세부 정보와 동일합니다.

```
Query:
PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/>
SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }

╔════╤════════╤════════╤═══════════════════╤═══════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ 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      │ -      │ PipelineJoin      │ pattern=distinct(ex:JaneDoe, foaf:knows, ?person)     │ -        │ 1        │ 2         │ 2.00  │ 13        ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person]                          │          │          │           │       │           ║
║    │        │        │                   │ patternEstimate=2                                     │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ PipelineJoin      │ pattern=distinct(?person, foaf:firstName, ?firstName) │ -        │ 2        │ 2         │ 1.00  │ 3         ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person, ?firstName]              │          │          │           │       │           ║
║    │        │        │                   │ patternEstimate=2                                     │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ Projection        │ vars=[?firstName]                                     │ retain   │ 2        │ 2         │ 1.00  │ 1         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ -      │ -      │ TermResolution    │ vars=[?firstName]                                     │ id2value │ 2        │ 2         │ 1.00  │ 7         ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

## 정적 모드 출력의 예제
정적 모드 출력

*세부* 모드 대신 *정적* 모드(기본값)에서 이전 쿼리와 동일한 쿼리를 실행한다고 가정해 보겠습니다.

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -d "explain=static"
```

이 예제에 표시된 대로 출력은 동일하지만, 마지막 3개의 열이 생략됩니다.

```
╔════╤════════╤════════╤═══════════════════╤═══════════════════════════════════════════════════════╤══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments                                             │ Mode     ║
╠════╪════════╪════════╪═══════════════════╪═══════════════════════════════════════════════════════╪══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]                                        │ -        ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 1  │ 2      │ -      │ PipelineJoin      │ pattern=distinct(ex:JaneDoe, foaf:knows, ?person)     │ -        ║
║    │        │        │                   │ joinType=join                                         │          ║
║    │        │        │                   │ joinProjectionVars=[?person]                          │          ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 2  │ 3      │ -      │ PipelineJoin      │ pattern=distinct(?person, foaf:firstName, ?firstName) │ -        ║
║    │        │        │                   │ joinType=join                                         │          ║
║    │        │        │                   │ joinProjectionVars=[?person, ?firstName]              │          ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 3  │ 4      │ -      │ Projection        │ vars=[?firstName]                                     │ retain   ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 4  │ -      │ -      │ TermResolution    │ vars=[?firstName]                                     │ id2value ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════════════════════════════════════╧══════════╝
```

## 다양한 파라미터 인코딩 방법
파라미터 인코딩

다음은 SPARQL `explain`을 호출할 때 파라미터를 인코딩하는 두 가지 방법을 보여주는 예제 쿼리입니다.

**URL 인코딩 사용** – 이 예제에서는 URL 파라미터 인코딩을 사용하고 *동적* 출력을 지정합니다.

```
curl -XGET "http(s)://your_server:your_port/sparql?query=SELECT%20*%20WHERE%20%7B%20%3Fs%20%3Fp%20%3Fo%20%7D%20LIMIT%20%31&explain=dynamic"
```

**직접 파라미터 지정** – 이는 이전 쿼리와 동일하지만, POST를 통해 직접 파라미터를 전달합니다.

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=SELECT * WHERE { ?s ?p ?o } LIMIT 1" \
   -d "explain=dynamic"
```

## 텍스트/일반 이외의 기타 출력 유형
기타 출력 유형

위 예제에서는 기본 `text/plain` 출력 유형을 사용합니다. Neptune은 SPARQL `explain` 출력을 다른 두 MIME 유형 형식, 즉 `text/csv` 및 `text/html`로 포맷할 수도 있습니다. HTTP `Accept` 헤더를 설정하여 해당 유형을 호출합니다. 다음과 같이 `curl`에서 `-H` 플래그를 사용하면 됩니다.

```
  -H "Accept: output type"
```

여기 몇 가지 예가 있습니다:

**`text/csv` 출력**  
이 쿼리는 `-H "Accept: text/csv"`를 지정하여 CSV MIME 유형을 호출합니다.

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=SELECT * WHERE { ?s ?p ?o } LIMIT 1" \
   -d "explain=dynamic" \
   -H "Accept: text/csv"
```

CSV 형식은 스프레드시트 또는 데이터베이스로 가져오기에 유용하며, 다음과 같이 각 `explain` 행의 필드를 세미콜론(`;`)으로 구분합니다.

```
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;-;PipelineJoin;pattern=distinct(?s, ?p, ?o),joinType=join,joinProjectionVars=[?s, ?p, ?o];-;1;6;6.00;1
2;3;-;Projection;vars=[?s, ?p, ?o];retain;6;6;1.00;2
3;-;-;Slice;limit=1;-;1;1;1.00;1
```

 

**`text/html` 출력**  
`-H "Accept: text/html"`을 지정할 경우 `explain`에서는 HTML 테이블을 생성합니다.

```
<!DOCTYPE html>
<html>
  <body>
    <table border="1px">
      <thead>
        <tr>
          <th>ID</th>
          <th>Out #1</th>
          <th>Out #2</th>
          <th>Name</th>
          <th>Arguments</th>
          <th>Mode</th>
          <th>Units In</th>
          <th>Units Out</th>
          <th>Ratio</th>
          <th>Time (ms)</th>
        </tr>
      </thead>

      <tbody>
        <tr>
          <td>0</td>
          <td>1</td>
          <td>-</td>
          <td>SolutionInjection</td>
          <td>solutions=[{}]</td>
          <td>-</td>
          <td>0</td>
          <td>1</td>
          <td>0.00</td>
          <td>0</td>
        </tr>

        <tr>
          <td>1</td>
          <td>2</td>
          <td>-</td>
          <td>PipelineJoin</td>
          <td>pattern=distinct(?s, ?p, ?o)<br>
              joinType=join<br>
              joinProjectionVars=[?s, ?p, ?o]</td>
          <td>-</td>
          <td>1</td>
          <td>6</td>
          <td>6.00</td>
          <td>1</td>
        </tr>

        <tr>
          <td>2</td>
          <td>3</td>
          <td>-</td>
          <td>Projection</td>
          <td>vars=[?s, ?p, ?o]</td>
          <td>retain</td>
          <td>6</td>
          <td>6</td>
          <td>1.00</td>
          <td>2</td>
        </tr>

        <tr>
          <td>3</td>
          <td>-</td>
          <td>-</td>
          <td>Slice</td>
          <td>limit=1</td>
          <td>-</td>
          <td>1</td>
          <td>1</td>
          <td>1.00</td>
          <td>1</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>
```

HTML은 다음과 같이 브라우저에서 렌더링됩니다.

![\[SPARQL Explain HTML 출력의 샘플\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/images/sparql-explain-dynamic-html-output.png)


## DFE가 활성화된 경우의 SPARQL `explain` 출력 예제
DFE가 활성화된 경우의 예제

다음은 Neptune DFE 대체 쿼리 엔진이 활성화된 경우의 SPARQL `explain` 출력 예제입니다.

```
╔════╤════════╤════════╤═══════════════════╤═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ 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      │ -      │ HashIndexBuild    │ solutionSet=solutionSet1                                                                                                                                                                                                │ -        │ 1        │ 1         │ 1.00  │ 22        ║
║    │        │        │                   │ joinVars=[]                                                                                                                                                                                                             │          │          │           │       │           ║
║    │        │        │                   │ sourceType=pipeline                                                                                                                                                                                                     │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFENode           │ DFE Stats=                                                                                                                                                                                                                    │ -        │ 101      │ 100       │ 0.99  │ 32        ║
║    │        │        │                   │ ====> DFE execution time (measured by DFEQueryEngine)                                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ accepted [micros]=127                                                                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ ready [micros]=2                                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │ running [micros]=5627                                                                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ finished [micros]=0                                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> DFE execution time (measured in DFENode)                                                                                                                                                                           │          │          │           │       │           ║
║    │        │        │                   │ -> setupTime [ms]=1                                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │ -> executionTime [ms]=14                                                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │ -> resultReadTime [ms]=0                                                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> Static analysis statistics                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ --> 35907 micros spent in parser.                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ --> 7643 micros spent in range count estimation                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ --> 2895 micros spent in value resolution                                                                                                                                                                               │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ --> 39974925 micros spent in optimizer loop                                                                                                                                                                             │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ DFEJoinGroupNode[ children={                                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │   DFEPatternNode[(?1, TERM[117442062], ?2, ?3) . project DISTINCT[?1, ?2] {rangeCountEstimate=100},                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │     OperatorInfoWithAlternative[                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │       rec=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_PIPELINE_JOIN,                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0002,comp=0.0000,mem=0],                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0002,comp=0.0000,mem=0]]],                                                                                                             │          │          │           │       │           ║
║    │        │        │                   │       alt=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_HASH_JOIN,                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0003,comp=0.0000,mem=3212],                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0003,comp=0.0000,mem=3212]]]]],                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │   DFEPatternNode[(?1, TERM[150997262], ?4, ?5) . project DISTINCT[?1, ?4] {rangeCountEstimate=100},                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │     OperatorInfoWithAlternative[                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │       rec=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_HASH_JOIN,                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0003,comp=0.0000,mem=6400],                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0003,comp=0.0000,mem=6400]]],                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │       alt=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_PIPELINE_JOIN,                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0010,comp=0.0000,mem=0],                                                                                                                      │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0010,comp=0.0000,mem=0]]]]]                                                                                                          │          │          │           │       │           ║
║    │        │        │                   │ },                                                                                                                                                                                                                      │          │          │           │       │           ║
║    │        │        │                   │ ]                                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> DFE configuration:                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │ solutionChunkSize=5000                                                                                                                                                                                                  │          │          │           │       │           ║
║    │        │        │                   │ ouputQueueSize=20                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ numComputeCores=3                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ maxParallelIO=10                                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │ numInitialPermits=12                                                                                                                                                                                                    │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ====> DFE configuration (reported back)                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │ numComputeCores=3                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ maxParallelIO=2                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ numInitialPermits=12                                                                                                                                                                                                    │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> Statistics & operator histogram                                                                                                                                                                                    │          │          │           │       │           ║
║    │        │        │                   │ ==> Statistics                                                                                                                                                                                                          │          │          │           │       │           ║
║    │        │        │                   │ -> 3741 / 3668 micros total elapsed (incl. wait / excl. wait)                                                                                                                                                           │          │          │           │       │           ║
║    │        │        │                   │ -> 3741 / 3 millis total elapse (incl. wait / excl. wait)                                                                                                                                                               │          │          │           │       │           ║
║    │        │        │                   │ -> 3741 / 0 secs total elapsed (incl. wait / excl. wait)                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │ ==> Operator histogram                                                                                                                                                                                                  │          │          │           │       │           ║
║    │        │        │                   │ -> 47.66% of total time (excl. wait): pipelineScan (2 instances)                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │ -> 10.99% of total time (excl. wait): merge (1 instances)                                                                                                                                                               │          │          │           │       │           ║
║    │        │        │                   │ -> 41.17% of total time (excl. wait): symmetricHashJoin (1 instances)                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ -> 0.19% of total time (excl. wait): drain (1 instances)                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ nodeId | out0   | out1 | opName            | args                                             | rowsIn | rowsOut | chunksIn | chunksOut | elapsed* | outWait | outBlocked | ratio    | rate* [M/s] | rate [M/s] | %     │          │          │           │       │           ║
║    │        │        │                   │ ------ | ------ | ---- | ----------------- | ------------------------------------------------ | ------ | ------- | -------- | --------- | -------- | ------- | ---------- | -------- | ----------- | ---------- | ----- │          │          │           │       │           ║
║    │        │        │                   │ node_0 | node_2 | -    | pipelineScan      | (?1, TERM[117442062], ?2, ?3) DISTINCT [?1, ?2]  | 0      | 100     | 0        | 1         | 874      | 0       | 0          | Infinity | 0.1144      | 0.1144     | 23.83 │          │          │           │       │           ║
║    │        │        │                   │ node_1 | node_2 | -    | pipelineScan      | (?1, TERM[150997262], ?4, ?5) DISTINCT [?1, ?4]  | 0      | 100     | 0        | 1         | 874      | 0       | 0          | Infinity | 0.1144      | 0.1144     | 23.83 │          │          │           │       │           ║
║    │        │        │                   │ node_2 | node_4 | -    | symmetricHashJoin |                                                  | 200    | 100     | 2        | 2         | 1510     | 73      | 0          | 0.50     | 0.0662      | 0.0632     | 41.17 │          │          │           │       │           ║
║    │        │        │                   │ node_3 | -      | -    | drain             |                                                  | 100    | 0       | 1        | 0         | 7        | 0       | 0          | 0.00     | 0.0000      | 0.0000     | 0.19  │          │          │           │       │           ║
║    │        │        │                   │ node_4 | node_3 | -    | merge             |                                                  | 100    | 100     | 2        | 1         | 403      | 0       | 0          | 1.00     | 0.2481      | 0.2481     | 10.99 │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ HashIndexJoin     │ solutionSet=solutionSet1                                                                                                                                                                                                │ -        │ 100      │ 100       │ 1.00  │ 4         ║
║    │        │        │                   │ joinType=join                                                                                                                                                                                                           │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ Distinct          │ vars=[?s, ?o, ?o1]                                                                                                                                                                                                      │ -        │ 100      │ 100       │ 1.00  │ 9         ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ -      │ Projection        │ vars=[?s, ?o, ?o1]                                                                                                                                                                                                      │ retain   │ 100      │ 100       │ 1.00  │ 2         ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ -      │ -      │ TermResolution    │ vars=[?s, ?o, ?o1]                                                                                                                                                                                                      │ id2value │ 100      │ 100       │ 1.00  │ 11        ║
╚════╧════════╧════════╧═══════════════════╧═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

# Neptune SPARQL `explain` 연산자
EXPLAIN 연산자

다음 섹션에서는 현재 Amazon Neptune에서 사용할 수 있는 SPARQL `explain` 기능의 연산자와 파라미터에 대해 설명합니다.

**중요**  
SPARQL `explain` 기능은 계속 개선되고 있습니다. 여기에 설명된 연산자와 파라미터는 향후 버전에서 변경될 수 있습니다.

**Topics**
+ [

## `Aggregation` 연산자
](#sparql-explain-operator-aggregation)
+ [

## `ConditionalRouting` 연산자
](#sparql-explain-operator-conditional-routing)
+ [

## `Copy` 연산자
](#sparql-explain-operator-copy)
+ [

## `DFENode` 연산자
](#sparql-explain-operator-dfenode)
+ [

## `Distinct` 연산자
](#sparql-explain-operator-distinct)
+ [

## `Federation` 연산자
](#sparql-explain-operator-federation)
+ [

## `Filter` 연산자
](#sparql-explain-operator-filter)
+ [

## `HashIndexBuild` 연산자
](#sparql-explain-operator-hash-index-build)
+ [

## `HashIndexJoin` 연산자
](#sparql-explain-operator-hash-index-join)
+ [

## `MergeJoin` 연산자
](#sparql-explain-operator-merge-join)
+ [

## `NamedSubquery` 연산자
](#sparql-explain-operator-named-subquery)
+ [

## `PipelineJoin` 연산자
](#sparql-explain-operator-pipeline-join)
+ [

## `PipelineCountJoin` 연산자
](#sparql-explain-operator-pipeline-count-join)
+ [

## `PipelinedHashIndexJoin` 연산자
](#sparql-explain-operator-pipeline-hash-index-join)
+ [

## `Projection` 연산자
](#sparql-explain-operator-projection)
+ [

## `PropertyPath` 연산자
](#sparql-explain-operator-property-path)
+ [

## `TermResolution` 연산자
](#sparql-explain-operator-term-resolution)
+ [

## `Slice` 연산자
](#sparql-explain-operator-slice)
+ [

## `SolutionInjection` 연산자
](#sparql-explain-operator-solution-injection)
+ [

## `Sort` 연산자
](#sparql-explain-operator-sort)
+ [

## `VariableAlignment` 연산자
](#sparql-explain-operator-variable-alignment)

## `Aggregation` 연산자
`Aggregation`

하나 이상의 집계를 수행하며, `count`, `max`, `min`, `sum` 등 SPARQL 집계 연산자의 의미 체계를 구현합니다.

`Aggregation`은 `groupBy` 절을 통한 선택적 그룹화 및 선택적 `having` 제약 조건과 함께 제공됩니다.

**인수**
+ `groupBy` – (*선택 사항*) 그룹화되는 수신 솔루션에 따라 표현식의 시퀀스를 지정하는 `groupBy` 절을 제공합니다.
+ `aggregates` – (*필수*) 집계 표현식의 순서가 지정된 목록을 지정합니다.
+ `having` – (*선택 사항*) SPARQL 쿼리의 `having` 절이 암시하는 대로 그룹의 필터에 제약 조건을 추가합니다.

## `ConditionalRouting` 연산자
`ConditionalRouting`

지정된 조건에 따라 수신 솔루션을 라우팅합니다. 조건을 충족하는 솔루션은 `Out #1`에서 참조하는 연산자 ID로 라우팅되지만, 충족하지 않는 솔루션은 `Out #2`에서 참조하는 연산자로 라우팅됩니다.

**인수**
+ `condition` – (*필수*) 라우팅 조건입니다.

## `Copy` 연산자
`Copy`

지정된 모드에서 지정한 대로 솔루션 스트림을 위임합니다.

**Modes**
+ `forward` – `Out #1`에서 식별된 다운스트림 연산자로 솔루션을 전달합니다.
+ `duplicate` – 솔루션을 복제하고 각각 `Out #1` 및 `Out #2`에서 식별된 두 연산자로 전달합니다.

`Copy`에는 인수가 없습니다.

## `DFENode` 연산자
`DFENode`

이 연산자는 DFE 대체 쿼리 엔진에서 실행되는 계획을 추상화한 것입니다. 자세한 DFE 계획은 이 연산자의 인수에 요약되어 있습니다. 현재 인수는 DFE 계획의 자세한 런타임 통계를 포함하도록 오버로드되었습니다. 여기에는 DFE가 쿼리를 실행하는 다양한 단계에 소요된 시간이 포함됩니다.

DFE 쿼리 계획에 대해 논리적으로 최적화된 추상 구문 트리(AST)에는 계획 시 고려했던 연산자 유형 정보와 연산자를 실행하는 데 드는 관련 최적/최악의 경우에 해당하는 비용 정보가 출력되어 있습니다. AST는 현재 다음과 같은 유형의 노드로 구성되어 있습니다.
+ `DFEJoinGroupNode`   –   하나 이상의 `DFEPatternNodes` 조인을 나타냅니다.
+ `DFEPatternNode`   –   일치하는 튜플을 기본 데이터베이스 밖으로 투영하는 데 사용하는 기본 패턴을 캡슐화합니다.

하위 섹션 `Statistics & Operator histogram`에는 `DataflowOp` 계획의 실행 시간 및 각 연산자가 사용한 CPU 시간 분류에 대한 세부 정보가 포함되어 있습니다. 이 아래에는 DFE에서 실행한 계획의 상세한 런타임 통계를 출력하는 표가 있습니다.

**참고**  
DFE는 랩 모드에서 출시된 실험용 기능이므로, 정확한 `explain` 출력 형식이 변경될 수 있습니다.

## `Distinct` 연산자
`Distinct`

변수의 하위 집합에서 개별 프로젝션을 컴퓨팅하여 중복을 제거합니다. 따라서 유입되는 솔루션의 수는 유출되는 솔루션의 수보다 크거나 같습니다.

**인수**
+ `vars` – (*필수*) `Distinct` 프로젝션을 적용할 변수입니다.

## `Federation` 연산자
연동

지정된 원격 SPARQL 엔드포인트에 지정된 쿼리를 전달합니다.

**인수**
+ `endpoint` – (*필수*) SPARQL `SERVICE` 문의 엔드포인트 URL입니다. 이는 상수 문자열일 수 있고, 동일한 쿼리 내의 변수를 토대로 쿼리 엔드포인트가 결정된 경우에는 변수 이름일 수도 있습니다.
+ `query` – (*필수*) 원격 엔드포인트로 전송되는 재구성된 쿼리 문자열입니다. 클라이언트가 지정하지 않더라도 엔진은 이 쿼리에 기본 접두사를 추가합니다.
+ `silent` – (*필수*) 해당 키워드 뒤에 `SILENT` 키워드가 나타났는지 여부를 표시하는 부울 값입니다. `SILENT`는 원격 `SERVICE` 부분이 실패하더라도 전체 쿼리가 실패하지 않도록 엔진에 명령합니다.

## `Filter` 연산자
`Filter`

필터는 수신 솔루션입니다. 필터 조건을 충족하는 솔루션만 업스트림 연산자로 전달되며, 다른 모든 솔루션은 삭제됩니다.

**인수**
+ `condition` – (*필수*) 필터 조건입니다.

## `HashIndexBuild` 연산자
`HashIndexBuild`

바인딩 목록을 가져와 `solutionSet` 인수로 정의된 이름이 있는 해시 인덱스에 스풀링합니다. 일반적으로 후속 연산자는 이 솔루션 집합에 대해 조인을 수행하며 해당 이름으로 참조합니다.

**인수**
+ `solutionSet` – (*필수*) 해시 인덱스 솔루션 세트의 이름입니다.
+ `sourceType` – (*필수*) 해시 인덱스에 저장할 바인딩을 가져올 소스의 유형입니다.
  + `pipeline` – 연산자 파이프라인의 다운스트림 연산자에서 해시 인덱스로 수신 솔루션을 스풀링합니다.
  + `binding set` – 인수로 지정된 고정 바인딩 세트를 해시 인덱스로 스풀링합니다.
+ `sourceBindingSet` – (*선택 사항*) `sourceType` 인수 값이 `binding set`인 경우 이 인수는 해시 인덱스로 스풀링할 정적 바인딩 세트를 지정합니다.

## `HashIndexJoin` 연산자
`HashIndexJoin`

수신 솔루션을 `solutionSet` 인수로 식별된 해시 인덱스 솔루션 집합에 조인합니다.

**인수**
+ `solutionSet` – (*필수*) 조인할 솔루션 세트의 이름입니다. 이 이름은 이전 단계에서 `HashIndexBuild` 연산자를 사용하여 구성된 해시 인덱스여야 합니다.
+ `joinType` – (*필수*) 수행할 조인 유형입니다.
  + `join` – 정상 조인이며, 공유된 모든 변수 간 정확하게 일치해야 합니다.
  + `optional` - `optional` 조인으로, SPARQL `OPTIONAL` 연산자 의미 시맨틱을 사용합니다.
  + `minus` – `minus` 작업은 조인 파트너가 없는 매핑을 유지하며, SPARQL `MINUS` 연산자 의미 시맨틱을 사용합니다.
  + `existence check` – 조인 파트너가 있는지 여부를 확인하며, `existenceCheckResultVar` 변수를 이 확인 결과에 바인딩합니다.
+ `constraints` – (*선택 사항*) 조인 중 고려되는 추가 조인 제약 조건입니다. 이러한 제약 조건을 충족하지 않는 조인은 삭제됩니다.
+ `existenceCheckResultVar` – (*선택 사항*) `joinType`이 `existence check`와 동일한 조인에 대해서만 사용됩니다(이전 `joinType` 인수 참조).

## `MergeJoin` 연산자
`MergeJoin`

`solutionSets` 인수로 식별된 대로 여러 솔루션 집합에 대한 병합 조인입니다.

**인수**
+ `solutionSets` – (*필수*) 함께 조인할 솔루션 세트입니다.

## `NamedSubquery` 연산자
`NamedSubquery`

`subQuery` 인수로 식별된 하위 쿼리의 평가를 트리거하고 결과를 `solutionSet` 인수로 지정된 솔루션 집합에 스풀링합니다. 연산자에 대한 수신 솔루션은 하위 쿼리로 전달된 후 다음 연산자로 전달됩니다.

**인수**
+ `subQuery` – (*필수*) 평가할 하위 쿼리의 이름입니다. 하위 쿼리는 명시적으로 출력에 렌더링됩니다.
+ `solutionSet` – (*필수*) 하위 쿼리 결과를 저장할 솔루션 세트의 이름입니다.

## `PipelineJoin` 연산자
`PipelineJoin`

이전 연산자의 출력을 입력으로 받으며, `pattern` 인수로 정의된 튜플 패턴에 조인합니다.

**인수**
+ `pattern` – (*필수*) subject-predicate-object 형식이며, 경우에 따라 조인의 기반이 되는 -graph 튜플입니다. 패턴에 대해 `distinct`가 지정되면 조인은 일치하는 모든 솔루션이 아닌 `projectionVars` 인수로 지정된 프로젝션 변수의 개별 솔루션만 추출합니다.
+ `inlineFilters` – (*선택 사항*) 패턴의 변수에 적용할 필터 세트입니다. 패턴은 이러한 필터와 함께 평가됩니다.
+ `joinType` – (*필수*) 수행할 조인 유형입니다.
  + `join` – 정상 조인이며, 공유된 모든 변수 간 정확하게 일치해야 합니다.
  + `optional` - `optional` 조인으로, SPARQL `OPTIONAL` 연산자 의미 시맨틱을 사용합니다.
  + `minus` – `minus` 작업은 조인 파트너가 없는 매핑을 유지하며, SPARQL `MINUS` 연산자 의미 시맨틱을 사용합니다.
  + `existence check` – 조인 파트너가 있는지 여부를 확인하며, `existenceCheckResultVar` 변수를 이 확인 결과에 바인딩합니다.
+ `constraints` – (*선택 사항*) 조인 중 고려되는 추가 조인 제약 조건입니다. 이러한 제약 조건을 충족하지 않는 조인은 삭제됩니다.
+ `projectionVars` – (*선택 사항*) 프로젝션 변수입니다. `distinct := true`와 함께 사용되어 지정된 변수 집합에 대해 개별 프로젝션을 추출합니다.
+ `cutoffLimit` – (*선택 사항*) 추출된 조인 파트너의 수에 대한 차단 제한입니다. 기본적으로는 제한이 없지만, `FILTER (NOT) EXISTS` 절을 구현하기 위해 조인을 수행할 때 이 값을 1로 설정할 수 있습니다. 1은 조인 파트너가 있음을 증명하거나 반증하기에 충분합니다.

## `PipelineCountJoin` 연산자
`PipelineCountJoin`

`PipelineJoin`의 변형입니다. 조인하는 대신 일치하는 조인 파트너를 계수하고 해당 개수를 `countVar` 인수로 지정된 변수에 바인딩합니다.

**인수**
+ `countVar` – (*필수*) 주로 조인 파트너의 수인 개수 결과를 바인딩할 변수입니다.
+ `pattern` – (*필수*) subject-predicate-object 형식이며, 경우에 따라 조인의 기반이 되는 -graph 튜플입니다. 패턴에 대해 `distinct`가 지정되면 조인은 일치하는 모든 솔루션이 아닌 `projectionVars` 인수로 지정된 프로젝션 변수의 개별 솔루션만 추출합니다.
+ `inlineFilters` – (*선택 사항*) 패턴의 변수에 적용할 필터 세트입니다. 패턴은 이러한 필터와 함께 평가됩니다.
+ `joinType` – (*필수*) 수행할 조인 유형입니다.
  + `join` – 정상 조인이며, 공유된 모든 변수 간 정확하게 일치해야 합니다.
  + `optional` - `optional` 조인으로, SPARQL `OPTIONAL` 연산자 의미 시맨틱을 사용합니다.
  + `minus` – `minus` 작업은 조인 파트너가 없는 매핑을 유지하며, SPARQL `MINUS` 연산자 의미 시맨틱을 사용합니다.
  + `existence check` – 조인 파트너가 있는지 여부를 확인하며, `existenceCheckResultVar` 변수를 이 확인 결과에 바인딩합니다.
+ `constraints` – (*선택 사항*) 조인 중 고려되는 추가 조인 제약 조건입니다. 이러한 제약 조건을 충족하지 않는 조인은 삭제됩니다.
+ `projectionVars` – (*선택 사항*) 프로젝션 변수입니다. `distinct := true`와 함께 사용되어 지정된 변수 집합에 대해 개별 프로젝션을 추출합니다.
+ `cutoffLimit` – (*선택 사항*) 추출된 조인 파트너의 수에 대한 차단 제한입니다. 기본적으로는 제한이 없지만, `FILTER (NOT) EXISTS` 절을 구현하기 위해 조인을 수행할 때 이 값을 1로 설정할 수 있습니다. 1은 조인 파트너가 있음을 증명하거나 반증하기에 충분합니다.

## `PipelinedHashIndexJoin` 연산자
`PipelinedHashIndexJoin`

이는 올인원 빌드 해시 인덱스 및 조인 연산자입니다. 바인딩 목록을 가져와 해시 인덱스로 스풀링한 후 수신 솔루션을 해시 인덱스에 조인합니다.

**인수**
+ `sourceType` – (*필수*) 해시 인덱스에 저장할 바인딩을 가져올 소스의 유형으로, 다음 중 하나입니다.
  + `pipeline`  –   `PipelinedHashIndexJoin`이 연산자 파이프라인의 다운스트림 연산자에서 해시 인덱스로 수신 솔루션을 스풀링하도록 합니다.
  + `binding set`  –   `PipelinedHashIndexJoin`이 `sourceBindingSet` 인수로 지정된 고정 바인딩 세트를 해시 인덱스로 스풀링하도록 합니다.
+ `sourceSubQuery `   –   (*선택 사항*) `sourceType` 인수 값이 `pipeline`인 경우 이 인수는 평가되어 해시 인덱스로 스풀링되는 하위 쿼리를 지정합니다.
+ `sourceBindingSet `   –   (*선택 사항*) `sourceType` 인수 값이 `binding set`인 경우 이 인수는 해시 인덱스로 스풀링할 정적 바인딩 세트를 지정합니다.
+ `joinType` – (*필수*) 수행할 조인 유형입니다.
  + `join` – 정상 조인이며, 공유된 모든 변수 간 정확하게 일치해야 합니다.
  + `optional` - `optional` 조인으로, SPARQL `OPTIONAL` 연산자 의미 시맨틱을 사용합니다.
  + `minus` – `minus` 작업은 조인 파트너가 없는 매핑을 유지하며, SPARQL `MINUS` 연산자 의미 시맨틱을 사용합니다.
  + `existence check` – 조인 파트너가 있는지 여부를 확인하며, `existenceCheckResultVar` 변수를 이 확인 결과에 바인딩합니다.
+ `existenceCheckResultVar`   –   (*선택 사항*) `joinType`이 `existence check`와 동일한 조인에 대해서만 사용됩니다(이전 joinType 인수 참조).

## `Projection` 연산자
`Projection`

변수의 하위 집합에 대해 프로젝션합니다. 유입되는 솔루션의 수는 유출되는 솔루션의 수와 같지만, 솔루션의 형태는 모드 설정에 따라 달라집니다.

**Modes**
+ `retain` – `vars` 인수로 지정된 변수만 솔루션에 유지합니다.
+ `drop` – `vars` 인수로 지정된 모든 변수를 삭제합니다.

**인수**
+ `vars` – (*필수*) 모드 설정에 따라 유지하거나 삭제할 변수입니다.

## `PropertyPath` 연산자
`PropertyPath`

`+` 또는 `*` 기호와 같은 재귀 속성 경로를 활성화합니다. Neptune은 `iterationTemplate` 인수로 지정된 템플릿을 기반으로 고정점 반복 방식을 구현합니다. 더 이상 새로운 솔루션을 찾을 수 없을 때까지 모든 고정점 반복에 대해 알려진 왼쪽 또는 오른쪽 변수가 템플릿에 바인딩됩니다.

**인수**
+ `iterationTemplate` – (*필수*) 고정점 반복을 구현하는 데 사용된 하위 쿼리 템플릿의 이름입니다.
+ `leftTerm` – (*필수*) 속성 경로의 왼쪽에 있는 조건(변수 또는 상수)입니다.
+ `rightTerm` – (*필수*) 속성 경로의 오른쪽에 있는 조건(변수 또는 상수)입니다.
+ `lowerBound` – (*필수*) 고정점 반복에 대한 하한값입니다(`*` 쿼리의 경우 `0` 또는 `+` 쿼리의 경우 `1`).

## `TermResolution` 연산자
`TermResolution`

모드에 따라 내부 문자열 식별자 값을 해당하는 외부 문자열로 변환하거나, 외부 문자열을 내부 문자열 식별자 값으로 변환합니다.

**Modes**
+ `value2id` – 리터럴 및 URI와 같은 조건을 해당하는 내부 ID 값에 매핑합니다(내부 값에 대한 인코딩).
+ `id2value` – 내부 ID 값을 리터럴 및 URI와 같은 해당 조건에 매핑합니다(내부 값의 디코딩).

**인수**
+ `vars` – (*필수*) 문자열 또는 내부 문자열 ID를 매핑해야 하는 변수를 지정합니다.

## `Slice` 연산자
`Slice`

SPARQL `LIMIT` 및 `OFFSET` 절의 의미 체계를 사용하여 수신 솔루션 스트림에 대해 조각을 구현합니다.

**인수**
+ `limit` – (*선택 사항*) 전달할 솔루션에 대한 제한입니다.
+ `offset` – (*선택 사항*) 전달하기 위해 솔루션을 평가할 오프셋입니다.

## `SolutionInjection` 연산자
`SolutionInjection`

입력을 받지 않습니다. 쿼리 계획에 솔루션을 정적으로 삽입하고 `solutions` 인수에 기록합니다.

쿼리 계획은 항상 이 정적 삽입으로 시작합니다. 정적 바인딩의 다양한 소스(예: `VALUES` 또는 `BIND` 절에서)를 조합하여 쿼리 자체에서 삽입할 정적 솔루션을 파생할 수 있는 경우 `SolutionInjection` 연산자는 이러한 파생된 정적 솔루션을 삽입합니다. 가장 간단한 경우에는 외부 `VALUES` 절에 의해 암시된 바인딩을 반영합니다.

쿼리에서 정적 솔루션을 파생할 수 없는 경우 `SolutionInjection`은 비어 있는 범용 솔루션을 삽입합니다. 이 솔루션은 쿼리 평가 프로세스 내내 확장되고 증가됩니다.

**인수**
+ `solutions` – (*필수*) 연산자에 의해 삽입된 솔루션의 시퀀스입니다.

## `Sort` 연산자
`Sort`

지정된 정렬 조건을 사용하여 솔루션 집합을 정렬합니다.

**인수**
+ `sortOrder` – (*필수*) 순서가 지정된 변수 목록이며, 각각 순차적으로 솔루션 세트를 정렬하는 데 사용되는 `ASC`(오름차순) 또는 `DESC`(내림차순) 식별자가 포함되어 있습니다.

## `VariableAlignment` 연산자
`VariableAlignment`

솔루션을 하나씩 검사하여 지정된 `sourceVar` 및 지정된 `targetVar` 변수에 대해 솔루션을 정렬합니다.

솔루션의 `sourceVar` 및 `targetVar`에 동일한 값이 있는 경우 변수가 정렬되었다고 간주되며, 중복 `sourceVar`가 프로젝션된 채로 솔루션이 전달됩니다.

변수가 서로 다른 값에 바인딩되면 솔루션이 전체적으로 필터링됩니다.

**인수**
+ `sourceVar` – (*필수*) 대상 변수와 비교할 소스 변수입니다. 솔루션에서 정렬된 경우(즉, 두 변수에 동일한 값이 있는 경우) 소스 변수가 프로젝션됩니다.
+ `targetVar` – (*필수*) 소스 변수와 비교되는 대상 변수입니다. 정렬된 경우에도 유지됩니다.

# Neptune에서 SPARQL `explain` 제한
`explain` 제한 사항

Neptune SPARQL `explain` 기능의 릴리스에 대한 제한은 다음과 같습니다.

**Neptune은 현재 SPARQL SELECT 쿼리에서만 Explain을 지원**  
`ASK`, `CONSTRUCT`, `DESCRIBE`, `SPARQL UPDATE` 쿼리 등 다른 쿼리 양식의 평가 프로세스에 대한 정보를 위해 해당 쿼리를 SELECT 쿼리로 변환할 수 있습니다. 그런 다음 `explain`을 사용하여 해당 SELECT 쿼리를 검사하면 됩니다.

예를 들어, `ASK WHERE {...}` 쿼리에 대한 `explain` 정보를 알아보려면 `SELECT WHERE {...} LIMIT 1` 쿼리를 `explain`과 함께 실행합니다.

마찬가지로, `CONSTRUCT {...} WHERE {...}` 쿼리의 경우 `CONSTRUCT {...}` 부분을 삭제하고 두 번째 `WHERE {...}` 절에서 `SELECT` 쿼리를 `explain`과 함께 실행합니다. 두 번째 `WHERE`에서 `CONSTRUCT` 템플릿으로 진행되는 솔루션에는 일반적으로 간단한 대체만 필요하므로 두 번째 `WHERE` 절을 평가하면 일반적으로 `CONSTRUCT` 쿼리 처리의 주요 문제점을 알 수 있습니다.

**향후 릴리스에서 Explain 연산자가 변경될 수 있음**  
SPARQL `explain` 연산자와 해당 파라미터가 향후 릴리스에서 변경될 수 있습니다.

**향후 릴리스에서 Explain 출력이 변경될 수 있음**  
예를 들어, 열 헤더가 변경될 수 있으며 테이블에 더 많은 열이 추가될 수 있습니다.

# `SERVICE` 확장을 사용하는 Neptune의 SPARQL 페더레이션된 쿼리
SPARQL `SERVICE` 확장

Amazon Neptune은 `SERVICE` 키워드를 사용하는 SPARQL 페더레이션된 쿼리 확장을 전적으로 지원합니다 (자세한 내용은 [SPARQL 1.1 연동 쿼리](https://www.w3.org/TR/sparql11-federated-query/) 참조).

`SERVICE` 키워드는 SPARQL 쿼리 엔진에게 원격 SPARQL 엔드포인트를 기준으로 쿼리의 일부분을 실행하여 최종 쿼리 결과를 구성하도록 지시합니다. `READ` 작업만 가능합니다. `WRITE` 및 `DELETE` 작업은 지원되지 않습니다. Neptune은 Virtual Private Cloud(VPC) 내에서 액세스할 수 있는 SPARQL 엔드포인트에 대해서만 페더레이션 쿼리를 실행할 수 있습니다. 그러나 VPC에서 리버스 프록시를 사용하여 VPC 내에서 외부 데이터 소스에 액세스할 수 있게 하는 것도 가능합니다.

**참고**  
SPARQL `SERVICE`를 사용하여 동일한 VPC에 있는 2개 이상의 Neptune 클러스터에 쿼리를 페더레이션하는 경우 모든 Neptune 클러스터가 서로 통신할 수 있도록 보안 그룹을 구성해야 합니다.

**중요**  
SPARQL 1.1 Federation은 외부 SPARQL 엔드포인트에 쿼리 및 파라미터를 전달할 때 사용자를 대신해 서비스 요청을 수행합니다. 외부 SPARQL 엔드포인트가 애플리케이션의 데이터 처리 및 보안 요구 사항을 충족하는지 확인하는 것은 사용자의 책임입니다.

## Neptune 페더레이션 쿼리의 예제
간단한 예제

다음은 SPARQL 연동 쿼리가 어떻게 작동하는지 보여주는 간단한 예제입니다.

고객이 `http://neptune-1:8182/sparql`에서 *Neptune-1*에 다음과 같은 쿼리를 보낸다고 가정해 보겠습니다.

```
SELECT * WHERE {
   ?person rdf:type foaf:Person .
   SERVICE <http://neptune-2:8182/sparql> {
       ?person foaf:knows ?friend .
    }
}
```

1. *Neptune-1*은 첫 번째 쿼리 패턴(*Q-1*)인 `?person rdf:type foaf:Person`을 평가하고 결과를 바탕으로 *Q-2*(`?person foaf:knows ?friend`)에서 `?person`을 해결한 다음, 결과 패턴을 `http://neptune-2:8182/sparql`의 *Neptune-2*에 전달합니다.

1. *Neptune-2*는 *Q-2*를 평가하고 그 결과를 *Neptune-1*에 다시 전송합니다.

1. *Neptune-1*은 두 패턴 모두에 대한 솔루션을 조인하여 결과를 고객에게 다시 전송합니다.

이 흐름은 다음 다이어그램에 나와 있습니다.

![\[평가 중인 SPARQL 연동 쿼리 패턴과 클라이언트에 다시 전송된 응답을 보여주는 흐름 다이어그램.\]](http://docs.aws.amazon.com/ko_kr/neptune/latest/userguide/images/federated.png)


**참고**  
기본적으로 옵티마이저는 `SERVICE` 명령이 실행되는 쿼리 실행 시점을 결정합니다. [joinOrder](sparql-query-hints-joinOrder.md) 쿼리 힌트를 사용하여 이 배치를 재정의할 수 있습니다.

## Neptune의 페더레이션 쿼리에 대한 액세스 제어
액세스 통제

Neptune은 인증 및 권한 부여에 AWS Identity and Access Management (IAM)을 사용합니다. 페더레이션 쿼리에 대한 액세스 제어에는 Neptune DB 인스턴스가 하나 이상 관여할 수 있습니다. 이들 인스턴스는 액세스 제어에 대한 요구 사항이 서로 다를 수 있습니다. 어떤 경우에는 이로 인해 연동 쿼리를 구성하는 능력이 제한될 수 있습니다.

이전 섹션에 제시된 간단한 예를 생각해 봅시다. *Neptune-1*은 호출에 사용한 것과 동일한 보안 인증 정보로 *Neptune-2*를 호출합니다.
+ *Neptune-1*에는 IAM 인증 및 권한 부여가 필요한데 *Neptune-2*에는 필요하지 않은 경우, *Neptune-1*이 페더레이션 쿼리를 수행할 수 있도록 적절한 IAM 권한만 있으면 됩니다.
+ *Neptune-1* 및 *Neptune-2* 모두에서 IAM 인증 및 권한 부여가 필요한 경우에는 두 데이터베이스 모두가 페더레이션 쿼리를 만들 수 있도록 IAM 권한을 연결해야 합니다. 두 클러스터 모두 동일한 AWS 계정과 동일한 리전에 있어야 합니다. 현재 교차 리전 및/또는 교차 계정 페더레이션된 쿼리 아키텍처는 지원되지 않습니다.
+ 그러나 *Neptune-1*에서 IAM이 사용되지 않지만, *Neptune-2*에서 사용되는 경우에는 페더레이션 쿼리를 만들 수 없습니다. 왜냐하면 *Neptune-1*이 IAM 보안 인증 정보를 검색하고, 이를 *Neptune-2*에 전달하여 쿼리의 두 번째 부분에 대해 권한을 부여할 수 없기 때문입니다.