

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 在 Neptune 中使用 Gremlin `explain` API
<a name="gremlin-explain-api"></a>

Amazon Neptune `explain` API 會傳回在所指定查詢執行時將執行的查詢計畫。因為 API 並未實際執行查詢，所以幾乎會立即傳回計劃。

它與 TinkerPop .explain() 步驟不同，因此能夠報告 Neptune 引擎的特定資訊。

## Gremlin `explain` 報告中包含的資訊
<a name="gremlin-explain-api-results"></a>

`explain` 報告包含下列資訊：
+ 如請求的查詢字串。
+ **原始周遊。**這是透過將查詢字串剖析為 TinkerPop 步驟所產生的 TinkerPop 周遊物件。這相當於在查詢上針對 TinkerPop TinkerGraph 執行 `.explain()` 所產生的原始查詢。
+ **轉換後的周遊。**這是將 TinkerPop 周遊轉換為 Neptune 邏輯查詢計劃表示法所產生的 Neptune 周遊。在許多情況下，整個 TinkerPop 周遊會轉換成兩個 Neptune 步驟：一個執行整個查詢 (`NeptuneGraphQueryStep`)，另一個則會將 Neptune 查詢引擎輸出轉換回 TinkerPop Traverser (`NeptuneTraverserConverterStep`)。
+ **最佳化的周遊。**這是 Neptune 查詢計畫的最佳化版本，其最佳化過程是透過一系列靜態減少工作最佳化工具來執行此查詢計畫，並根據靜態分析和估計基數來重寫查詢。這些最佳化工具會執行如下動作：根據範圍計數重新排序運算子、刪除不必要或冗餘運算子、重新安排篩選條件、將運算子推送至不同的群組等等。
+ **述詞計數。**由於先前所述的 Neptune 索引策略，具有大量不同的述詞可能會產生效能問題。這對於沒有邊緣標籤 (`.in` 或 `.both`) 的反向周遊運算子的查詢尤其適用。如果使用這類運算子且述詞計數夠高，則 `explain` 報告會顯示警告訊息。
+ **DEF 資訊。**啟用 DFE 替代引擎時，下列周遊元件可能會顯示在最佳化的周遊中：
  + **`DFEStep`** – 周遊中 Neptune 最佳化的 DFE 步驟，其中包含子 `DFENode`。`DFEStep` 代表查詢計畫在 DFE 引擎中執行的一部分。
  + **`DFENode`** – 包含做為一個或多個子 `DFEJoinGroupNodes` 的中繼表示法。
  + **`DFEJoinGroupNode`** – 代表一個或多個 `DFENode` 或 `DFEJoinGroupNode` 元素的聯結。
  + **`NeptuneInterleavingStep`** – 周遊中 Neptune 最佳化的 DFE 步驟，其中包含子 `DFEStep`。

    也包含一個包含周遊相關資訊的 `stepInfo` 元素，例如邊界元素、使用的路徑元素等。此資訊用來處理子 `DFEStep`。

  了解 DFE 是否正在評估查詢的一個簡單方法，就是檢查 `explain` 輸出是否包含 `DFEStep`。周遊中不屬於 `DFEStep` 的任何部分將不會由 DFE 執行，而是將會由 TinkerPop 引擎執行。

  如需範例報告，請參閱 [啟用 DFE 的範例](#gremlin-explain-dfe)。

## Gremlin `explain` 語法
<a name="gremlin-explain-api-syntax"></a>

`explain` API 的語法與 HTTP API 的查詢語法相同，但它使用 `/gremlin/explain`做為端點而非 `/gremlin`，如下列範例所示。

------
#### [ AWS CLI ]

```
aws neptunedata execute-gremlin-explain-query \
  --endpoint-url https://{{your-neptune-endpoint}}:{{port}} \
  --gremlin-query "g.V().limit(1)"
```

如需詳細資訊，請參閱《 AWS CLI 命令參考》中的 [execute-gremlin-explain-query](https://docs.aws.amazon.com/cli/latest/reference/neptunedata/execute-gremlin-explain-query.html)。

------
#### [ SDK ]

```
import boto3
from botocore.config import Config

client = boto3.client(
    'neptunedata',
    endpoint_url='https://{{your-neptune-endpoint}}:{{port}}',
    config=Config(read_timeout=None, retries={'total_max_attempts': 1})
)

response = client.execute_gremlin_explain_query(
    gremlinQuery='g.V().limit(1)'
)

print(response['output'])
```

如需 Java、.NET 等其他語言的 AWS SDK 範例，請參閱 [AWS 開發套件](access-graph-gremlin-sdk.md)。

------
#### [ awscurl ]

```
awscurl https://{{your-neptune-endpoint}}:{{port}}/gremlin/explain \
  --region {{us-east-1}} \
  --service neptune-db \
  -X POST \
  -d '{"gremlin":"g.V().limit(1)"}'
```

**注意**  
此範例假設您的 AWS 登入資料已在您的環境中設定。將 {{us-east-1}} 取代為 Neptune 叢集的區域。

如需使用 **awscurl**搭配 IAM 身分驗證的詳細資訊，請參閱 [使用 `awscurl` 搭配臨時憑證，安全地連線至啟用 IAM 身分驗證的資料庫叢集](iam-auth-connect-command-line.md#iam-auth-connect-awscurl)。

------
#### [ curl ]

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

## 未轉換的 TinkerPop 步驟
<a name="gremlin-explain-unconverted-steps"></a>

在理想情況下，周遊中的所有 TinkerPop 步驟都有原生 Neptune 運算子涵蓋範圍。如果情況並非如此，則 Neptune 會退回至 TinkerPop 步驟執行，以找出其運算子涵蓋範圍的差距。如果周遊使用的步驟，Neptune 還沒有其原生涵蓋範圍，則 `explain` 報告會顯示警告，其中顯示差距發生的位置。

遇到沒有對應原生 Neptune 運算子的步驟時，會使用 TinkerPop 步驟執行從該點往前的整個周遊，即使後續步驟確實具有原生 Neptune 運算子也一樣。

例外狀況是在呼叫 Neptune 全文搜尋時。NeptuneSearchStep 實作沒有原生對等項目的步驟做為全文搜尋步驟。

## 查詢中所有步驟都具有原生對等項目的 `explain` 輸出範例
<a name="gremlin-explain-all-steps-converted"></a>

以下是查詢的範例 `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
```

## 查詢中的部分步驟沒有原生對等項目的範例
<a name="gremlin-explain-not-all-steps-converted"></a>

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` 步驟的下游，而不再原生處理它也一樣。

為了節省效能和成本，請務必嘗試制訂周遊，以便以原生方式在 Neptune 查詢引擎內完成最大的工作量，而不是藉由 TinkerPop 步驟實作進行。

## 使用 Neptune 全文搜尋的查詢範例
<a name="gremlin-explain-full-text-search-steps"></a>

以下查詢使用 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` 的範例
<a name="gremlin-explain-dfe"></a>

以下是 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)。