

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

# 教程：使用亚马逊 OpenSearch 服务创建搜索应用程序
<a name="search-example"></a>

使用 Amazon Serv OpenSearch ice 创建搜索应用程序的常用方法是使用网络表单向服务器发送用户查询。然后，您可以授权服务器 OpenSearch APIs 直接调用，并让服务器向 Serv OpenSearch ice 发送请求。但是，如果您想编写不依赖服务器的客户端代码，则应针对安全和性能风险作出补偿。不建议允许未签名的公开访问权限。 OpenSearch APIs 用户可能会访问不安全的终端节点，或者通过过于广泛的查询（或过多的查询）影响集群性能。

本章介绍一个解决方案：使用 Amazon API Gateway 将用户限制为其中的一个子集， OpenSearch APIs 并 AWS Lambda 对从 API Gateway 到 OpenSearch 服务的请求进行签名。

![\[搜索应用程序流程图。\]](http://docs.aws.amazon.com/zh_cn/opensearch-service/latest/developerguide/images/search-application-diagram.png)


**注意**  
标准 API Gateway 和 Lambda 定价适用，但不能超出本教程的限制使用量，成本应忽略不计。

## 先决条件
<a name="search-example-prereq"></a>

本教程的先决条件是 OpenSearch 服务域。如果您还没有，请按照[创建 OpenSearch 服务域](gsgcreate-domain.md)中的步骤创建一个。

## 步骤 1：为示例数据建立索引
<a name="search-example-index"></a>

下载 [sample-movies.zip](samples/sample-movies.zip)，解压它，然后使用 [\$1bulk](https://opensearch.org/docs/latest/api-reference/document-apis/bulk/) API 操作将 5000 个文档添加到 `movies` 索引：

```
POST https://search-my-domain.us-west-1.es.amazonaws.com/_bulk
{ "index": { "_index": "movies", "_id": "tt1979320" } }
{"directors":["Ron Howard"],"release_date":"2013-09-02T00:00:00Z","rating":8.3,"genres":["Action","Biography","Drama","Sport"],"image_url":"http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg","plot":"A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.","title":"Rush","rank":2,"running_time_secs":7380,"actors":["Daniel Brühl","Chris Hemsworth","Olivia Wilde"],"year":2013,"id":"tt1979320","type":"add"}
{ "index": { "_index": "movies", "_id": "tt1951264" } }
{"directors":["Francis Lawrence"],"release_date":"2013-11-11T00:00:00Z","genres":["Action","Adventure","Sci-Fi","Thriller"],"image_url":"http://ia.media-imdb.com/images/M/MV5BMTAyMjQ3OTAxMzNeQTJeQWpwZ15BbWU4MDU0NzA1MzAx._V1_SX400_.jpg","plot":"Katniss Everdeen and Peeta Mellark become targets of the Capitol after their victory in the 74th Hunger Games sparks a rebellion in the Districts of Panem.","title":"The Hunger Games: Catching Fire","rank":4,"running_time_secs":8760,"actors":["Jennifer Lawrence","Josh Hutcherson","Liam Hemsworth"],"year":2013,"id":"tt1951264","type":"add"}
...
```

请注意，上方是一个示例命令，其中包含一小部分可用数据。要执行 `_bulk` 操作，您需要复制和粘贴 `sample-movies` 文件的全部内容。有关更多说明，请参阅 [选项 2：上传多个文档](gsgupload-data.md#gsgmultiple-document)。

您也可以使用以下 curl 命令实现相同的结果：

```
curl -XPOST -u 'master-user:master-user-password' 'domain-endpoint/_bulk' --data-binary @bulk_movies.json -H 'Content-Type: application/json'
```

## 步骤 2：创建并部署 Lambda 函数
<a name="search-example-lambda"></a>

在 API Gateway 中创建 API 前，创建将请求传递到的 Lambda 函数。

### 创建 Lambda 函数
<a name="sample-lamdba-python"></a>

在此解决方案中，API Gateway 将请求传递给 Lambda 函数，该函数查询 OpenSearch 服务并返回结果。由于此示例函数使用的是外部库，您需要创建一个部署程序包并将其上传到 Lambda。

**创建部署包**

1. 打开命令提示符并创建 `my-opensearch-function` 项目目录。例如，在 macOS 上，请执行以下操作：

   ```
   mkdir my-opensearch-function
   ```

1. 导航到 `my-sourcecode-function` 项目目录。

   ```
   cd my-opensearch-function
   ```

1. 复制以下 Python 示例代码的内容，并且使用名为 `opensearch-lambda.py` 的新文件将其保存。将您的区域和主机端点添加到文件中。

   ```
   import boto3
   import json
   import requests
   from requests_aws4auth import AWS4Auth
   
   region = '' # For example, us-west-1
   service = 'es'
   credentials = boto3.Session().get_credentials()
   awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)
   
   host = '' # The OpenSearch domain endpoint with https:// and without a trailing slash
   index = 'movies'
   url = host + '/' + index + '/_search'
   
   # Lambda execution starts here
   def lambda_handler(event, context):
   
       # Put the user query into the query DSL for more accurate search results.
       # Note that certain fields are boosted (^).
       query = {
           "size": 25,
           "query": {
               "multi_match": {
                   "query": event['queryStringParameters']['q'],
                   "fields": ["title^4", "plot^2", "actors", "directors"]
               }
           }
       }
   
       # Elasticsearch 6.x requires an explicit Content-Type header
       headers = { "Content-Type": "application/json" }
   
       # Make the signed HTTP request
       r = requests.get(url, auth=awsauth, headers=headers, data=json.dumps(query))
   
       # Create the response and add some extra content to support CORS
       response = {
           "statusCode": 200,
           "headers": {
               "Access-Control-Allow-Origin": '*'
           },
           "isBase64Encoded": False
       }
   
       # Add the search results to the response
       response['body'] = r.text
       return response
   ```

1. 在新的 `package` 目录中安装外部库。

   ```
   pip3 install --target ./package boto3
   pip3 install --target ./package requests
   pip3 install --target ./package requests_aws4auth
   ```

1. 使用已安装库在根目录下创建部署程序包。以下命令可在项目目录中生成 `my-deployment-package.zip` 文件。

   ```
   cd package
   zip -r ../my-deployment-package.zip .
   ```

1. 将 `opensearch-lambda.py` 文件添加到 zip 文件的根目录。

   ```
   cd ..
   zip my-deployment-package.zip opensearch-lambda.py
   ```

有关创建 Lambda 函数和部署程序包的更多信息，请参阅 *AWS Lambda 开发人员指南*中的[使用 .zip 文件归档部署 Python Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html)和本指南中的 [创建 Lambda 部署程序包](integrations-s3-lambda.md#integrations-s3-lambda-deployment-package)。

使用 Lambda 控制台创建函数

1. [在家中导航到 Lambda 控制台。https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/home )在左侧导航窗格中，选择**函数**。

1. 选择**创建函数**。

1. 配置以下字段：
   + 函数名称：opensearch-function
   + 运行时：Python 3.9
   + 架构：x86\$164

   保留所有其他默认选项，然后选择**创建函数**。

1. 在函数摘要页面的**代码源**部分，选择从下拉列表中**上传**，然后选择 **.zip 文件**。找到您创建的 `my-deployment-package.zip` 文件，然后选择**保存**。

1. *处理程序*是函数代码中处理事件的方法。在**运行时设置**下，选择**编辑**，根据 Lambda 函数所在的部署包中的文件名更改处理程序名称。鉴于您的文件名为 `opensearch-lambda.py`，请将处理程序重命名为 `opensearch-lambda.lambda_handler`。有关更多信息，请参阅 [Python 中的 Lambda 函数处理程序中的](https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html)。

## 步骤 3：在 API Gateway 中创建 API
<a name="search-example-api"></a>

使用 API Gateway 可以创建更有限的 API，并简化与 OpenSearch `_search` API 交互的过程。API Gateway 还可让您启用安全功能，如 Amazon Cognito 身份验证和请求限制。执行以下步骤来创建和部署 API：

### 创建和配置 API
<a name="create-api"></a>

使用 API Gateway 控制台创建 API

1. 在[https://console.aws.amazon.com/apigateway/家](https://console.aws.amazon.com/apigateway/home )中导航到 API Gateway 控制台。在左侧导航窗格中，选择**APIs**。

1. 定位**REST API**（非私有），然后选择**构建**。

1. 在下一页中，找到**新建 API** 部分，确保选中**新建 API**。

1. 配置以下字段：
   + API 名称：**opensearch-api**
   + 描述：**用于搜索亚马逊 OpenSearch 服务域名的公共 API**
   + 端点类型：**区域**

1. 选择**创建 API**。

1. 选择**操作**和**创建方法**。

1. 在下拉菜单中选择**GET**，然后单击复选标记进行确认。

1. 配置以下设置，然后选择**保存**：


| 设置 | 值 | 
| --- | --- | 
| 集成类型 | Lambda 函数 | 
| 使用 Lambda 代理集成 | 是 | 
| Lambda 区域 | us-west-1 | 
| Lambda 函数 | opensearch-lambda | 
| 使用原定设置超时 | 是 | 

### 配置该方法请求
<a name="method-request"></a>

选择**方法请求**并配置以下设置：


| 设置 | 值 | 
| --- | --- | 
| Authorization | NONE | 
| 请求验证器 |  验证查询字符串参数和标头   | 
| 必需的 API 密钥 | false | 

在 **URL 查询字符串参数**下，选择**添加查询字符串**并配置以下参数：


| 设置 | 值 | 
| --- | --- | 
| Name | q | 
| 必需 |  是  | 

### 部署 API 并配置阶段
<a name="deploy-api"></a>

 借助 API Gateway 控制台，您可以创建部署并将其与新的或现有阶段相关联，从而部署 API。

1. 选择**操作**和**部署 API**。

1. 对于**部署阶段**选择**新阶段**并将阶段命名为 `opensearch-api-test`。

1. 选择**部署**。

1. 在阶段编辑器中配置以下设置，然后选择**保存更改**：


| 设置 | 值 | 
| --- | --- | 
| 启用限制 | 是 | 
| 费率 |  1000  | 
| 突增 | 500 | 

这些设置将配置一个 API，该 API 只有一个方法：一个针对终端节点根的 `GET` 请求 (`https://some-id.execute-api.us-west-1.amazonaws.com/search-es-api-test`)。该请求需要单个参数 (`q`) - 查询字符串要搜索的。调用后，该方法会将请求传递到将运行 `opensearch-lambda` 函数的 Lambda。有关更多信息，请参阅[在 Amazon API Gateway 中创建 API](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-create-api.html) 和[在 Amazon API Gateway 中部署 REST API](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-deploy-api.html)。

## 步骤 4：（可选）修改域访问策略
<a name="search-example-perms"></a>

您的 OpenSearch 服务域必须允许 Lambda 函数向`GET`索引发出请求。`movies`如果您的域具有已启用精细访问控制的开放访问策略，则可以将其保持原样：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-west-1:123456789012:domain/domain-name/*"
    }
  ]
}
```

------

您也可以选择建立更加精细的域访问策略。例如，以下最低策略提供了对整个 `movies` 索引的 `opensearch-lambda-role`（通过 Lambda 创建）访问：要获取 Lambda 自动创建的角色的确切名称，请转到 AWS Identity and Access Management (IAM) 控制台，选择**角色**，然后搜索 “lambda”。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/service-role/opensearch-lambda-role-1abcdefg"
      },
      "Action": "es:ESHttpGet",
      "Resource": "arn:aws:es:us-west-1:123456789012:domain/domain-name/movies/_search"
    }
  ]
}
```

------

**重要**  
如果您为域启用了精细的访问控制，则还需要在 OpenSearch 仪表板中[将角色映射到用户](fgac.md#fgac-mapping)，否则您将看到权限错误。

### 配置 Lambda 执行角色权限
<a name="search-example-lambda-iam"></a>

除了配置域访问策略外，您还必须确保 Lambda 执行角色具有访问您的 OpenSearch 服务域所必需的 IAM 权限。Lambda 函数需要特定的权限，具体取决于您使用的是托管域还是无 OpenSearch 服务集合。

**对于托管 OpenSearch 服务域：**

将以下 IAM 策略附加到您的 Lambda 执行角色以允许其向您的 OpenSearch 服务域发出请求：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "es:ESHttpGet",
        "es:ESHttpPost"
      ],
      "Resource": "arn:aws:es:us-west-1:123456789012:domain/domain-name/*"
    }
  ]
}
```

------

**对于无服务器 OpenSearch 服务集合：**

如果您使用的是无服务器 OpenSearch 服务，请将以下 IAM 策略附加到您的 Lambda 执行角色：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "aoss:*",
      "Resource": "arn:aws:aoss:us-west-1:123456789012:collection/collection-id"
    }
  ]
}
```

------

要将这些策略附加到 Lambda 执行角色，请执行以下操作：

1. 导航到 [https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/) 的 IAM 控制台。

1. 选择**角色**，并搜索 Lambda 执行角色（通常命名为 `opensearch-lambda-role-xxxxxxxx`）。

1. 选择**添加权限**，然后选择**创建内联策略**。

1. 选择 **JSON** 选项卡并粘贴上面的相应策略，将占位符值替换为实际资源 ARNs。

1. 选择**查看策略**，提供名称（例如 `OpenSearchAccess`），然后选择**创建策略**。

**注意**  
如果没有这些 IAM 权限，即使域访问策略允许请求，您的 Lambda 函数在尝试查询您的 OpenSearch 服务域时也会收到 “访问被拒绝” 错误。

有关访问策略的更多信息，请参阅 [配置访问策略](createupdatedomains.md#createdomain-configure-access-policies)。

## 映射 Lambda 角色（如果使用精细访问控制）
<a name="search-example-perms-fgac"></a>

精细访问控制将在您能测试应用程序之前引入一个额外步骤。即使您将 HTTP 基本身份验证用于所有其他目的，也需要将 Lambda 角色映射到用户，否则您将看到权限错误。

1. 导航到该域的 OpenSearch 控制面板 URL。

1. 从主菜单中，选择**安全**、**角色**，然后选择 `all_access` 链接和需要将 Lambda 角色映射到的角色。

1. 选择**映射的用户**、**管理映射**。

1. 在 **Backend roles**（后端角色）下，添加 Lambda 角色的 Amazon 资源名称（ARN）。ARN 应采用 `arn:aws:iam::123456789123:role/service-role/opensearch-lambda-role-1abcdefg` 形式。

1. 选择**映射**并确认在**映射的用户**下显示的用户或角色。

## 步骤 5：测试 Web 应用程序
<a name="search-example-webpage"></a>

**测试 Web 应用程序**

1. 下载 [sample-site.zip](samples/sample-site.zip)，解压后在常用文本编辑器中打开 `scripts/search.js`。

1. 更新 `apigatewayendpoint` 变量以指向您的 API Gateway 端点，并在给定路径末尾添加反斜线。您可以选择**阶段**，然后选择 API 的名称，即可在 API Gateway 中快速找到端点。`apigatewayendpoint` 变量应采用 `https://some-id.execute-api.us-west-1.amazonaws.com/opensearch-api-test`/ 形式。

1. 打开 `index.html` 并尝试运行对 *thor*、*house* 和其他几个术语的搜索。  
![\[一个对 thor 的示例搜索。\]](http://docs.aws.amazon.com/zh_cn/opensearch-service/latest/developerguide/images/search-ui.png)

### 排除 CORS 错误
<a name="search-example-cors"></a>

尽管 Lambda 函数在响应中包含支持 CORS 的内容，但您仍可能会看到以下错误：

```
Access to XMLHttpRequest at '<api-gateway-endpoint>' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present in the requested resource.
```

如果，请尝试以下操作：

1. 在 GET 资源上[启用 CORS](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors-console.html)。在 **Advanced (高级)** 西方，设置 **Access-Control-Allow-Credentials** 为 `'true'`。

1. 在 API Gateway 中重新部署 API (**Action (操作)**、**Deploy API (部署 API)**)。

1. 删除并重新添加 Lambda 函数触发器。重新添加，选择**添加触发器**并创建调用函数的 HTTP 端点。该触发器必须具有以下配置：    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/opensearch-service/latest/developerguide/search-example.html)

## 后续步骤
<a name="search-example-next"></a>

本章只是一个展示概念的起始点。您可以考虑以下修改：
+ 将您自己的数据添加到 OpenSearch 服务域。
+ 将方法添加到您的 API。
+ 在 Lambda 函数中，修改搜索查询或提高不同的字段。
+ 以不同的方式呈现结果或修改 `search.js` 以向用户显示不同的字段。