はじめに
2026年6月に、AWS Glue Data Catalog に ビジネスコンテキストとセマンティック検索 のプレビューが発表されました。
これまでのData Catalogは「テーブル名」「カラム名」「パーティション」といった技術的メタデータの管理が中心でした。今回のアップデートにより、ビジネスの意味に基づいてデータを発見・理解できるようになります。
本記事では、この機能をゼロから一通り試した手順を、ハマりポイントも含めて詳細にまとめます。
この機能でできること
- Glue Data Catalog のテーブルに 用語集(Glossary) や カスタムメタデータ(Form) を追加
- セマンティック検索(SearchAssets API) でビジネスの意味に基づいてデータアセットを発見
- MCP互換のAIエージェント(Claude Code、Kiro、Cursor等)からカタログに接続
対応リージョン(プレビュー)
- us-east-1(バージニア北部)
- us-east-2(オハイオ)
- us-west-2(オレゴン)
- eu-west-1(アイルランド)
東京リージョン(ap-northeast-1)は2026年8月時点で未対応です。
前提条件
- AWSアカウント
- AWS CLIがインストール・設定済み
- Python 3 + boto3(後述する理由で必要)
- 対応リージョンで操作可能なIAMロール
必要なIAMポリシー
ビジネスコンテキスト関連のアクションに加え、通常のGlue操作(CreateDatabase, CreateTable 等)やS3操作権限も必要です。
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"glue:SearchAssets", "glue:PutAsset", "glue:GetAsset", "glue:DeleteAsset",
"glue:PutAssetType", "glue:GetAssetType", "glue:DeleteAssetType", "glue:ListAssetTypes",
"glue:CreateGlossary", "glue:UpdateGlossary", "glue:GetGlossary", "glue:ListGlossaries", "glue:DeleteGlossary",
"glue:CreateGlossaryTerm", "glue:UpdateGlossaryTerm", "glue:GetGlossaryTerm", "glue:ListGlossaryTerms", "glue:DeleteGlossaryTerm",
"glue:AssociateGlossaryTerms", "glue:DisassociateGlossaryTerms",
"glue:PutFormType", "glue:GetFormType", "glue:DeleteFormType", "glue:ListFormTypes",
"glue:PutAttachment", "glue:DeleteAttachment",
"glue:ListIterableForms", "glue:BatchGetIterableForms"
],
"Resource": "*"
}]
}
⚠️ 最大のハマりポイント:AWS CLI / boto3 が未対応
最初に最も重要な注意点をお伝えします。
2026年8月時点で、ビジネスコンテキスト関連のAPI(CreateGlossary, SearchAssets 等)は AWS CLI および boto3 に未実装 です。
$ aws glue create-glossary --name "test" --region us-east-1
aws: error: argument operation: Invalid choice
import boto3 # v1.42.97
client = boto3.Session().client('glue')
methods = [m for m in dir(client) if 'glossary' in m.lower()]
print(methods) # => [] 空!
解決策:SigV4署名付き直接API呼び出し
Glue APIはJSON 1.1プロトコルを使用しており、X-Amz-Target ヘッダーでアクション名を指定します。botocore の SigV4Auth を使って署名を生成し、直接HTTPリクエストを送信します。
以降のステップで使う共通ヘルパー関数は以下の通りです:
import json
import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
import http.client
PROFILE = 'YOUR_PROFILE'
REGION = 'us-east-1'
session = boto3.Session(profile_name=PROFILE, region_name=REGION)
credentials = session.get_credentials().get_frozen_credentials()
def call_glue_api(action, payload):
"""Glue プレビューAPIを直接呼び出すヘルパー関数"""
headers = {
'Content-Type': 'application/x-amz-json-1.1',
'X-Amz-Target': f'AWSGlue.{action}'
}
request = AWSRequest(
method='POST',
url=f'https://glue.{REGION}.amazonaws.com',
data=json.dumps(payload),
headers=headers
)
SigV4Auth(credentials, 'glue', REGION).add_auth(request)
conn = http.client.HTTPSConnection(f'glue.{REGION}.amazonaws.com')
conn.request('POST', '/', body=json.dumps(payload), headers=dict(request.headers))
response = conn.getresponse()
result = json.loads(response.read().decode())
conn.close()
return response.status, result
pip install boto3 が必要です(botocore を SigV4 署名生成のために利用)。
Step 1: サンプルデータの準備(S3)
ECサイトの顧客データと売上データをサンプルとして用意します。
バケット作成
aws s3api create-bucket \
--bucket glue-catalog-demo-${ACCOUNT_ID} \
--region us-east-1
us-east-1 の場合は --create-bucket-configuration は不要です(デフォルトリージョンのため)。
サンプルデータ
customer_id,first_name,last_name,email,signup_date,membership_tier,lifetime_value
CUST-101,John,Smith,john.smith@example.com,2025-01-15,Gold,1250.50
CUST-102,Jane,Doe,jane.doe@example.com,2025-03-22,Silver,890.00
CUST-103,Bob,Johnson,bob.j@example.com,2024-11-01,Platinum,3200.75
CUST-104,Alice,Williams,alice.w@example.com,2025-06-10,Gold,1800.00
CUST-105,Charlie,Brown,charlie.b@example.com,2024-08-20,Silver,750.25
CUST-106,Diana,Lee,diana.l@example.com,2025-02-14,Gold,1100.00
CUST-107,Eve,Garcia,eve.g@example.com,2025-04-30,Bronze,320.00
CUST-108,Frank,Martinez,frank.m@example.com,2024-12-25,Platinum,4500.00
CUST-109,Grace,Taylor,grace.t@example.com,2025-07-01,Bronze,150.00
order_id,customer_id,product_id,product_name,category,quantity,unit_price,total_amount,order_date,region
ORD-001,CUST-101,PROD-A1,Wireless Mouse,Electronics,2,29.99,59.98,2026-07-01,Northeast
ORD-002,CUST-102,PROD-B2,Office Chair,Furniture,1,299.99,299.99,2026-07-01,West
ORD-003,CUST-103,PROD-C3,Python Programming Book,Books,3,45.00,135.00,2026-07-02,Southeast
ORD-004,CUST-101,PROD-D4,USB-C Hub,Electronics,1,79.99,79.99,2026-07-02,Northeast
ORD-005,CUST-104,PROD-E5,Standing Desk,Furniture,1,549.99,549.99,2026-07-03,Midwest
ORD-006,CUST-105,PROD-F6,Noise Cancelling Headphones,Electronics,1,199.99,199.99,2026-07-03,West
ORD-007,CUST-106,PROD-G7,Ergonomic Keyboard,Electronics,2,89.99,179.98,2026-07-04,Northeast
ORD-008,CUST-107,PROD-H8,Data Science Handbook,Books,1,55.00,55.00,2026-07-04,Southeast
ORD-009,CUST-108,PROD-I9,Monitor Arm,Furniture,2,45.99,91.98,2026-07-05,West
ORD-010,CUST-109,PROD-J10,Webcam HD,Electronics,1,69.99,69.99,2026-07-05,Midwest
アップロード
aws s3 cp customers.csv s3://glue-catalog-demo-${ACCOUNT_ID}/data/customers/
aws s3 cp sales_transactions.csv s3://glue-catalog-demo-${ACCOUNT_ID}/data/sales/
Step 2: Glue データベース・テーブルの作成
ここは通常のAWS CLIで操作可能です。
データベース作成
aws glue create-database \
--database-input '{
"Name": "ecommerce_db",
"Description": "E-commerce demo database for testing Glue Data Catalog business context and semantic search features"
}' \
--region us-east-1
テーブル作成
customers テーブル(クリックで展開)
aws glue create-table \
--database-name ecommerce_db \
--table-input '{
"Name": "customers",
"Description": "Customer master data including membership tiers and lifetime value",
"StorageDescriptor": {
"Columns": [
{"Name": "customer_id", "Type": "string", "Comment": "Unique customer identifier"},
{"Name": "first_name", "Type": "string", "Comment": "Customer first name"},
{"Name": "last_name", "Type": "string", "Comment": "Customer last name"},
{"Name": "email", "Type": "string", "Comment": "Customer email address"},
{"Name": "signup_date", "Type": "date", "Comment": "Date customer signed up"},
{"Name": "membership_tier", "Type": "string", "Comment": "Customer membership level: Bronze, Silver, Gold, Platinum"},
{"Name": "lifetime_value", "Type": "double", "Comment": "Total customer spend in USD"}
],
"Location": "s3://glue-catalog-demo-YOUR_ACCOUNT_ID/data/customers/",
"InputFormat": "org.apache.hadoop.mapred.TextInputFormat",
"OutputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat",
"SerdeInfo": {
"SerializationLibrary": "org.apache.hadoop.hive.serde2.OpenCSVSerde",
"Parameters": {"separatorChar": ",", "quoteChar": "\"", "escapeChar": "\\\\"}
}
},
"TableType": "EXTERNAL_TABLE",
"Parameters": {"classification": "csv", "skip.header.line.count": "1"}
}' \
--region us-east-1
sales_transactions テーブル(クリックで展開)
aws glue create-table \
--database-name ecommerce_db \
--table-input '{
"Name": "sales_transactions",
"Description": "Sales order transactions with product details, quantities, and revenue",
"StorageDescriptor": {
"Columns": [
{"Name": "order_id", "Type": "string", "Comment": "Unique order identifier"},
{"Name": "customer_id", "Type": "string", "Comment": "Reference to customer"},
{"Name": "product_id", "Type": "string", "Comment": "Product SKU"},
{"Name": "product_name", "Type": "string", "Comment": "Product display name"},
{"Name": "category", "Type": "string", "Comment": "Product category"},
{"Name": "quantity", "Type": "int", "Comment": "Number of items ordered"},
{"Name": "unit_price", "Type": "double", "Comment": "Price per unit in USD"},
{"Name": "total_amount", "Type": "double", "Comment": "Total order amount in USD"},
{"Name": "order_date", "Type": "date", "Comment": "Date order was placed"},
{"Name": "region", "Type": "string", "Comment": "Sales region"}
],
"Location": "s3://glue-catalog-demo-YOUR_ACCOUNT_ID/data/sales/",
"InputFormat": "org.apache.hadoop.mapred.TextInputFormat",
"OutputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat",
"SerdeInfo": {
"SerializationLibrary": "org.apache.hadoop.hive.serde2.OpenCSVSerde",
"Parameters": {"separatorChar": ",", "quoteChar": "\"", "escapeChar": "\\\\"}
}
},
"TableType": "EXTERNAL_TABLE",
"Parameters": {"classification": "csv", "skip.header.line.count": "1"}
}' \
--region us-east-1
ここまでが従来のGlue操作です。ここから先が今回の新機能になります。
Step 3: 用語集(Glossary)と用語(Term)の作成
ここからは前述の call_glue_api() ヘルパー関数を使用します。
3-1. 用語集の作成
status, result = call_glue_api('CreateGlossary', {
"Name": "Enterprise Data Glossary",
"Description": "Standardized business definitions for e-commerce data assets."
})
print(f"Status: {status}")
print(json.dumps(result, indent=2))
{
"Id": "<YOUR_GLOSSARY_ID>",
"Name": "Enterprise Data Glossary",
"Description": "Standardized business definitions for e-commerce data assets."
}
レスポンスの Id を以降の操作で使います。
3-2. 用語の作成
GLOSSARY_ID = "<YOUR_GLOSSARY_ID>" # ↑で取得
# Customer Lifetime Value
status, result = call_glue_api('CreateGlossaryTerm', {
"GlossaryIdentifier": GLOSSARY_ID,
"Name": "Customer Lifetime Value",
"ShortDescription": "Total revenue generated by a customer over their entire relationship.",
"LongDescription": "The cumulative monetary value of all purchases made by a customer from their first order to the present. Used for customer segmentation and marketing investment decisions."
})
# => Id: "<TERM_ID_1>"
# Membership Tier
status, result = call_glue_api('CreateGlossaryTerm', {
"GlossaryIdentifier": GLOSSARY_ID,
"Name": "Membership Tier",
"ShortDescription": "Customer loyalty program level based on spending.",
"LongDescription": "Classification of customers into Bronze, Silver, Gold, or Platinum tiers based on their annual spending thresholds. Determines discount rates and benefit eligibility."
})
# => Id: "<TERM_ID_2>"
# Sales Transaction
status, result = call_glue_api('CreateGlossaryTerm', {
"GlossaryIdentifier": GLOSSARY_ID,
"Name": "Sales Transaction",
"ShortDescription": "A completed purchase order from a customer.",
"LongDescription": "A record of a completed purchase including product details, quantities, pricing, and customer information. Each transaction represents one order which may contain multiple items."
})
# => Id: "<TERM_ID_3>"
3-3. 用語をテーブルに関連付け
アセットの識別子はGlueテーブルの ARN形式 を使用します。
ACCOUNT_ID = "<YOUR_ACCOUNT_ID>"
# customers テーブルに "Customer Lifetime Value" と "Membership Tier" を関連付け
call_glue_api('AssociateGlossaryTerms', {
"AssetIdentifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/customers",
"GlossaryTermIdentifiers": ["<TERM_ID_1>", "<TERM_ID_2>"]
})
# sales_transactions テーブルに "Sales Transaction" と "Customer Lifetime Value" を関連付け
call_glue_api('AssociateGlossaryTerms', {
"AssetIdentifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/sales_transactions",
"GlossaryTermIdentifiers": ["<TERM_ID_3>", "<TERM_ID_1>"]
})
プレビュー中は1アセットあたり最大 10個 の用語を関連付け可能です。
Step 4: カスタムメタデータ(Form)の設定
4-1. フォームタイプの定義
フォームタイプのスキーマは Smithy IDL 構文で定義します。
status, result = call_glue_api('PutFormType', {
"Name": "DataGovernance",
"Schema": """structure DataGovernance {
dataOwner: String
dataClassification: String
retentionDays: Integer
piiContains: String
}"""
})
print(json.dumps(result, indent=2))
{
"Id": "DataGovernance",
"Name": "DataGovernance",
"Schema": "structure DataGovernance {\n dataOwner: String\n ..."
}
ハマりポイント①: PutFormType のパラメータ
最初は Model + Fields 配列で渡そうとしたが 400エラー になりました。
# ❌ これは動かない
call_glue_api('PutFormType', {
"Name": "DataGovernance",
"Model": {"Fields": [{"Name": "data_owner", "Type": "String"}]}
})
# => 400: "Value at 'schema' failed to satisfy constraint: Member must not be null"
正解: Schema パラメータに Smithy IDL 構文の文字列 を渡す。structure 名はフォームタイプ名と一致させる。
4-2. テーブルにフォームをアタッチ
# customers テーブル
call_glue_api('PutAttachment', {
"AssetIdentifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/customers",
"AttachmentName": "governanceInfo",
"FormTypeId": "DataGovernance",
"Content": json.dumps({
"dataOwner": "Customer Data Team",
"dataClassification": "Confidential",
"retentionDays": 730,
"piiContains": "Yes"
})
})
# sales_transactions テーブル
call_glue_api('PutAttachment', {
"AssetIdentifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/sales_transactions",
"AttachmentName": "governanceInfo",
"FormTypeId": "DataGovernance",
"Content": json.dumps({
"dataOwner": "Revenue Analytics Team",
"dataClassification": "Internal",
"retentionDays": 365,
"piiContains": "No"
})
})
ハマりポイント②: PutAttachment のパラメータ
# ❌ これは動かない
call_glue_api('PutAttachment', {
"AssetIdentifier": "arn:aws:glue:...",
"FormName": "DataGovernance",
"FormFields": [{"Name": "data_owner", "Value": "Customer Data Team"}]
})
# => 400: "Value at 'formTypeId' failed to satisfy constraint: Member must not be null;
# Value at 'attachmentName' failed to satisfy constraint: Member must not be null;
# Value at 'content' failed to satisfy constraint: Member must not be null"
正解: パラメータは FormTypeId、AttachmentName(任意の名前)、Content(JSON文字列)。
4-3. 確認: GetAsset でアセット情報を取得
status, result = call_glue_api('GetAsset', {
"Identifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/customers"
})
ハマりポイント③: GetAsset のパラメータ名
# ❌ AssetIdentifier → 400: "identifier is required"
call_glue_api('GetAsset', {"AssetIdentifier": "arn:..."})
# ✅ Identifier が正解
call_glue_api('GetAsset', {"Identifier": "arn:..."})
レスポンスで、紐づいた用語(GlossaryTerms)とフォーム(Attachments)が確認できます:
{
"Name": "customers",
"GlossaryTerms": ["<TERM_ID_1>", "<TERM_ID_2>"],
"Attachments": {
"governanceInfo": {
"Content": "{\"retentionDays\":730,\"dataClassification\":\"Confidential\",\"piiContains\":\"Yes\",\"dataOwner\":\"Customer Data Team\"}",
"FormTypeId": "DataGovernance"
}
}
}
Step 5: セマンティック検索を試す
いよいよ本丸のセマンティック検索です。
基本的な検索
status, result = call_glue_api('SearchAssets', {
"SearchText": "customer spending"
})
print(json.dumps(result, indent=2))
{
"Items": [
{
"AssetName": "customers",
"AssetDescription": "Customer master data including membership tiers and lifetime value",
"AssetTypeId": "Table",
"Id": "arn:aws:glue:us-east-1:<YOUR_ACCOUNT_ID>:table/ecommerce_db/customers",
"Namespace": "amazon.glue",
"Type": "Table"
}
],
"TotalCount": 1
}
「customer spending」という検索語はテーブル名やカラム名に含まれていませんが、テーブル説明の "lifetime value" や関連付けた用語の意味 からセマンティックにマッチしています。
フィルタ付き検索
# テーブルのみに絞り込み
status, result = call_glue_api('SearchAssets', {
"SearchText": "customer",
"FilterClause": {
"AttributeFilter": {
"Attribute": "type",
"Operator": "equals",
"Value": {"StringValue": "Table"}
}
},
"MaxResults": 10
})
複合フィルタ(AND条件)
# テーブル × Glueネームスペース で絞り込み
status, result = call_glue_api('SearchAssets', {
"SearchText": "data",
"FilterClause": {
"AndAllFilters": [
{"AttributeFilter": {"Attribute": "type", "Operator": "equals", "Value": {"StringValue": "Table"}}},
{"AttributeFilter": {"Attribute": "namespace", "Operator": "equals", "Value": {"StringValue": "amazon.glue"}}}
]
},
"MaxResults": 10
})
検索結果まとめ
様々なクエリで試した結果です:
| 検索クエリ | ヒット | 考察 |
|---|---|---|
"customer spending" |
✅ customers | テーブル説明の"lifetime value"からセマンティックにマッチ |
"loyalty program levels" |
✅ customers | 用語"Membership Tier"の定義を通じてマッチ |
"revenue and orders" |
✅ sales_transactions, customers | 両方に関連するビジネスコンテキスト |
"lifetime value" |
✅ customers, sales_transactions | 用語が両テーブルに関連付けられているため |
"product catalog" |
✅ sales_transactions | product関連カラムの存在から |
"purchase history" |
❌ ヒットなし | セマンティック距離が遠い |
"PII data" |
❌ ヒットなし | フォームコンテンツのインデックスに時間がかかる可能性 |
"顧客データ" (日本語) |
❌ ヒットなし | 日本語検索は未対応 |
セマンティック検索は、テーブルの説明・カラムコメント・関連付けた用語の定義を総合的に理解してマッチングしています。キーワードの完全一致ではなく、意味的な近さで検索結果が決まります。
クリーンアップ
不要になったらリソースを削除します。削除順序に注意が必要です。
クリーンアップスクリプト(クリックで展開)
ACCOUNT_ID = "YOUR_ACCOUNT_ID"
GLOSSARY_ID = "YOUR_GLOSSARY_ID"
TERM_IDS = ["TERM_ID_1", "TERM_ID_2", "TERM_ID_3"]
# 1. アタッチメント削除
for table in ["customers", "sales_transactions"]:
call_glue_api('DeleteAttachment', {
"AssetIdentifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/{table}",
"AttachmentName": "governanceInfo"
})
# 2. 用語の関連付け解除
call_glue_api('DisassociateGlossaryTerms', {
"Identifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/customers",
"GlossaryTermIdentifiers": TERM_IDS[:2]
})
call_glue_api('DisassociateGlossaryTerms', {
"Identifier": f"arn:aws:glue:us-east-1:{ACCOUNT_ID}:table/ecommerce_db/sales_transactions",
"GlossaryTermIdentifiers": [TERM_IDS[2], TERM_IDS[0]]
})
# 3. 用語削除(用語集削除前に必須)
for term_id in TERM_IDS:
call_glue_api('DeleteGlossaryTerm', {"Identifier": term_id})
# 4. 用語集削除
call_glue_api('DeleteGlossary', {"Identifier": GLOSSARY_ID})
# 5. フォームタイプ削除
call_glue_api('DeleteFormType', {"Identifier": "DataGovernance"})
# 6. Glue テーブル・データベース削除
aws glue delete-table --database-name ecommerce_db --name customers --region us-east-1
aws glue delete-table --database-name ecommerce_db --name sales_transactions --region us-east-1
aws glue delete-database --name ecommerce_db --region us-east-1
# 7. S3バケット削除
aws s3 rb s3://glue-catalog-demo-${ACCOUNT_ID} --force --region us-east-1
まとめ
できたこと
- ✅ 用語集と用語を定義し、テーブルに関連付けてビジネスの意味を付与
- ✅ Smithy IDL でカスタムメタデータスキーマを定義し、テーブルに構造化メタデータを付与
- ✅ セマンティック検索で自然言語クエリからテーブルを発見(キーワード一致ではなく意味マッチ)
- ✅ フィルタ機能で検索結果を絞り込み
現時点の制約
| 項目 | 状況 |
|---|---|
| AWS CLI / boto3 対応 | ❌ 未対応(SigV4直接呼び出しが必要) |
| 日本語セマンティック検索 | ❌ 未対応 |
| GUIコンソール | ❓ ドキュメント上未記載 |
| 東京リージョン | ❌ 未対応 |
| CMK暗号化カタログ | ❌ 未対応 |
| アセットあたりの用語数 | 最大10個 |
AIエージェント連携
MCP互換エージェント(Claude Code、Kiro、Cursor、Codex等)からカタログを利用するには、Agent Toolkit for AWS の aws-data-analytics プラグインをインストールします。
これにより、AIエージェントが:
- カタログからテーブルを検索
- ビジネスコンテキスト(用語・フォーム)を取得
- スキルアセットからドメイン知識を読み込み
といった操作をほぼセットアップなしで行えるようになります。
所感
従来のGlue Data Catalogは「技術者向けのスキーマ管理ツール」という印象でしたが、ビジネスコンテキスト機能により データカタログとしての本来の役割 ──ビジネスユーザーとエンジニアの間の共通言語としてのカタログ── に一歩近づいた印象です。
特にAIエージェントとの連携は強力で、「このデータは何を意味するのか」をエージェントが正確に理解した上でクエリを生成できるようになる可能性があります。
CLIやSDKの正式対応が待たれるところですが、プレビュー段階でもAPIは安定して動作しており、本記事の手順で一通りの機能を体験できます。