11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenSearchにどんなドキュメントが取り込まれているか確認したい👀

Last updated at Posted at 2025-12-14

はじめに

Amazon Bedrock Knowledge Base のベクトルデータベース OpenSearch Serverlessにどんなドキュメントが取り込まれているのか確認するべく実装方法を調査しました!

やりたいこと

  • OpenSearch Serverlessにどんなドキュメントが取り込まれているか確認したい
  • UIから確認するのではなくOpenSearch Serverlessへ直接クエリしたい

環境・方法

  • OpenSearch Serverless
  • Java 8

Java8で開発する必要がありOpenSearch公式のSDKを使用できなかったため少し遠回りをしてます😅

AWS Signature Version 4で署名したHttpリクエストを、AssumeRoleによる一時的な認証情報を用いてRESTクライアント経由でOpenSearch Serverlessに送信しています。

サンプルコード

デフォルトインデックスである bedrock-knowledge-base-default-index に何が保存されているのか、Search API(_search)で検索しています。
/bedrock-knowledge-base-default-index/_search
※一部抜粋

java
// 認証情報プロバイダーの生成
AwsBasicCredentials basicCredentials = AwsBasicCredentials.create(accessKey, secretKey);
StsClient stsClient = StsClient.builder()
    .region(Region.of(region))
    .credentialsProvider(StaticCredentialsProvider.create(basicCredentials))
    .build();
AssumeRoleRequest assumeRoleRequest = AssumeRoleRequest.builder()
    .roleArn(roleArn)
    .roleSessionName(roleSessionName)
    .externalId(externalId)
    .durationSeconds(3600)
    .build();
StsAssumeRoleCredentialsProvider credentialsProvider = StsAssumeRoleCredentialsProvider.builder()
    .stsClient(stsClient)
    .refreshRequest(assumeRoleRequest)
    .build();

// Aws4Signerを使ったインターセプター
AwsRequestSigningInterceptor interceptor =
    new AwsRequestSigningInterceptor(serviceName, Region.of(region), endpoint, credentialsProvider);

// RestClientの初期化
HttpHost proxy = new HttpHost(proxyHost, proxyPort, "http");
RestClient restClient = RestClient.builder(new HttpHost(endpoint, 443, "https"))
    .setHttpClientConfigCallback(httpClientBuilder -> {
        httpClientBuilder.setProxy(proxy);
        httpClientBuilder.addInterceptorLast(interceptor);
        return httpClientBuilder;
    })
    .build();

// OpenSearch APIリクエスト送信
ObjectMapper mapper = new ObjectMapper();
ObjectNode query = mapper.createObjectNode();
// ...クエリ構築処理...
String queryJson = mapper.writeValueAsString(query);

Request request = new Request("POST", "/bedrock-knowledge-base-default-index/_search");
request.setJsonEntity(queryJson);
Response response = restClient.performRequest(request);

restClient.close();
stsClient.close();

// レスポンス取得
String result;
try (InputStream responseStream = response.getEntity().getContent()) {
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    byte[] chunk = new byte[16384];
    int bytesRead;
    while ((bytesRead = responseStream.read(chunk)) != -1) {
        buffer.write(chunk, 0, bytesRead);
    }
    result = new String(buffer.toByteArray(), StandardCharsets.UTF_8);
}

レスポンス

ドキュメントごとにまとまるよう成型したものがこちら(一部のみ)↓
※実際のレスポンスの値を一部編集しています
これでどんなドキュメントが取り込まれているか確認できるようになりました!
これを確認しデータの差分更新に活用できたり!

{
    "took": 24,
    "timed_out": false,
    "_shards": {
        "total": 0,
        "successful": 0,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 259,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "unique_files": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "https://app.box.com/file/11111",
                    "doc_count": 106,
                    "sample": {
                        "hits": {
                            "total": {
                                "value": 106,
                                "relation": "eq"
                            },
                            "max_score": 0.06503199,
                            "hits": [
                                {
                                    "_index": "bedrock-knowledge-base-default-index",
                                    "_id": "xxxx",
                                    "_score": 0.06503199,
                                    "_source": {
                                        "last_updated": "1756946477000",
                                        "update_user": "11111",
                                        "size": "67628",
                                        "file_name": "人事規程.docx",
                                        "create_user": "22222",
                                        "create_date": "1756946477000",
                                        "url": "https://app.box.com/file/11111"
                                    }
                                }
                            ]
                        }
                    }
                },

躓いたところ

長らく403エラーに苦しめられていましたが(😭)原因はx-amz-content-sha256ヘッダーが足りないということでした。
PUT or POST リクエスト (BODYがある) 場合、x-amz-content-sha256 というヘッダーの付与が必要になるようなのです。

そもそもその認識がなかったので全く気づきませんでした。。。

そこでヘッダーが追加されるよう明示的に指定したところ解決しました。
※一部抜粋

 .putHeader("x-amz-content-sha256", "required")

今回、私はこの一文を追加することで解決できましたが、他にも方法はたくさんあるのではないかと思っています!

まとめ

公式SDKを使えていたらあっという間に、何にも躓かず実装できたかもしれません。
ですが、コードを実行した裏では私が知らないだけで、多くのルールに則って必要なやり取りが行われていることをこの実装を通して学びました。とても勉強になりました。そしてSDKなどを使用したらおそらく楽に実装できるようになっているのだろうな~と思うとSDKすごい!有難い!という気持ちになりました。
また、AssumeRoleによる一時的な認証情報を利用することで、よりセキュアにOpenSearch Serverlessへアクセスできることも学びの一つでした。
今回のように常にベストプラクティスが実践できるとは限らないので、そのような場面に出くわした際は今回のような経験が活きてくるのではないかと思っています。
このあたりが怪しい?といったあたりを付ける選択肢が増え、知識に一つ深みが出たように感じます!

参考

・一般的なリクエストヘッダー
https://docs.aws.amazon.com/ja_jp/amazonglacier/latest/dev/api-common-request-headers.html
・Signature Calculations for the Authorization Header: Transferring Payload in a Single Chunk (AWS Signature Version 4)
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
・Search API
https://docs.opensearch.org/latest/api-reference/search-apis/search/

11
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?