AWSサポートとの問い合わせ履歴をDynamoDBに入れCloudSearchで日本語全文検索してみた
はじめに
こんにちは植木和樹です。AWSで困ったときに頼りになるのがAWSサポートです。サポートでは分からないことを解決するまで親切丁寧に対応してくれるので大変助かっております。特にクラスメソッドでは日々AWSの多く機能を利用しているため、サポートとのやりとりはAWSノウハウの宝庫といっても過言ではありません。
せっかくのケースを埋もれさせておくのはもったいない。ということで、本日は過去のサポートとの問い合わせ履歴をCloudSearchで全文検索する仕組みを作ってみました。
CloudSearchといえば今年の4月に大幅なバージョンアップが行われ、これまで難だった日本語検索機能も追加されています。
環境
AWSサポートは一般的に本番環境用のアカウントからケースを作成することになります。今回はAWSサポート用アカウント(aws-support)とは別に、個人検証用のアカウント(default)にDynamoDBとCloudSearchを用意しています。
AWSサポート→DynamoDB
DynamoDBにテーブルを作成する
AWSサポートとやりとりした履歴データは問い合わせカテゴリや優先度を収めた「cases」と、実際の問い合わせ内容が収められた「communications」で構成されています。今回はそれぞれをDynamoDBの別テーブルに収めることにしましょう。テーブルは異なりますがプライマリキー(Hash+Range)は同じです。
また今回は検索はCloudSearchで行い、検索結果から得られたcase_idで各ケースデータを取得することを想定しているのでセカンダリーインデックスは設定していません。DynamoDBで直接検索する場合はdisplay_id(問い合わせ時に用いる数字8桁のケースID)をグローバルセカンダリーインデックスに指定しておくと良いでしょう。
aws-support-cases テーブル
項目 | 値 |
---|---|
Primary Hash Key | case_id (String) |
Primary Range Key | time_created (String) |
aws-support-communications テーブル
項目 | 値 |
---|---|
Primary Hash Key | case_id (String) |
Primary Range Key | time_created (String) |
AWSサポートのケース情報を抽出し、DynamoDBに格納する
awscliでケースを抽出しDynamoDBに入れることを試みたのですが、各ケースをDynamoDBにインポートするためのJSON形式に変換するのが手間だったのでAWS SDK for Rubyでインポート用のスクリプトを用意しました。またawscli(Python)だと時々海外の方が入力する右シングルクォーテーション(\u2019)が扱えなかったこともRubyを用いた理由です。
スクリプトの注意点は下記となります。
- describe_cases時に言語設定(language)が"en"と"ja"が分かれて返ってくるため、それぞれの言語で取得する。
- AWSサポートのエンドポイントはus-east-1のみなのでAWS::Supportインスタンス生成時の設定でRegionを固定する。
- display_idはString#to_iで数値に変換する。
- ケースでの添付ファイルが空の場合attachmentSetが空の配列になるが、空の配列はDynamoDBに入れられないのでキーを削除する。
- DynamoDBとCloudSearchは--profileオプションでAWSクレデンシャルを指定します。
- AWSサポート用のクレデンシャルはaws-supportというプロファイル名で$HOME/.aws/configに設定しておきます。
cm-export-cases.rb
#!/usr/bin/env ruby require 'aws-sdk-v1' require 'optparse' require 'time' require 'pp' begin require 'aws/profile_parser' rescue LoadError; end ARGV.options do |opt| begin aws_opts = {} opt.on('-h', '--help') { puts opt.help; exit 0 } opt.on('-d', '--debug') { aws_opts[:log_level] = :debug } opt.on('-k', '--access-key ACCESS_KEY') { |v| aws_opts[:access_key_id] = v } opt.on('-s', '--secret-key SECRET_KEY') { |v| aws_opts[:secret_access_key] = v } opt.on('-r', '--region REGION') { |v| aws_opts[:region] = v } opt.on('--profile PROFILE') { |v| parser = AWS::ProfileParser.new; aws_opts = parser.get(v) } opt.parse! if aws_opts.empty? puts opt.help exit 1 end AWS.config(aws_opts) rescue => e $stderr.puts e exit 1 end end # get an existing table by name and specify its hash key dynamo_db = AWS::DynamoDB.new case_table = dynamo_db.tables['aws-support-cases'] comm_table = dynamo_db.tables['aws-support-communications'] case_table.hash_key = [:case_id, :string] comm_table.hash_key = [:case_id, :string] # Gather support cases from an another profile aws_opts = {} begin parser = AWS::ProfileParser.new aws_opts = parser.get('aws-support') rescue => e $stderr.puts e exit 1 end lastyear = Time.now - 365 * 24 * 60 * 60 aws_opts[:region] = 'us-east-1' support = AWS::Support.new(aws_opts) %w(en ja).each do |lang| case_response = support.client.describe_cases(:language => lang, :after_time => lastyear.utc.iso8601, :include_resolved_cases => true, :include_communications => false) case_response.data[:cases].each do |case_data| case_data[:display_id] = case_data[:display_id].to_i pp case_data # add a case item case_item = case_table.items.create(case_data) # add related communication items comm_response = support.client.describe_communications(:case_id => case_data[:case_id]) comm_response.data[:communications].each do |comm_data| comm_data.delete_if { |k,v| v.empty? } pp comm_data comm_item = comm_table.items.create(comm_data) end end end
スクリプトを保存したら下記のコマンドを実行すると、ケースのDynamoへのエクスポートが行われます。
$ ruby cm-export-cases.rb --profile default
サポートケースの数が多い場合はmax_resultsで件数を制限しながら少しずつインポートしなければいけないと思いますが、ここでは省略しています。
DynamoDB→CloudSearch
CloudSearchにはDynamoDBからドキュメントをインポートする機能があります。またDynamoDBに格納されたレコード情報を基にインデックス設定をほぼ自動的に行うことができます。
CloudSearchドメインの作成インデックスの自動設定
CloudSearchマネージメントコンソールの画面から新しい検索ドメインとしてaws-supportを作成します。
awscliでも作成することができます。
$ aws cloudsearch create-domain --domain-name aws-support
ドメインを作成したら「Indexing Options」にある「configuration wizard」のリンクをクリックしてインデックス設定を行いましょう。DynamoDBに格納されたレコード情報を基にインデックス設定を設定してくれます。
インデックスの設定見直し
自動設定されたインデックスでは日本語検索などに少々不便なので見直します。
Name | 変更箇所 | 備考 |
---|---|---|
category_code | Typeを"text"から"literal"に変更する。 | 「一般的なご質問」や「インスタンス関連」などカテゴリーコード |
language | Typeを"text"から"literal"に変更する。 | "en"または"ja" |
service_code | Typeを"text"から"literal"に変更する。 | 「EC2」や「RDS」などサービスコード |
severity_code | Typeを"text"から"literal"に変更する。 | 緊急度のコード(low, normal, high, urgent など) |
status | Typeを"text"から"literal"に変更する。 | ケースのステータス(解決済み = resolved など) |
subject | Analysis Schemeを"English"から"Japanese"に変更する。 | 日本語の形態素解析が有効になる |
またDynamoDB上ではtime_createdはStringですが、日付時刻をISO8601形式で格納しているためCloudSearchではDateとして認識してくれています。賢い。
DynamoDBのaws-support-casesを用いたインデックス設定が終わったら、同様にaws-support-communicationsも読み込ませてf_bodyフィールド(DynamoDBではbody)を追加しておきましょう。ケースの「内容」にあたるフィールドなのでAnalysis Schemaは"Japanese"に変更しておきましょう。
ドキュメントのアップロード
インデックスを設定したら「Upload Documents」ボタンをクリックしてDynamoDBからドキュメントをアップロード(インポート)しましょう。これもaws-support-casesとaws-support-communications、それぞれのテーブルをアップロードすればケース情報とやりとりの本文すべてを対象に全文検索ができるようになります。
検索してみる
試しにいくつかの単語で検索してみました。
「リザーブ」で検索してみます。
ただ「リザーブド」では検索がマッチしません。Analysis SchemeのStemming(語幹処理)で細かな調整が必要なようですね。
課題
今回はAWSサポートの問い合わせ履歴をエクスポートして、DynamoDBにインポートし、CloudSearchで全文検索できるまでを行いました。しかし実際の運用に用いるためにはまだまだ課題があります。
- 定期的なケースのエクスポートとインポート。
- CloudSearchで検索した結果を基に、ケースを見やすく表示する。(フロントエンドとサーバーサイドアプリの開発)
- 大量のケースの一括インポート
- 日本語検索でマッチしない単語の調整
- アクセスポリシーの見直し
- ドキュメントに応じたCloudSearchのスペックやスケーリングの設定
- ケースに添付されたファイルの扱いはどうする?
まとめ
微調整は必要ですが、ケースをCloudSearchで全文検索するまでなら、たった半日程度で準備ができてしまいました。
CloudSearchは今年の3月に大幅なバージョンアップが行われました。昨年の9月に初めてCloudSearchを使ったときよりも、ずっと賢く・使いやすくなっている印象があります。サーバーを用意せずお手軽に全文検索できるシステムを用意できますので、みなさんも是非試してみてください。