

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

# 在提取过程中丰富您的文档
<a name="custom-document-enrichment"></a>

**注意**  
功能支持因所用索引类型和搜索 API 而异。要查看您使用的索引类型和搜索 API 是否支持此功能，请参阅[索引类型](https://docs.aws.amazon.com/kendra/latest/dg/hiw-index-types.html)。

在文档提取过程中，您可以更改内容和文档元数据字段或属性。借 Amazon Kendra助 “*自定义文档扩充*” 功能，当您将文档收录到 Amazon Kendra中时，您可以创建、修改或删除文档属性和内容。这意味着您可以根据需要操作和提取数据。

此功能使您可以控制文档的处理和提取到 Amazon Kendra的方式。例如，在将文档摄入文档时，您可以清除文档元数据中的个人身份信息。 Amazon Kendra

使用此功能的另一种方法是在中调用 Lambda 函数， AWS Lambda 对图像运行光学字符识别 (OCR)、对文本进行翻译以及其他任务，以准备用于搜索或分析的数据。例如，您可以调用函数对图像运行 OCR。该函数可以解释图像中的文本，并将每张图像视为文本文档。接收邮寄的客户调查并将这些调查作为图像存储的公司可以将这些图像作为文本文档提取到 Amazon Kendra中。然后，公司可以在 Amazon Kendra中搜索有价值的客户调查信息。

您可以使用基本操作作为数据的首次解析，然后使用 Lambda 函数对数据应用更复杂的操作。例如，您可以使用基本操作简单地删除文档元数据字段“Customer\$1ID”中的所有值，然后应用 Lambda 函数从文档中的文本图像中提取文本。

## 自定义文档富集功能的工作原理
<a name="how-custom-document-enrichment-works"></a>

自定义文档富集的总体过程如下：

1. 在创建或更新数据源时，或者直接将文档编入索引时，可以配置自定义文档扩展 Amazon Kendra。

1. Amazon Kendra 应用内联配置或基本逻辑来更改您的数据。有关更多信息，请参阅 [更改元数据的基本操作](#basic-data-maniplation)。

1. 如果您选择配置高级数据操作，则 Amazon Kendra 可以将其应用于原始原始文档或结构化的、已解析的文档。有关更多信息，请参阅 [Lambda 函数：提取和更改元数据或内容](#advanced-data-manipulation)。

1. 您修改过的文档会被收录到。 Amazon Kendra

在此过程的任何时候，如果您的配置无效， Amazon Kendra 都会引发错误。

当您调用[CreateDataSource[UpdateDataSource](https://docs.aws.amazon.com/kendra/latest/APIReference/API_UpdateDataSource.html)](https://docs.aws.amazon.com/kendra/latest/APIReference/API_CreateDataSource.html)、或时 [BatchPutDocument](https://docs.aws.amazon.com/kendra/latest/APIReference/API_BatchPutDocument.html) APIs，您需要提供您的自定义文档扩充配置。如果您调用 `BatchPutDocument`，则必须为每个请求配置自定义文档富集功能。如果您使用控制台，则可以选择您的索引，然后选择**文档丰富**来配置“自定义文档富集”。

如果您在控制台中使用**文档富集**功能，则可以选择仅配置基本操作或仅配置 Lambda 函数，或者同时配置两者，就像使用 API 一样。您可以在控制台步骤中选择**下一步**，选择不配置基本操作，只配置 Lambda 函数，包括应用于原始（提取前）数据还是结构化（提取后）数据。您只能通过完成控制台中的所有步骤来保存配置。如果您未完成所有步骤，则不会保存您的文档配置。

## 更改元数据的基本操作
<a name="basic-data-maniplation"></a>

您可以使用基本逻辑操作文档字段和内容。这包括移除字段中的值、使用条件修改字段中的值或创建字段。要进行超出基本逻辑操作范围的高级操作，请调用 Lambda 函数。有关更多信息，请参阅 [Lambda 函数：提取和更改元数据或内容](#advanced-data-manipulation)。

要应用基本逻辑，请使用[DocumentAttributeTarget](https://docs.aws.amazon.com/kendra/latest/APIReference/API_DocumentAttributeTarget.html)对象指定要操作的目标字段。您提供属性键。例如，“Department”键是一个字段或属性，其中包含与文档关联的所有部门名称。如果满足特定条件，您还可以指定要在目标字段中使用的值。你可以使用[DocumentAttributeCondition](https://docs.aws.amazon.com/kendra/latest/APIReference/API_DocumentAttributeCondition.html)对象来设置条件。例如，如果“source\$1URI”字段的 URI 值包含“financial”，则使用文档的“Finance”目标值预填写“Department”目标字段。您也可以删除目标文档属性值。

要使用控制台应用基本逻辑，请选择您的索引，然后在导航菜单中选择**文档富集**。转至**配置基本操作**，将基本操作应用于您的文档字段和内容。

以下是使用基本逻辑删除名为“Customer\$1ID”的文档字段中所有客户标识号的示例。

**示例 1：删除与文件关联的客户识别码**

应用基本操作之前的数据。


| **Document\$1ID** | **Body\$1Text** | **Customer\$1ID** | 
| --- | --- | --- | 
| 1 | Lorem Ipsum。 | CID1234 | 
| 2 | Lorem Ipsum。 | CID1235 | 
| 3 | Lorem Ipsum。 | CID1236 | 

应用基本操作后的数据。


| **Document\$1ID** | **Body\$1Text** | **Customer\$1ID** | 
| --- | --- | --- | 
| 1 | Lorem Ipsum。 |   | 
| 2 | Lorem Ipsum。 |   | 
| 3 | Lorem Ipsum。 |   | 

以下是使用基本逻辑创建名为“Department”的字段，并根据来自“Source\$1URI”字段的信息在该字段中预填部门名称的示例。如果“source\$1URI”字段的 URI 值包含“financial”，则使用文档的“Finance”目标值预填写“Department”目标字段。

**示例 2：创建“Department”字段，并使用条件在其中预填与文档关联的部门名称。**

应用基本操作之前的数据。


| **Document\$1ID** | **Body\$1Text** | **Source\$1URI** | 
| --- | --- | --- | 
| 1 | Lorem Ipsum。 | financial/1 | 
| 2 | Lorem Ipsum。 | financial/2 | 
| 3 | Lorem Ipsum。 | financial/3 | 

应用基本操作后的数据。


| **Document\$1ID** | **Body\$1Text** | **Source\$1URI** | **Department** | 
| --- | --- | --- | --- | 
| 1 | Lorem Ipsum。 | financial/1 | 财务 | 
| 2 | Lorem Ipsum。 | financial/2 | 财务 | 
| 3 | Lorem Ipsum。 | financial/3 | 财务 | 

**注意**  
Amazon Kendra 如果目标文档字段尚未创建为索引字段，则无法创建该字段。创建索引字段后，您可以使用创建文档字段`DocumentAttributeTarget`。 Amazon Kendra 然后将新创建的文档元数据字段映射到您的索引字段。

以下代码是配置基本数据操作以删除与文档关联的客户识别码的示例。

------
#### [ Console ]

**配置基本数据操作以删除客户识别码**

1. 在左侧导航窗格的**索引**下，选择**文档富集**，然后选择**添加文档富集**。

1. 在**配置基本操作**页面上，从下拉列表中选择要更改文档字段和内容的数据来源。然后从下拉列表中选择文档字段名称“Customer\$1ID”，从下拉列表中选择索引字段名称“Customer\$1ID”，然后从下拉列表中选择目标操作**删除**。然后选择**添加基本操作**。

------
#### [ CLI ]

**配置基本数据操作以删除客户识别码**

```
aws kendra create-data-source \
 --name data-source-name \
 --index-id index-id \
 --role-arn arn:aws:iam::account-id:role/role-name \
 --type S3 \
 --configuration '{"S3Configuration":{"BucketName":"S3-bucket-name"}}' \
 --custom-document-enrichment-configuration '{"InlineConfigurations":[{"Target":{"TargetDocumentAttributeKey":"Customer_ID", "TargetDocumentAttributeValueDeletion": true}}]}'
```

------
#### [ Python ]

**配置基本数据操作以删除客户识别码**

```
import boto3
from botocore.exceptions import ClientError
import pprint
import time

kendra = boto3.client("kendra")

print("Create a data source with customizations")

# Provide the name of the data source
name = "data-source-name"
# Provide the index ID for the data source
index_id = "index-id"
# Provide the IAM role ARN required for data sources
role_arn = "arn:aws:iam::${account-id}:role/${role-name}"
# Provide the data source connection information
data_source_type = "S3"
S3_bucket_name = "S3-bucket-name"
# Configure the data source with Custom Document Enrichment
configuration = {"S3Configuration":
        {
            "BucketName": S3_bucket_name
        }
    }
custom_document_enrichment_configuration = {"InlineConfigurations":[
        {
            "Target":{"TargetDocumentAttributeKey":"Customer_ID",
                       "TargetDocumentAttributeValueDeletion": True}
        }]
    }

try:
    data_source_response = kendra.create_data_source(
        Name = name,
        IndexId = index_id,
        RoleArn = role_arn,
        Type = data_source_type
        Configuration = configuration
        CustomDocumentEnrichmentConfiguration = custom_document_enrichment_configuration
    )

    pprint.pprint(data_source_response)

    data_source_id = data_source_response["Id"]

    print("Wait for Amazon Kendra to create the data source with your customizations.")

    while True:
        # Get the details of the data source, such as the status
        data_source_description = kendra.describe_data_source(
            Id = data_source_id,
            IndexId = index_id
        )
        status = data_source_description["Status"]
        print(" Creating data source. Status: "+status)
        time.sleep(60)
        if status != "CREATING":
            break

    print("Synchronize the data source.")

    sync_response = kendra.start_data_source_sync_job(
        Id = data_source_id,
        IndexId = index_id
    )

    pprint.pprint(sync_response)

    print("Wait for the data source to sync with the index.")

    while True:

        jobs = kendra.list_data_source_sync_jobs(
            Id= data_source_id,
            IndexId= index_id
        )

        # For this example, there should be one job
        status = jobs["History"][0]["Status"]

        print(" Syncing data source. Status: "+status)
        time.sleep(60)
        if status != "SYNCING":
            break

except  ClientError as e:
        print("%s" % e)

print("Program ends.")
```

------
#### [ Java ]

**配置基本数据操作以删除客户识别码**

```
package com.amazonaws.kendra;

import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.services.kendra.KendraClient;
import software.amazon.awssdk.services.kendra.model.CreateDataSourceRequest;
import software.amazon.awssdk.services.kendra.model.CreateDataSourceResponse;
import software.amazon.awssdk.services.kendra.model.CreateIndexRequest;
import software.amazon.awssdk.services.kendra.model.CreateIndexResponse;
import software.amazon.awssdk.services.kendra.model.DataSourceConfiguration;
import software.amazon.awssdk.services.kendra.model.DataSourceStatus;
import software.amazon.awssdk.services.kendra.model.DataSourceSyncJob;
import software.amazon.awssdk.services.kendra.model.DataSourceSyncJobStatus;
import software.amazon.awssdk.services.kendra.model.DataSourceType;
import software.amazon.awssdk.services.kendra.model.DescribeDataSourceRequest;
import software.amazon.awssdk.services.kendra.model.DescribeDataSourceResponse;
import software.amazon.awssdk.services.kendra.model.DescribeIndexRequest;
import software.amazon.awssdk.services.kendra.model.DescribeIndexResponse;
import software.amazon.awssdk.services.kendra.model.IndexStatus;
import software.amazon.awssdk.services.kendra.model.ListDataSourceSyncJobsRequest;
import software.amazon.awssdk.services.kendra.model.ListDataSourceSyncJobsResponse;
import software.amazon.awssdk.services.kendra.model.S3DataSourceConfiguration;
import software.amazon.awssdk.services.kendra.model.StartDataSourceSyncJobRequest;
import software.amazon.awssdk.services.kendra.model.StartDataSourceSyncJobResponse;

public class CreateDataSourceWithCustomizationsExample {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Create a data source with customizations");
        
        String dataSourceName = "data-source-name";
        String indexId = "index-id";
        String dataSourceRoleArn = "arn:aws:iam::account-id:role/role-name";
        String s3BucketName = "S3-bucket-name"

        KendraClient kendra = KendraClient.builder().build();
        
        CreateDataSourceRequest createDataSourceRequest = CreateDataSourceRequest
            .builder()
            .name(dataSourceName)
            .description(experienceDescription)
            .roleArn(experienceRoleArn)
            .type(DataSourceType.S3)
            .configuration(
                DataSourceConfiguration
                    .builder()
                    .s3Configuration(
                        S3DataSourceConfiguration
                            .builder()
                            .bucketName(s3BucketName)
                            .build()
                    ).build()
            )
            .customDocumentEnrichmentConfiguration(
                CustomDocumentEnrichmentConfiguration
                    .builder()
                    .inlineConfigurations(Arrays.asList(
                        InlineCustomDocumentEnrichmentConfiguration
                            .builder()
                            .target(
                                DocumentAttributeTarget
                                    .builder()
                                    .targetDocumentAttributeKey("Customer_ID")
                                    .targetDocumentAttributeValueDeletion(true)
                                    .build())
                            .build()
                    )).build();
        
        CreateDataSourceResponse createDataSourceResponse = kendra.createDataSource(createDataSourceRequest);
        System.out.println(String.format("Response of creating data source: %s", createDataSourceResponse));

        String dataSourceId = createDataSourceResponse.id();
        System.out.println(String.format("Waiting for Kendra to create the data source %s", dataSourceId));
        DescribeDataSourceRequest describeDataSourceRequest = DescribeDataSourceRequest
            .builder()
            .indexId(indexId)
            .id(dataSourceId)
            .build();

        while (true) {
            DescribeDataSourceResponse describeDataSourceResponse = kendra.describeDataSource(describeDataSourceRequest);

            DataSourceStatus status = describeDataSourceResponse.status();
            System.out.println(String.format("Creating data source. Status: %s", status));
            TimeUnit.SECONDS.sleep(60);
            if (status != DataSourceStatus.CREATING) {
                break;
            }
        }

        System.out.println(String.format("Synchronize the data source %s", dataSourceId));
        StartDataSourceSyncJobRequest startDataSourceSyncJobRequest = StartDataSourceSyncJobRequest
            .builder()
            .indexId(indexId)
            .id(dataSourceId)
            .build();
        StartDataSourceSyncJobResponse startDataSourceSyncJobResponse = kendra.startDataSourceSyncJob(startDataSourceSyncJobRequest);
        System.out.println(String.format("Waiting for the data source to sync with the index %s for execution ID %s", indexId, startDataSourceSyncJobResponse.executionId()));

        // For this example, there should be one job
        ListDataSourceSyncJobsRequest listDataSourceSyncJobsRequest = ListDataSourceSyncJobsRequest
            .builder()
            .indexId(indexId)
            .id(dataSourceId)
            .build();

        while (true) {
            ListDataSourceSyncJobsResponse listDataSourceSyncJobsResponse = kendra.listDataSourceSyncJobs(listDataSourceSyncJobsRequest);
            DataSourceSyncJob job = listDataSourceSyncJobsResponse.history().get(0);
            System.out.println(String.format("Syncing data source. Status: %s", job.status()));

            TimeUnit.SECONDS.sleep(60);
            if (job.status() != DataSourceSyncJobStatus.SYNCING) {
                break;
            }

        }

        System.out.println("Data source creation with customizations is complete");
    }
}
```

------

## Lambda 函数：提取和更改元数据或内容
<a name="advanced-data-manipulation"></a>

您可以使用 Lambda 函数操作您的文档字段和内容。如果您想超越基本逻辑并应用高级数据操作，这很有用。例如，使用光学字符识别（OCR），它可以解释图像中的文本，并将每张图像视为文本文档。或者，检索特定时区的当前日期时间，然后在日期字段的空值处插入日期时间。

您可以先应用基本逻辑，然后使用 Lambda 函数进一步操作数据，反之亦然。您也可以选择仅应用 Lambda 函数。

Amazon Kendra 可以调用 Lambda 函数，在摄取过程中应用高级数据操作，作为您的一部分。[CustomDocumentEnrichmentConfiguration](https://docs.aws.amazon.com/kendra/latest/APIReference/API_CustomDocumentEnrichmentConfiguration.html)[您指定的角色包括执行 Lambda 函数和访问 Amazon S3 存储桶以存储数据操作IAM 输出的权限，请参阅访问角色。](https://docs.aws.amazon.com/kendra/latest/dg/iam-roles.html)

Amazon Kendra 可以将 Lambda 函数应用于您的原始原始文档或结构化的、已解析的文档。您可以配置一个 Lambda 函数，该函数使用您的原始数据或原始数据并应用您的数据操作。[PreExtractionHookConfiguration](https://docs.aws.amazon.com/kendra/latest/APIReference/API_CustomDocumentEnrichmentConfiguration.html)您还可以配置一个 Lambda 函数，该函数使用您的结构化文档并应用您的数据操作。[PostExtractionHookConfiguration](https://docs.aws.amazon.com/kendra/latest/APIReference/API_CustomDocumentEnrichmentConfiguration.html) Amazon Kendra 提取文档元数据和文本以构建您的文档。您的 Lambda 函数必须遵循必需的请求和响应结构。有关更多信息，请参阅 [Lambda 函数的数据合约](#cde-data-contracts-lambda)。

要在控制台中配置 Lambda 函数，请选择您的索引，然后在导航菜单中选择**文档富集**。前往**配置 Lambda 函数**来配置 Lambda 函数。

您只能为 `PreExtractionHookConfiguration` 配置一个 Lambda 函数，只能为 `PostExtractionHookConfiguration` 配置一个 Lambda 函数。然而，Lambda 函数可在需要时调用其他函数。您可以同时配置 `PreExtractionHookConfiguration` 和/或 `PostExtractionHookConfiguration`。适用于 `PreExtractionHookConfiguration` 的 Lambda 函数的运行时间不得超过 5 分钟，适用于 `PostExtractionHookConfiguration` 的 Lambda 函数的运行时间不得超过 1 分钟。配置自定义文档扩充功能自然要 Amazon Kendra 比不配置时更长的时间才能将您的文档收录到其中。

您可以配置 Amazon Kendra 为仅在满足条件时调用 Lambda 函数。例如，您可以指定一个条件，即如果日期时间值为空，则 Amazon Kendra 应调用插入当前日期时间的函数。

以下是使用 Lambda 函数运行 OCR 来解释图像中的文本并将此文本存储在名为“Document\$1Image\$1Text”的字段中的示例。

**示例 1：从图像中提取文本以创建文本文档**

应用高级操作之前的数据。


| **Document\$1ID** | **Document\$1Image** | 
| --- | --- | 
| 1 | image\$11.png | 
| 2 | image\$12.png | 
| 3 | image\$13.png | 

应用高级操作后的数据。


| **Document\$1ID** | **Document\$1Image** | **Document\$1Image\$1Text** | 
| --- | --- | --- | 
| 1 | image\$11.png | 邮寄的调查回复 | 
| 2 | image\$12.png | 邮寄的调查回复 | 
| 3 | image\$13.png | 邮寄的调查回复 | 

以下是使用 Lambda 函数为空日期值插入当前日期时间的示例。这使用的条件是，如果日期字段值为“null”，则将其替换为当前日期时间。

**示例 2：将 Last\$1Updated 字段中的空值替换为当前日期时间。**

应用高级操作之前的数据。


| **Document\$1ID** | **Body\$1Text** | **Last\$1Updated** | 
| --- | --- | --- | 
| 1 | Lorem Ipsum。 | 2020 年 1 月 1 日 | 
| 2 | Lorem Ipsum。 |   | 
| 3 | Lorem Ipsum。 | 2020 年 7 月 1 日 | 

应用高级操作后的数据。


| **Document\$1ID** | **Body\$1Text** | **Last\$1Updated** | 
| --- | --- | --- | 
| 1 | Lorem Ipsum。 | 2020 年 1 月 1 日 | 
| 2 | Lorem Ipsum。 | 2021 年 12 月 1 日 | 
| 3 | Lorem Ipsum。 | 2020 年 7 月 1 日 | 

以下代码是配置 Lambda 函数以对原始原始数据进行高级数据操作的示例。

------
#### [ Console ]

**配置 Lambda 函数，以便对原始原始数据进行高级数据操作**

1. 在左侧导航窗格的**索引**下，选择**文档富集**，然后选择**添加文档富集**。

1. 在 “**配置 Lambda 函数**” 页面的 “**用于预提取的 Lambda**” 部分，从下拉列表中选择您的 Lambda 函数 ARN 和您的存储桶。 Amazon S3 通过从下拉列表中选择创建新角色的选项来添加您的 IAM 访问角色。这将创建创建文档丰富功能所需的 Amazon Kendra 权限。

------
#### [ CLI ]

**配置 Lambda 函数，以便对原始原始数据进行高级数据操作**

```
aws kendra create-data-source \
 --name data-source-name \
 --index-id index-id \
 --role-arn arn:aws:iam::account-id:role/role-name \
 --type S3 \
 --configuration '{"S3Configuration":{"BucketName":"S3-bucket-name"}}' \
 --custom-document-enrichment-configuration '{"PreExtractionHookConfiguration":{"LambdaArn":"arn:aws:iam::account-id:function/function-name", "S3Bucket":"S3-bucket-name"}, "RoleArn": "arn:aws:iam:account-id:role/cde-role-name"}'
```

------
#### [ Python ]

**配置 Lambda 函数，以便对原始原始数据进行高级数据操作**

```
import boto3
from botocore.exceptions import ClientError
import pprint
import time

kendra = boto3.client("kendra")

print("Create a data source with customizations.")

# Provide the name of the data source
name = "data-source-name"
# Provide the index ID for the data source
index_id = "index-id"
# Provide the IAM role ARN required for data sources
role_arn = "arn:aws:iam::${account-id}:role/${role-name}"
# Provide the data source connection information
data_source_type = "S3"
S3_bucket_name = "S3-bucket-name"
# Configure the data source with Custom Document Enrichment
configuration = {"S3Configuration":
        {
            "BucketName": S3_bucket_name
        }
    }
custom_document_enrichment_configuration = {"PreExtractionHookConfiguration":
        {
            "LambdaArn":"arn:aws:iam::account-id:function/function-name",
            "S3Bucket":"S3-bucket-name"
        }
    "RoleArn":"arn:aws:iam::account-id:role/cde-role-name"
    }

try:
    data_source_response = kendra.create_data_source(
        Name = name,
        IndexId = index_id,
        RoleArn = role_arn,
        Type = data_source_type
        Configuration = configuration
        CustomDocumentEnrichmentConfiguration = custom_document_enrichment_configuration
    )

    pprint.pprint(data_source_response)

    data_source_id = data_source_response["Id"]

    print("Wait for Amazon Kendra to create the data source with your customizations.")

    while True:
        # Get the details of the data source, such as the status
        data_source_description = kendra.describe_data_source(
            Id = data_source_id,
            IndexId = index_id
        )
        status = data_source_description["Status"]
        print(" Creating data source. Status: "+status)
        time.sleep(60)
        if status != "CREATING":
            break

    print("Synchronize the data source.")

    sync_response = kendra.start_data_source_sync_job(
        Id = data_source_id,
        IndexId = index_id
    )

    pprint.pprint(sync_response)

    print("Wait for the data source to sync with the index.")

    while True:

        jobs = kendra.list_data_source_sync_jobs(
            Id = data_source_id,
            IndexId = index_id
        )

        # For this example, there should be one job
        status = jobs["History"][0]["Status"]

        print(" Syncing data source. Status: "+status)
        time.sleep(60)
        if status != "SYNCING":
            break

except  ClientError as e:
        print("%s" % e)

print("Program ends.")
```

------
#### [ Java ]

**配置 Lambda 函数，以便对原始原始数据进行高级数据操作**

```
package com.amazonaws.kendra;

import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.services.kendra.KendraClient;
import software.amazon.awssdk.services.kendra.model.CreateDataSourceRequest;
import software.amazon.awssdk.services.kendra.model.CreateDataSourceResponse;
import software.amazon.awssdk.services.kendra.model.CreateIndexRequest;
import software.amazon.awssdk.services.kendra.model.CreateIndexResponse;
import software.amazon.awssdk.services.kendra.model.DataSourceConfiguration;
import software.amazon.awssdk.services.kendra.model.DataSourceStatus;
import software.amazon.awssdk.services.kendra.model.DataSourceSyncJob;
import software.amazon.awssdk.services.kendra.model.DataSourceSyncJobStatus;
import software.amazon.awssdk.services.kendra.model.DataSourceType;
import software.amazon.awssdk.services.kendra.model.DescribeDataSourceRequest;
import software.amazon.awssdk.services.kendra.model.DescribeDataSourceResponse;
import software.amazon.awssdk.services.kendra.model.DescribeIndexRequest;
import software.amazon.awssdk.services.kendra.model.DescribeIndexResponse;
import software.amazon.awssdk.services.kendra.model.IndexStatus;
import software.amazon.awssdk.services.kendra.model.ListDataSourceSyncJobsRequest;
import software.amazon.awssdk.services.kendra.model.ListDataSourceSyncJobsResponse;
import software.amazon.awssdk.services.kendra.model.S3DataSourceConfiguration;
import software.amazon.awssdk.services.kendra.model.StartDataSourceSyncJobRequest;
import software.amazon.awssdk.services.kendra.model.StartDataSourceSyncJobResponse;


public class CreateDataSourceWithCustomizationsExample {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Create a data source with customizations");
        
        String dataSourceName = "data-source-name";
        String indexId = "index-id";
        String dataSourceRoleArn = "arn:aws:iam::account-id:role/role-name";
        String s3BucketName = "S3-bucket-name"

        KendraClient kendra = KendraClient.builder().build();
        
        CreateDataSourceRequest createDataSourceRequest = CreateDataSourceRequest
            .builder()
            .name(dataSourceName)
            .description(experienceDescription)
            .roleArn(experienceRoleArn)
            .type(DataSourceType.S3)
            .configuration(
                DataSourceConfiguration
                    .builder()
                    .s3Configuration(
                        S3DataSourceConfiguration
                            .builder()
                            .bucketName(s3BucketName)
                            .build()
                    ).build()
            )
            .customDocumentEnrichmentConfiguration(
                CustomDocumentEnrichmentConfiguration
                    .builder()
                    .preExtractionHookConfiguration(
                        HookConfiguration
                            .builder()
                            .lambdaArn("arn:aws:iam::account-id:function/function-name")
                            .s3Bucket("S3-bucket-name")
                            .build())
                    .roleArn("arn:aws:iam::account-id:role/cde-role-name")
                    .build();
        
        CreateDataSourceResponse createDataSourceResponse = kendra.createDataSource(createDataSourceRequest);
        System.out.println(String.format("Response of creating data source: %s", createDataSourceResponse));

        String dataSourceId = createDataSourceResponse.id();
        System.out.println(String.format("Waiting for Kendra to create the data source %s", dataSourceId));
        DescribeDataSourceRequest describeDataSourceRequest = DescribeDataSourceRequest
            .builder()
            .indexId(indexId)
            .id(dataSourceId)
            .build();

        while (true) {
            DescribeDataSourceResponse describeDataSourceResponse = kendra.describeDataSource(describeDataSourceRequest);

            DataSourceStatus status = describeDataSourceResponse.status();
            System.out.println(String.format("Creating data source. Status: %s", status));
            TimeUnit.SECONDS.sleep(60);
            if (status != DataSourceStatus.CREATING) {
                break;
            }
        }

        System.out.println(String.format("Synchronize the data source %s", dataSourceId));
        StartDataSourceSyncJobRequest startDataSourceSyncJobRequest = StartDataSourceSyncJobRequest
            .builder()
            .indexId(indexId)
            .id(dataSourceId)
            .build();
        StartDataSourceSyncJobResponse startDataSourceSyncJobResponse = kendra.startDataSourceSyncJob(startDataSourceSyncJobRequest);
        System.out.println(String.format("Waiting for the data source to sync with the index %s for execution ID %s", indexId, startDataSourceSyncJobResponse.executionId()));

        // For this example, there should be one job
        ListDataSourceSyncJobsRequest listDataSourceSyncJobsRequest = ListDataSourceSyncJobsRequest
            .builder()
            .indexId(indexId)
            .id(dataSourceId)
            .build();

        while (true) {
            ListDataSourceSyncJobsResponse listDataSourceSyncJobsResponse = kendra.listDataSourceSyncJobs(listDataSourceSyncJobsRequest);
            DataSourceSyncJob job = listDataSourceSyncJobsResponse.history().get(0);
            System.out.println(String.format("Syncing data source. Status: %s", job.status()));

            TimeUnit.SECONDS.sleep(60);
            if (job.status() != DataSourceSyncJobStatus.SYNCING) {
                break;
            }

        }

        System.out.println("Data source creation with customizations is complete");
    }
}
```

------

## Lambda 函数的数据合约
<a name="cde-data-contracts-lambda"></a>

用于高级数据操作的 Lambda 函数与 Amazon Kendra 数据合约交互。合约是您的 Lambda 函数的必备请求和响应结构。如果您的 Lambda 函数不遵循这些结构，则 Amazon Kendra 会引发错误。

适用于 `PreExtractionHookConfiguration` 的 Lambda 函数应采用以下请求结构：

```
{
    "version": <str>,
    "dataBlobStringEncodedInBase64": <str>, //In the case of a data blob
    "s3Bucket": <str>, //In the case of an S3 bucket
    "s3ObjectKey": <str>, //In the case of an S3 bucket
    "metadata": <Metadata>
}
```

包括 `CustomDocumentAttribute` 结构在内的 `metadata` 结构如下所示：

```
{
    "attributes": [<CustomDocumentAttribute<]
}

CustomDocumentAttribute
{
    "name": <str>,
    "value": <CustomDocumentAttributeValue>
}

CustomDocumentAttributeValue
{
    "stringValue": <str>,
    "integerValue": <int>,
    "longValue": <long>,
    "stringListValue": list<str>,
    "dateValue": <str>
}
```

`PreExtractionHookConfiguration` 的 Lambda 函数必须符合以下响应结构：

```
{
    "version": <str>,
    "dataBlobStringEncodedInBase64": <str>, //In the case of a data blob
    "s3ObjectKey": <str>, //In the case of an S3 bucket
    "metadataUpdates": [<CustomDocumentAttribute>]
}
```

适用于 `PostExtractionHookConfiguration` 的 Lambda 函数应采用以下请求结构：

```
{
    "version": <str>,
    "s3Bucket": <str>,
    "s3ObjectKey": <str>,
    "metadata": <Metadata>
}
```

`PostExtractionHookConfiguration` 的 Lambda 函数必须符合以下响应结构：

```
PostExtractionHookConfiguration Lambda Response
{
    "version": <str>,
    "s3ObjectKey": <str>,
    "metadataUpdates": [<CustomDocumentAttribute>]
}
```

您修改过的文档已上传到您的 Amazon S3 存储桶。修改后的文档必须遵循 [结构化的文档格式](#structured-document-format) 中显示的格式。

### 结构化的文档格式
<a name="structured-document-format"></a>

Amazon Kendra 将您的结构化文档上传到给定的 Amazon S3 存储桶。结构化文档遵循以下格式：

```
Kendra document

{
   "textContent": <TextContent>
}

TextContent
{
  "documentBodyText": <str>
}
```

### 遵守数据合同的 Lambda 函数示例
<a name="example-lambda-function-advanced-manipulation"></a>

以下 Python 代码是 Lambda 函数的示例，该函数对元数据字段 `_authors`、`_document_title` 以及原始文档或原始文档的正文内容进行高级操作。

**如果正文内容存放在 Amazon S3 桶中**

```
import json
import boto3
     
s3 = boto3.client("s3")

# Lambda function for advanced data manipulation    
def lambda_handler(event, context):
    # Get the value of "S3Bucket" key name or item from the given event input
    s3_bucket = event.get("s3Bucket")
    # Get the value of "S3ObjectKey" key name or item from the given event input
    s3_object_key = event.get("s3ObjectKey")
    
    content_object_before_CDE = s3.get_object(Bucket = s3_bucket, Key = s3_object_key)
    content_before_CDE = content_object_before_CDE["Body"].read().decode("utf-8");
    content_after_CDE = "CDEInvolved " + content_before_CDE
    
    # Get the value of "metadata" key name or item from the given event input
    metadata = event.get("metadata")
    # Get the document "attributes" from the metadata 
    document_attributes = metadata.get("attributes")
    
    s3.put_object(Bucket = s3_bucket, Key = "dummy_updated_kendra_document", Body=json.dumps(content_after_CDE))
    return {
        "version": "v0",
        "s3ObjectKey": "dummy_updated_kendra_document",
        "metadataUpdates": [
            {"name":"_document_title", "value":{"stringValue":"title_from_pre_extraction_lambda"}},
            {"name":"_authors", "value":{"stringListValue":["author1", "author2"]}}
        ]
    }
```

**如果正文内容驻留在数据块中**

```
import json
import boto3
import base64

# Lambda function for advanced data manipulation
def lambda_handler(event, context):
    
    # Get the value of "dataBlobStringEncodedInBase64" key name or item from the given event input 
    data_blob_string_encoded_in_base64 = event.get("dataBlobStringEncodedInBase64")
    # Decode the data blob string in UTF-8
    data_blob_string = base64.b64decode(data_blob_string_encoded_in_base64).decode("utf-8")
    # Get the value of "metadata" key name or item from the given event input    
    metadata = event.get("metadata")
    # Get the document "attributes" from the metadata
    document_attributes = metadata.get("attributes")
    
    new_data_blob = "This should be the modified data in the document by pre processing lambda ".encode("utf-8")
    return {
        "version": "v0",
        "dataBlobStringEncodedInBase64": base64.b64encode(new_data_blob).decode("utf-8"),
        "metadataUpdates": [
            {"name":"_document_title", "value":{"stringValue":"title_from_pre_extraction_lambda"}},
            {"name":"_authors", "value":{"stringListValue":["author1", "author2"]}}
        ]
    }
```

以下 Python 代码是 Lambda 函数的示例，该函数应用了对元数据字段 `_authors`、`_document_title` 的高级操作，以及结构化或已解析文档的正文内容。

```
import json
import boto3
import time

s3 = boto3.client("s3")

# Lambda function for advanced data manipulation
def lambda_handler(event, context):
    
    # Get the value of "S3Bucket" key name or item from the given event input
    s3_bucket = event.get("s3Bucket")
    # Get the value of "S3ObjectKey" key name or item from the given event input
    s3_key = event.get("s3ObjectKey")
    # Get the value of "metadata" key name or item from the given event input
    metadata = event.get("metadata")
    # Get the document "attributes" from the metadata 
    document_attributes = metadata.get("attributes")
    
    kendra_document_object = s3.get_object(Bucket = s3_bucket, Key = s3_key)
    kendra_document_string = kendra_document_object['Body'].read().decode('utf-8')
    kendra_document = json.loads(kendra_document_string)
    kendra_document["textContent"]["documentBodyText"] = "Changing document body to a short sentence."
    
    s3.put_object(Bucket = s3_bucket, Key = "dummy_updated_kendra_document", Body=json.dumps(kendra_document))

    return {
        "version" : "v0",
        "s3ObjectKey": "dummy_updated_kendra_document",
        "metadataUpdates": [
            {"name": "_document_title", "value":{"stringValue": "title_from_post_extraction_lambda"}},
            {"name": "_authors", "value":{"stringListValue":["author1", "author2"]}}
        ]
    }
```