

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

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

Amazon Neptune Gremlin `explain` API 返回将在运行指定查询时执行的查询计划。由于该 API 实际上并未运行查询，因此几乎可即时返回计划。

它与 TinkerPop .explain () 步骤的不同之处在于能够报告特定于 Neptune 引擎的信息。

## Gremlin `explain` 报告中包含的信息
<a name="gremlin-explain-api-results"></a>

`explain` 报告包含以下信息：
+ 要求的查询字符串。
+ **原始遍历。**这是通过将查询字符串解析为步骤而生成的 TinkerPop Traversal 对象。 TinkerPop 它等同于在针对的查询`.explain()`上运行所产生的原始查询 TinkerPop TinkerGraph。
+ **转换的遍历。**这是通过将遍历转换为海王星逻辑查询计划表示形式而生成的 Neptune Trav TinkerPop ersal。在许多情况下，整个 TinkerPop 遍历会转换为两个 Neptune 步骤：一个执行整个查询 (`NeptuneGraphQueryStep`)，另一个将海王星查询引擎的输出转换回 Traversers ()。 TinkerPop `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`。任何不属于的遍历部分都不会由 DFE 执行，而是由引擎执行。`DFEStep` TinkerPop 

  有关示例报告，请参阅[启用 DFE 的示例](#gremlin-explain-dfe)。

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

`explain` API 的语法与查询的 HTTP API 相同，不同之处在于，它使用 `/gremlin/explain` 作为终端节点而不是 `/gremlin`，如以下示例所示。

```
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 运算符的步骤时，即使后续步骤确实有原生 Neptune 运算符，也将使用 TinkerPop 步骤运行从该点开始的整个遍历。

这种情况的例外是调用 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 的查询示例 full-text-search
<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)。