

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

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

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

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

## `Gremlin 解释报告中包含的信息`
<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 解释语法`
<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)"
```

有关更多信息，请参阅《命令参考》中的 [execute-gremlin-explain-query](https://docs.aws.amazon.com/cli/latest/reference/neptunedata/execute-gremlin-explain-query.html)。 AWS CLI 

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

这种情况的例外是调用 Neptune 全文搜索时。 NeptuneSearchStep 实现了没有原生等效项的步骤作为全文搜索步骤。

## expl `a` in 输出示例，其中查询中的所有步骤都具有本机等效项
<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 时使用`解释`的示例
<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
```

[`explain` 中的信息](#gremlin-explain-api-results)有关报告中 DFE-specific 各部分的说明，请参阅。