Amazon CloudSearchのクライアントプログラミング

■ Search Data Format (SDF)   Amazon CloudSearchでデータをインデクシングするのにSDFというフォーマット(JSONもしくはXML)で データをアップロードする必要がありますが、2014年1月現在、以下の方法があります。 1. コマンドラインツールをインストールしてcs-import-documentsコマンドを使う 2. ManagementConsoleでブラウザからポチポチする 3. DocumentEndpointに直接curl等でPOSTする   SDFフォーマット的には↓に詳しく記載されていますが、 http://aws.amazon.com/articles/8871401284621700#_Ref198105621   以下のような情報が必要な感じになります。 - 操作種別(add/delete) - 一意の識別子 - バージョン番号 - 言語コード(en) - 実際のフィールドとデータ     SDKを使って開発しているのでその中でやりくりしたい   先日、Solrをイジっていて、SolrJでクライアントプログラミングしてみました(http://shinodogg.com/?p=5899)が、 ソレと同じようなノリで、Javaのオブジェクトを作って、そのままインデクシングしたいな、と。     ■ まずはAmazon CloudSearchに検索サーバーを準備してSDKからアクセス   CloudSearchにDomainを定義します。 (後からtextフィールドもresultのチェック入れましたが、件数なくてもActiveになるまでそこそこ時間かかります)

  JavaSDKのCloudSearchなクラスを使ってアクセスしてみます。   - CloudSearchのクライアント。リージョンはVirginia(まだ日本にきてないので)

AmazonCloudSearchClient client = new AmazonCloudSearchClient(new PropertiesCredentials(SampleIndexer.class.getResourceAsStream("AwsCredentials.properties")));
client.setRegion(Region.getRegion(Regions.US_EAST_1));

  - 検索ドメインを表示 ↓この辺の情報が取れます。(複数ドメインがあれば複数) ・ドメインのID, 名前, 作成済み, 削除済み, 検索対象ドキュメント数 ・ドキュメントサービス(インデクシング)のエンドポイント, サーチサービスのエンドポイント ・インスタンスのタイプや数、パーティション数なんかも

DescribeDomainsResult describeDomainsResult = client.describeDomains();
List domainStatusList = describeDomainsResult.getDomainStatusList();
for (DomainStatus status: domainStatusList) {
    System.out.println(status);
}

  - ドメインのフィールドを表示

DescribeIndexFieldsRequest describeIndexFieldsRequest = new DescribeIndexFieldsRequest();
describeIndexFieldsRequest.withDomainName("sample");
DescribeIndexFieldsResult indexFieldsResult = client.describeIndexFields(describeIndexFieldsRequest);
List indexFieldStatusList = indexFieldsResult.getIndexFields();
for (IndexFieldStatus idxFieldStatus : indexFieldStatusList) {
    System.out.println(idxFieldStatus);
}

↓こんな感じでイロイロ取れます。いつそのフィールドのドメイン定義がUpdateされた〜とか。

{Options: {IndexFieldName: detail,IndexFieldType: text,TextOptions: {FacetEnabled: false,ResultEnabled: true,},SourceAttributes: []},Status: {CreationDate: Tue Jan 07 16:01:26 JST 2014,UpdateDate: Tue Jan 07 20:43:00 JST 2014,UpdateVersion: 22,State: Active,PendingDeletion: false}}
{Options: {IndexFieldName: head,IndexFieldType: text,TextOptions: {FacetEnabled: false,ResultEnabled: true,},SourceAttributes: []},Status: {CreationDate: Tue Jan 07 16:01:25 JST 2014,UpdateDate: Tue Jan 07 20:43:00 JST 2014,UpdateVersion: 22,State: Active,PendingDeletion: false}}
{Options: {IndexFieldName: id,IndexFieldType: uint,SourceAttributes: []},Status: {CreationDate: Tue Jan 07 16:01:23 JST 2014,UpdateDate: Tue Jan 07 16:31:16 JST 2014,UpdateVersion: 14,State: Active,PendingDeletion: false}}

  んま、とりあえず、SDKを使ってCloudSearchと会話できる確認がとれましたよ、と。     JavaのオブジェクトからSDFを生成してPOSTする   SDFと言っても、普通のJSON(もしくはXML)なので、普通にバリューオブジェクトを作れば良さそうです。   ということで、まずはJavaJSON用ライブラリ。 以前はよくJacksonというライブラリを使っていましたが、ググったらGoogleのがあるらしいので (その名もGson。ストレートな名前がグッときますね。笑)、それを使ってみます。 ↓pom.xmlに定義を追加してmavenでjarファイルをダウンロードしてきます。

