

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

# 教學課程：使用 Amazon OpenSearch Service 建立一個搜尋應用程式
<a name="search-example"></a>

使用 ​Amazon OpenSearch Service 建立搜尋應用程式的常見方法是使用 web 表單將使用者查詢傳送到伺服器。接著，您可以授權伺服器直接呼叫 ​OpenSearch API 並讓伺服器將請求傳送至 OpenSearch Service。但是如果您想要編寫不依賴伺服器的用戶端程式碼，您應彌補安全性和效能風險。允許對 ​OpenSearch API 的未簽署、公開存取是不明智的做法。使用者可能會透過過於廣泛的查詢 (或太多查詢) 存取不安全的端點或影響叢集效能。

本章提供解決方案：使用 Amazon API Gateway 將使用者限制為 OpenSearch APIs的子集 AWS Lambda ，並簽署從 API Gateway 到 OpenSearch Service 的請求。

![\[搜索應用程式流程圖。\]](http://docs.aws.amazon.com/zh_tw/opensearch-service/latest/developerguide/images/search-application-diagram.png)


**注意**  
標準 API Gateway​ 和 ​Lambda 定價適用，但在此教學課程的限制使用量之內，成本應微乎其微。

## 先決條件
<a name="search-example-prereq"></a>

此教學的先決條件是 OpenSearch Service​ 網域。如果沒有，請遵循[建立 OpenSearch Service 網域](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 操作來將 5,000 個文件新增到 `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 Service 並傳回結果。由於此範例函數使用外部程式庫，您需要建立部署套件並將其上傳至 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. 導覽至位於 https：//[https://console.aws.amazon.com/lambda/home](https://console.aws.amazon.com/lambda/home ) 的 Lambda 主控台。在左側導覽窗格中，選擇**函數**。

1. 選取 **Create function** (建立函式)。

1. 設定下列欄位：
   + 函數名稱：openearch-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. 導覽至 API Gateway 主控台，網址為 https：//[https://console.aws.amazon.com/apigateway/home](https://console.aws.amazon.com/apigateway/home )。在左側導覽窗格中，選擇 **APIs**。

1. 找到 **REST API** (非私有) 並選擇 **Build** (建置)。

1. 在下列頁面上，找到**建立新 API** 區段，並確認已選取**新 API**。

1. 設定下列欄位：
   + API 名稱：**opensearch-api**
   + 描述：**搜尋 Amazon OpenSearch Service 網域的公有 API**
   + 端點類型：**區域**

1. 選擇**建立 API**。

1. 選擇 **Actions** (動作) 和 **Create Method** (建立方法)。

1. 在下拉式選單中選擇 **GET**，然後按一下核取記號以確認。

1. 進行下列設定，然後選擇 **Save** (儲存)：


| 設定 | Value | 
| --- | --- | 
| 整合類型 | Lambda 函式 | 
| 使用 Lambda 代理整合 | 是 | 
| Lambda 區域 | us-west-1 | 
| Lambda 函式 | opensearch-lambda | 
| 使用預設逾時 | 是 | 

### 設定方法請求
<a name="method-request"></a>

選擇 **Method Request** (方法請求)，然後進行下列設定：


| 設定 | Value | 
| --- | --- | 
| Authorization | NONE | 
| 請求驗證程式 |  驗證查詢字串參數與標頭   | 
| 需要 API 金鑰 | false | 

在 **URL 查詢字串參數**下，選擇**新增查詢字串**並設定下列參數：


| 設定 | Value | 
| --- | --- | 
| 名稱 | q | 
| 必要 |  是  | 

### 部署 API 並設定階段
<a name="deploy-api"></a>

 API Gateway 主控台可讓您透過建立部署並將它與全新或現有階段建立關聯來部署 API。

1. 選擇 **Actions** (動作) 和 **Deploy API** (部署 API)。

1. 對於 **Deployment stage** (部署階段)，選擇 **New Stage** (新增階段) 並將階段命名為 `opensearch-api-test`。

1. 選擇**部署**。

1. 在階段編輯器中進行下列設定，然後選擇 **Save Changes** (儲存變更)：


| 設定 | Value | 
| --- | --- | 
| 啟用調節 | 是 | 
| 速率 |  1000  | 
| 爆量 | 500 | 

這些設定會設定僅有一個方法的 API：`GET` 對端點根進行要求 (`https://some-id.execute-api.us-west-1.amazonaws.com/search-es-api-test`)。要求需要單一參數 (`q`)，要搜尋的查詢字串。呼叫時，方法會將請求傳遞至 Lambda，它會執行 ​`opensearch-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 Service 網域必須允許 ​Lambda 函數對 `movies` 索引進行 ​`GET` 請求。如果您的網域具有啟用了精細存取控制的開放存取政策，可以保持原樣：

------
#### [ 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 Dashboards 中[將角色映射至使用者](fgac.md#fgac-mapping)，否則您將看到許可錯誤。

### 設定 Lambda 執行角色許可
<a name="search-example-lambda-iam"></a>

除了設定網域存取政策之外，您還必須確保 Lambda 執行角色具有存取 OpenSearch Service 網域所需的 IAM 許可。Lambda 函數需要特定許可，取決於您使用的是受管網域或 OpenSearch Service Serverless 集合。

**對於受管 OpenSearch Service 網域：**

將下列 IAM 政策連接至您的 Lambda 執行角色，以允許其向 OpenSearch Service 網域提出請求：

------
#### [ 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 Service Serverless 集合：**

如果您使用的是 OpenSearch Service Serverless，請將下列 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. 導覽至 IAM 主控台，網址為 [https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)。

1. 選擇**角色**並搜尋您的 Lambda 執行角色 （通常名為 `opensearch-lambda-role-xxxxxxxx`)。

1. 選擇**新增許可**，然後選擇**建立內嵌政策**。

1. 選擇 **JSON** 索引標籤，然後從上方貼上適當的政策，將預留位置值取代為實際的資源 ARNs。

1. 選擇**檢閱政策**，提供類似 的名稱`OpenSearchAccess`，然後選擇**建立政策**。

**注意**  
如果沒有這些 IAM 許可，即使網域存取政策允許請求，您的 Lambda 函數也會在嘗試查詢 OpenSearch Service 網域時收到「存取遭拒」錯誤。

如需存取政策的詳細資訊，請參閱[設定存取政策](createupdatedomains.md#createdomain-configure-access-policies)。

## 映射 Lambda 角色 (如果使用精細存取控制)
<a name="search-example-perms-fgac"></a>

精細存取控制會在您可以測試應用程式之前引進額外的步驟。即使您將 HTTP 基本身分驗證用於所有其他目的，您也需要將 Lambda 角色映射至使用者，否則您會看到許可錯誤。

1. 導覽至網域的 OpenSearch Dashboards URL。

1. 從主功能表中，選擇**安全性**、**角色**，然後選取 的連結`all_access`，這是您需要映射 Lambda 角色的角色。

1. 選擇 **Mapped users** (已映射的使用者)、**Manage mapping** (管理映射)。

1. 在 **Backend roles** (後端角色) 下方，新增 Lambda 角色的 Amazon Resource Name (ARN)。ARN 應該採用 的形式`arn:aws:iam::123456789123:role/service-role/opensearch-lambda-role-1abcdefg`。

1. 選擇 **Map** (映射)，並確認使用者或角色顯示在 **Mapped users** (已映射的使用者) 中。

## 步驟 5：測試 Web 應用程式
<a name="search-example-webpage"></a>

**若要測試 web 應用程式**

1. 下載 [sample-site.zip](samples/sample-site.zip)，將其解壓縮並使用您最愛的文字編輯器將 `scripts/search.js`​ 開啟。

1. 更新 `apigatewayendpoint`變數以指向您的 API Gateway 端點，並將反斜線新增至指定路徑的結尾。您可以透過選擇 **Stages** (階段)，然後選取 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_tw/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 (**Actions** (動作)、**Deploy API** (部署 API))。

1. 刪除並重新新增您的 Lambda 函數觸發程序。新增重新新增，選擇**新增觸發**並建立叫用函數的 HTTP 端點。觸發必須具有以下組態：    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/opensearch-service/latest/developerguide/search-example.html)

## 後續步驟
<a name="search-example-next"></a>

本章只是展縣概念的起點。您可以考慮以下修改：
+ 將自己的資料新增到 OpenSearch Service 網域。
+ 將方法新增至 API
+ 在 Lambda​ 函數中，修改搜尋查詢或提升不同的欄位。
+ 樣式結果不同或修改 `search.js`​ 來向使用者顯示不同的欄位。