

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

# Amazon Neptune 中的非字符串 OpenSearch 索引
<a name="full-text-search-non-string-indexing"></a>

Amazon Neptune 中的非字符串 OpenSearch 索引编制允许使用流轮询器将谓词的非字符串值复制到 OpenSearch。然后，所有可以安全地转换为相应的 OpenSearch 映射或数据类型的谓词值都将复制到 OpenSearch。

要在新堆栈上启用非字符串索引编制，必须将 CloudFormation 模板中的 `Enable Non-String Indexing` 标志设置为 `true`。这是默认设置。要更新现有堆栈以支持非字符串索引编制，请参阅下面的[更新现有的堆栈](full-text-search-non-string-indexing-update.md)。

**注意**  
最好不要在 **`1.0.4.2`** 之前的引擎版本上启用非字符串索引编制。
OpenSearch 查询使用正则表达式查找与多个字段匹配的字段名，其中一些字段包含字符串值，另一些字段包含非字符串值，但查询会失败并返回错误。如果 Neptune 中的全文搜索查询属于这种类型，也会发生同样的情况。
按非字符串字段排序时，请在字段名称后面附加“.value”，以将其与字符串字段区分开来。

**Contents**
+ [更新现有的 Neptune 全文搜索堆栈以支持非字符串索引编制](full-text-search-non-string-indexing-update.md)
+ [在 Neptune 全文搜索中筛选哪些字段已编制索引](full-text-search-non-string-indexing-filters.md)
  + [按属性或谓词名称筛选](full-text-search-non-string-indexing-filters.md#full-text-search-non-string-indexing-filters-name)
  + [按属性或谓词值类型筛选](full-text-search-non-string-indexing-filters.md#full-text-search-non-string-indexing-filters-datatype)
+ [将 SPARQL 和 Gremlin 数据类型映射到 OpenSearch](full-text-search-non-string-indexing-mapping.md)
+ [验证数据映射](full-text-search-data-validation.md)
+ [Neptune 中的非字符串 OpenSearch 查询示例](full-text-search-non-string-examples.md)
  + [获取所有使用期限大于 30 且名称以“Si”开头的顶点](full-text-search-non-string-examples.md#full-text-search-non-string-example-1)
  + [获取所有使用期限介于 10 到 50 之间且名称与“Ronka”模糊匹配的节点](full-text-search-non-string-examples.md#full-text-search-non-string-example-2)
  + [获取时间戳在过去 25 天内的所有节点](full-text-search-non-string-examples.md#full-text-search-non-string-example-3)
  + [获取时间戳在给定年份和月份内的所有节点](full-text-search-non-string-examples.md#full-text-search-non-string-example-4)

# 更新现有的 Neptune 全文搜索堆栈以支持非字符串索引编制
<a name="full-text-search-non-string-indexing-update"></a>

如果您已经在使用 Neptune 全文搜索，则需要采取以下步骤来支持非字符串索引编制：

1. **停止流轮询器 Lambda 函数。**这样可以确保在导出过程中不会复制任何新的更新。为此，请禁用用于调用 Lambda 函数的云事件规则：
   + 在 AWS 管理控制台 中，导航到 CloudWatch。
   + 选择**规则**。
   + 选择带有 Lambda 流轮询器名称的规则。
   + 选择**禁用**以暂时禁用该规则。

1. **在 OpenSearch 中删除当前 Neptune 索引。**使用以下 `curl` 查询从 OpenSearch 集群中删除 `amazon_neptune` 索引：

   ```
   curl -X DELETE "your OpenSearch endpoint/amazon_neptune"
   ```

1. **开始从 Neptune 一次性导出到 OpenSearch。**此时最好设置一个新的 OpenSearch 堆栈，以便为执行导出的轮询器获取新的构件。

   按照 [GitHub 中此处]( https://github.com/awslabs/amazon-neptune-tools/blob/master/export-neptune-to-elasticsearch/readme.md)列出的步骤，开始将您的 Neptune 数据一次性导出到 OpenSearch 中。

1. **更新现有流轮询器的 Lambda 构件。**成功完成将 Neptune 数据导出到 OpenSearch 后，请执行以下步骤：
   + 在 AWS 管理控制台 中，导航到 CloudFormation。
   + 选择主要的父 CloudFormation 堆栈。
   + 为该堆栈选择**更新**选项。
   + 选择**从选项替换当前模板**。
   + 对于模板源，请选择 **Amazon S3 URL**。
   + 对于 Amazon S3 URL，输入：

     ```
     https://aws-neptune-customer-samples.s3.amazonaws.com/neptune-stream/neptune_to_elastic_search.json
     ```
   + 在不更改任何 CloudFormation 参数的情况下选择**下一步**。
   + 选择**更新堆栈**。CloudFormation 将用最新的构件替换流轮询器的 Lambda 代码构件。

1. **再次启动流轮询器。**为此，请启用相应的 CloudWatch 规则：
   + 在 AWS 管理控制台 中，导航到 CloudWatch。
   + 选择**规则**。
   + 选择带有 Lambda 流轮询器名称的规则。
   + 选择**启用**。

# 在 Neptune 全文搜索中筛选哪些字段已编制索引
<a name="full-text-search-non-string-indexing-filters"></a>

CloudFormation 模板详细信息中有两个字段可让您指定要从 OpenSearch 索引编制中排除的属性、谓词键或数据类型：

## 按属性或谓词名称筛选
<a name="full-text-search-non-string-indexing-filters-name"></a>

您可以使用名为 `Properties to exclude from being inserted into Elastic Search Index` 的可选 CloudFormation 模板参数，来提供要从 OpenSearch 索引编制中排除的以逗号分隔的属性或谓词键列表。

例如，假设您将此参数设置为 `bob`。

```
"Properties to exclude from being inserted into Elastic Search Index" : bob
```

在这种情况下，以下 Gremlin 更新查询的流记录将被删除，而不是进入索引：

```
g.V("1").property("bob", "test")
```

同样，您可以将此参数设置为 `http://my/example#bob`：

```
"Properties to exclude from being inserted into Elastic Search Index" : http://my/example#bob
```

在这种情况下，以下 SPARQL 更新查询的流记录将被删除，而不是进入索引：

```
PREFIX ex: <http://my/example#>
INSERT DATA { ex:s1 ex:bob "test"}.
```

如果您未在此 CloudFormation 模板参数中输入任何内容，则所有未以其它方式排除的属性键都将编制索引。

## 按属性或谓词值类型筛选
<a name="full-text-search-non-string-indexing-filters-datatype"></a>

您可以使用名为 `Datatypes to exclude from being inserted into Elastic Search Index` 的可选 CloudFormation 模板参数，来提供要从 OpenSearch 索引编制中排除的以逗号分隔的属性或谓词值数据类型列表。

对于 SPARQL，您无需列出完整的 XSD 类型 URI，只需列出数据类型令牌即可。您可以列出的有效数据类型令牌有：
+ `string`
+ `boolean`
+ `float`
+ `double`
+ `dateTime`
+ `date`
+ `time`
+ `byte`
+ `short`
+ `int`
+ `long`
+ `decimal`
+ `integer`
+ `nonNegativeInteger`
+ `nonPositiveInteger`
+ `negativeInteger`
+ `unsignedByte`
+ `unsignedShort`
+ `unsignedInt`
+ `unsignedLong`

对于 Gremlin，要列出的有效数据类型有：
+ `string`
+ `date`
+ `bool`
+ `byte`
+ `short`
+ `int`
+ `long`
+ `float`
+ `double`

例如，假设您将此参数设置为 `string`。

```
"Datatypes to exclude from being inserted into Elastic Search Index" : string
```

在这种情况下，以下 Gremlin 更新查询的流记录将被删除，而不是进入索引：

```
g.V("1").property("myStringval", "testvalue")
```

同样，您可以将此参数设置为 `int`：

```
"Datatypes to exclude from being inserted into Elastic Search Index" : int
```

在这种情况下，以下 SPARQL 更新查询的流记录将被删除，而不是进入索引：

```
PREFIX ex: <http://my/example#>
PREFIX xsd:<http://www.w3.org/2001/XMLSchema#>
INSERT DATA { ex:s1 ex:bob "11"^^xsd:int }.
```

如果您未在此 CloudFormation 模板参数中输入任何内容，则其值可以安全地转换为 OpenSearch 等效值的所有属性都将编制索引。查询语言不支持的列出类型将被忽略。

# 将 SPARQL 和 Gremlin 数据类型映射到 OpenSearch
<a name="full-text-search-non-string-indexing-mapping"></a>

OpenSearch 中的新数据类型映射是根据属性或对象中使用的数据类型创建的。由于某些字段包含不同类型的值，因此初始映射可能会排除该字段的某些值。

Neptune 数据类型映射到 OpenSearch 数据类型，如下所示：


| SPARQL 类型 | Gremlin 类型 | OpenSearch 类型 | 
| --- | --- | --- | 
|  `XSD:int` `XSD:unsignedInt` `XSD:integer` `XSD:byte` `XSD:unsignedByte` `XSD:short` `XSD:unsignedShort` `XSD:long` `XSD:unsignedLong`  |  `byte` `short` `int` `long`  | `long` | 
|  `XSD:float` `XSD:double` `XSD:decimal`  |  `float` `double`  | `double` | 
| `XSD:boolean` | `bool` | `boolean` | 
|  `XSD:datetime` `XSD:date`  | `date` | `date` | 
|  `XSD:string` `XSD:time`  | `string` | `text` | 
| *自定义数据类型* | *（不适用*） | `text` | 
| *任何其它数据类型* | *（不适用*） | `text` | 

例如，以下 Gremlin 更新查询会导致向 OpenSearch 中添加“newField”的新映射，即 `{ "type" : "double" }`：

```
g.V("1").property("newField" 10.5)
```

同样，以下 SPARQL 更新查询会导致向 OpenSearch 中添加“ex:byte”的新映射，即 `{ "type" : "long" }`：

```
PREFIX ex: <http://my/example#>
PREFIX xsd:<http://www.w3.org/2001/XMLSchema#>

INSERT DATA { ex:test ex:byte "123"^^xsd:byte }.
```

**注意**  
如您所见，从 Neptune 映射到 OpenSearch 的项目在 OpenSearch 中的数据类型可能与它在 Neptune 中具有的数据类型不同。但是，OpenSearch 中有一个显式文本字段，即“datatype”，用于记录该项目在 Neptune 中的数据类型。

# 验证数据映射
<a name="full-text-search-data-validation"></a>



使用以下过程将数据从 Neptune 复制到 OpenSearch：
+ 如果 OpenSearch 中已经存在相关字段的映射：
  + 如果可以使用数据验证规则将数据安全地转换为现有映射，则将该字段存储在 OpenSearch 中。
  + 否则，请删除相应的流更新记录。
+ 如果相关字段没有现有的映射，请在 Neptune 中查找与该字段的数据类型对应的 OpenSearch 数据类型。
  + 如果可以使用数据验证规则将字段数据安全地转换为 OpenSearch 数据类型，则将新的映射和字段数据存储在 OpenSearch 中。
  + 否则，请删除相应的流更新记录。

值是根据等效的 OpenSearch 类型或现有 OpenSearch 映射而不是根据 Neptune 类型进行验证的。例如，对于 `"123"^^xsd:int` 中的值 `"123"` 的验证是针对 `long` 类型而不是 `int` 类型进行的。

尽管 Neptune 尝试将所有数据复制到 OpenSearch，但在某些情况下，OpenSearch 中的数据类型与 Neptune 中的数据类型完全不同，在这种情况下，会跳过记录，而不是在 OpenSearch 中编制索引。

例如，在 Neptune 中，一个属性可以有多个不同类型的值，而在 OpenSearch 中，一个字段在索引中必须具有相同的类型。

通过启用调试日志，您可以查看在从 Neptune 导出到 OpenSearch 期间删除了哪些记录。调试日志条目的示例为：

```
Dropping Record : Data type not a valid Gremlin type 
<Record>
```

数据类型按如下所示进行验证：
+ **`text`** – Neptune 中的所有值都可以安全地映射到 OpenSearch 中的文本。
+ **`long`** – 当 OpenSearch 映射类型为 long 时，Neptune 数据类型的以下规则适用（在以下示例中，假设 `"testLong"` 具有 `long` 映射类型）：
  + `boolean` – 无效，无法转换，并且会删除相应的流更新记录。

    无效的 Gremlin 示例为：

    ```
      "testLong" : true.
      "testLong" : false.
    ```

    无效的 SPARQL 示例为：

    ```
      ":testLong" : "true"^^xsd:boolean
      ":testLong" : "false"^^xsd:boolean
    ```
  + `datetime` – 无效，无法转换，并且会删除相应的流更新记录。

    无效的 Gremlin 示例为：

    ```
      ":testLong" :  datetime('2018-11-04T00:00:00').
    ```

    无效的 SPARQL 示例为：

    ```
      ":testLong" : "2016-01-01"^^xsd:date
    ```
  + `float`、`double` 或 `decimal` – 如果 Neptune 中的值是一个可以容纳 64 位的整数，则它是有效的，并且以 long 形式存储在 OpenSearch 中；但如果它有小数部分，或者是 `NaN` 或 `INF`，或者大于 9,223,372,036,854,775,807 或小于 -9,223,372,036,854,775,808，则它是无效的且相应的流更新记录被删除。

    有效的 Gremlin 示例为：

    ```
      "testLong" :  145.0.
      ":testLong" :  123
      ":testLong" :  -9223372036854775807
    ```

    有效的 SPARQL 示例为：

    ```
      ":testLong" : "145.0"^^xsd:float
      ":testLong" :  145.0
      ":testLong" : "145.0"^^xsd:double
      ":testLong" : "145.0"^^xsd:decimal
      ":testLong" : "-9223372036854775807"
    ```

    无效的 Gremlin 示例为：

    ```
      "testLong" :  123.45
      ":testLong" :  9223372036854775900
    ```

    无效的 SPARQL 示例为：

    ```
      ":testLong" :  123.45
      ":testLong" :  9223372036854775900
      ":testLong" : "123.45"^^xsd:float
      ":testLong" : "123.45"^^xsd:double
      ":testLong" : "123.45"^^xsd:decimal
    ```
  + `string` – 如果 Neptune 中的值是可以包含在 64 位整数中的整数的字符串表示形式，则该值是有效的，并在 OpenSearch 中转换为 `long`。任何其它字符串值对于 Elasticseearch `long` 映射都无效，相应的数据流更新记录将被删除。

    有效的 Gremlin 示例为：

    ```
      "testLong" :  "123".
      ":testLong" :  "145.0"
      ":testLong" :  "-9223372036854775807"
    ```

    有效的 SPARQL 示例为：

    ```
      ":testLong" : "145.0"^^xsd:string
      ":testLong" : "-9223372036854775807"^^xsd:string
    ```

    无效的 Gremlin 示例为：

    ```
      "testLong" :  "123.45"
      ":testLong" :  "9223372036854775900"
      ":testLong" :  "abc"
    ```

    无效的 SPARQL 示例为：

    ```
      ":testLong" : "123.45"^^xsd:string
      ":testLong" : "abc"
      ":testLong" : "9223372036854775900"^^xsd:string
    ```
+ **`double`** – 如果 OpenSearch 映射类型为 `double`，则适用以下规则（此处，假定“testDouble”字段在 OpenSearch 中具有 `double` 映射）：
  + `boolean` – 无效，无法转换，并且会删除相应的流更新记录。

    无效的 Gremlin 示例为：

    ```
      "testDouble" : true.
      "testDouble" : false.
    ```

    无效的 SPARQL 示例为：

    ```
      ":testDouble" : "true"^^xsd:boolean
      ":testDouble" : "false"^^xsd:boolean
    ```
  + `datetime` – 无效，无法转换，并且会删除相应的流更新记录。

    无效的 Gremlin 示例为：

    ```
      ":testDouble" :  datetime('2018-11-04T00:00:00').
    ```

    无效的 SPARQL 示例为：

    ```
      ":testDouble" : "2016-01-01"^^xsd:date
    ```
  + 浮点 `NaN` 或 `INF` – 如果 SPARQL 中的值是浮点 `NaN` 或 `INF`，则该值无效，并且会删除相应的流更新记录。

    无效的 SPARQL 示例为：

    ```
    "  :testDouble" : "NaN"^^xsd:float
      ":testDouble" : "NaN"^^double
      ":testDouble" : "INF"^^double
      ":testDouble" : "-INF"^^double
    ```
  + 数字或数字字符串 – 如果 Neptune 中的值是任何其它数字或数字的数字字符串表示（可以安全地表示为 `double`），那么它是有效的，并在 OpenSearch 中转换为 `double`。任何其它字符串值对于 OpenSearch `double` 映射都无效，并将删除相应的数据流更新记录。

    有效的 Gremlin 示例为：

    ```
      "testDouble" :  123
      ":testDouble" :  "123"
      ":testDouble" :  145.67
      ":testDouble" :  "145.67"
    ```

    有效的 SPARQL 示例为：

    ```
      ":testDouble" :  123.45
      ":testDouble" :  145.0
      ":testDouble" : "123.45"^^xsd:float
      ":testDouble" : "123.45"^^xsd:double
      ":testDouble" : "123.45"^^xsd:decimal
      ":testDouble" : "123.45"^^xsd:string
    ```

    无效的 Gremlin 示例为：

    ```
      ":testDouble" :  "abc"
    ```

    无效的 SPARQL 示例为：

    ```
      ":testDouble" : "abc"
    ```
+ **`date`** – 如果 OpenSearch 映射类型为 `date`，则 Neptune `date` 和 `dateTime` 值有效，任何可以成功解析为 `dateTime` 格式的字符串值也是有效的。

  Gremlin 或 SPARQL 中的有效示例为：

  ```
    Date(2016-01-01)
    "2016-01-01" "
    2003-09-25T10:49:41"
    "2003-09-25T10:49"
    "2003-09-25T10"
    "20030925T104941-0300"
    "20030925T104941"
    "2003-Sep-25" "
    Sep-25-2003"
    "2003.Sep.25"
    "2003/09/25"
    "2003 Sep 25" "
    Wed, July 10, '96"
    "Tuesday, April 12, 1952 AD 3:30:42pm PST"
    "123"
    "-123"
    "0"
    "-0"
    "123.00"
    "-123.00"
  ```

  无效的示例为：

  ```
    123.45
    True
    "abc"
  ```

# Neptune 中的非字符串 OpenSearch 查询示例
<a name="full-text-search-non-string-examples"></a>

Neptune 目前不直接支持 OpenSearch 范围查询。但是，您可以使用 Lucene 语法和 query-type="query\$1string" 实现相同的效果，如以下示例查询所示。

## 获取所有使用期限大于 30 且名称以“Si”开头的顶点
<a name="full-text-search-non-string-example-1"></a>

在 Gremlin 中：

```
g.withSideEffect('Neptune#fts.endpoint', 'http://your-es-endpoint')
 .withSideEffect("Neptune#fts.queryType", "query_string")
 .V().has('*', 'Neptune#fts predicates.age.value:>30 && predicates.name.value:Si*');
```

在 SPARQL 中：

```
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX neptune-fts: <http://aws.amazon.com/neptune/vocab/v01/services/fts#>
SELECT * WHERE {
  SERVICE neptune-fts:search {
    neptune-fts:config neptune-fts:endpoint 'http://localhost:9200' .
    neptune-fts:config neptune-fts:queryType 'query_string' .
    neptune-fts:config neptune-fts:query "predicates.\\*foaf\\*age.value:>30 AND predicates.\\*foaf\\*name.value:Si*" .
    neptune-fts:config neptune-fts:field '*' .
    neptune-fts:config neptune-fts:return ?res .
  }
}
```

为了简洁起见，这里使用 `"\\*foaf\\*age` 来代替完整的 URI。此正则表达式将检索 URI 中同时包含 `foaf` 和 `age` 的所有字段。

## 获取所有使用期限介于 10 到 50 之间且名称与“Ronka”模糊匹配的节点
<a name="full-text-search-non-string-example-2"></a>

在 Gremlin 中：

```
g.withSideEffect('Neptune#fts.endpoint', 'http://your-es-endpoint')
 .withSideEffect("Neptune#fts.queryType", "query_string")
 .V().has('*', 'Neptune#fts predicates.age.value:[10 TO 50] AND predicates.name.value:Ronka~');
```

在 SPARQL 中：

```
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX neptune-fts: <http://aws.amazon.com/neptune/vocab/v01/services/fts#>
SELECT * WHERE {
  SERVICE neptune-fts:search {
    neptune-fts:config neptune-fts:endpoint 'http://localhost:9200' .
    neptune-fts:config neptune-fts:queryType 'query_string' .
    neptune-fts:config neptune-fts:query "predicates.\\*foaf\\*age.value:[10 TO 50] AND predicates.\\*foaf\\*name.value:Ronka~" .
    neptune-fts:config neptune-fts:field '*' .
    neptune-fts:config neptune-fts:return ?res .
  }
}
```

## 获取时间戳在过去 25 天内的所有节点
<a name="full-text-search-non-string-example-3"></a>

在 Gremlin 中：

```
g.withSideEffect('Neptune#fts.endpoint', 'http://your-es-endpoint')
 .withSideEffect("Neptune#fts.queryType", "query_string")
 .V().has('*', 'Neptune#fts predicates.timestamp.value:>now-25d');
```

在 SPARQL 中：

```
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX neptune-fts: <http://aws.amazon.com/neptune/vocab/v01/services/fts#>
SELECT * WHERE {
SELECT * WHERE {
  SERVICE neptune-fts:search {
    neptune-fts:config neptune-fts:endpoint 'http://localhost:9200' .
    neptune-fts:config neptune-fts:queryType 'query_string' .
    neptune-fts:config neptune-fts:query "predicates.\\*foaf\\*timestamp.value:>now-25d~" .
    neptune-fts:config neptune-fts:field '*' .
    neptune-fts:config neptune-fts:return ?res .
  }
}
```

## 获取时间戳在给定年份和月份内的所有节点
<a name="full-text-search-non-string-example-4"></a>

在 Gremlin 中，使用 Lucene 语法中的[日期数学表达式](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/common-options.html#date-math)，对于 2020 年 12 月：

```
g.withSideEffect('Neptune#fts.endpoint', 'http://your-es-endpoint')
 .withSideEffect("Neptune#fts.queryType", "query_string")
 .V().has('*', 'Neptune#fts predicates.timestamp.value:>2020-12');
```

Gremlin 的替代形式：

```
g.withSideEffect('Neptune#fts.endpoint', 'http://your-es-endpoint')
 .withSideEffect("Neptune#fts.queryType", "query_string")
 .V().has('*', 'Neptune#fts predicates.timestamp.value:[2020-12 TO 2021-01]');
```