<dependencies>
    <!--  Gson: Java to Json conversion -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.2.4</version>
      <scope>compile</scope>
    </dependency>
</dependencies>

  - Valueオブジェクトを作ります。 ・SDF

public class SDF {
    private String type; //操作種別(add/delete)
    private String id; //一意の識別子
    private int version; //バージョン番号
    private String lang; //言語コード(en)
    private Sample fields; //実際のフィールドとデータ
〜 setterとgetterは省略 〜

ドメインで定義したヤツ

public class Sample {
    private int id;
    private String head;
    private String detail;
〜 setterとgetterは省略 〜

  - ドカっと値を詰めてやります。

List<SDF> sdfList = new ArrayList<SDF>();

SDF sdf = new SDF();
sdf.setType("add");
sdf.setId("111111");
sdf.setVersion(1111);
sdf.setLang("en");
Sample sample = new Sample();
sample.setId(1111);
sample.setHead("sample head 11");
sample.setDetail("sample detail 12");
sdf.setFields(sample);
sdfList.add(sdf);

sdf = new SDF();
sdf.setType("add");
sdf.setId("111112");
sdf.setVersion(1111);
sdf.setLang("en");
sample = new Sample();
sample.setId(1112);
sample.setHead("head bungin");
sample.setDetail("detail junkie");
sdf.setFields(sample);
sdfList.add(sdf);

  上記で2レコードを追加するJavaなオブジェクトが出来上がりました。   - オブジェクトをJSON文字列に。スッゲー簡単…。

Gson gson = new Gson();
System.out.println(gson.toJson(sdfList));

↓インデントするとこんな感じになります。

[
    {
        "fields": {
            "detail": "sample detail 12",
            "head": "sample head 11",
            "id": 1111
        },
        "id": "111111",
        "lang": "en",
        "type": "add",
        "version": 1111
    },
    {
        "fields": {
            "detail": "detail junkie",
            "head": "head bungin",
            "id": 1112
        },
        "id": "111112",
        "lang": "en",
        "type": "add",
        "version": 1111
    }
]

  - よくあるHTTPクライアントなヤツ

HttpClient httpClient = new DefaultHttpClient();

// POSTでドキュメントエンドポイントに。
// URLの最後の方の"2011-02-01"はCloudSearchのバージョン名で今のところコレです。
HttpPost post = new HttpPost("http://doc-sample-xxx.us-east-1.cloudsearch.amazonaws.com/2011-02-01/documents/batch");

// JavaオブジェクトをJSONにしてContentTypeの設定&エンティティにセット
StringEntity entity = new StringEntity(gson.toJson(sdfList));
entity.setContentType("application/json");
post.setEntity(entity);

// 実行して結果を取得
HttpResponse response = httpClient.execute(post);
System.out.println(response.getStatusLine());
String responseString = EntityUtils.toString(response.getEntity());
System.out.println(responseString);

  - 実行結果 ステータスコードと何件追加したよ的なヤツが返ってきます。

HTTP/1.1 200 OK
{"status": "success", "adds": 2, "deletes": 0}

    ■ ManagementConsoleからクエリ   ↓正しくインデックスされていることが確認出来ました。

  上記のコードも大量にデータを扱うとなるとイロイロとチューニングなポイントがありそうですが、 一般的なライブラリの組み合わせでやりくり出来そうです。     ■ 次回は、、   SnapDishの清田さんのブログ形態素解析まわりをCloudSearchの外でやって、 日本語でもCloudSearchを活用されている例が紹介されていますが、 Yahoo!さんが日本語形態素解析APIを公開していたりするので、 そちらを使ってCloudSearchでの日本語の検索を試してみたいと思います。    

[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン (Software Design plus)
大谷 純 阿部 慎一朗 大須賀 稔 北野 太郎 鈴木 教嗣 平賀 一昭
技術評論社
売り上げランキング: 5,985