Month: June 2015

Azure Searchのインデックス更新方法には大きく分けてPUSHとPULLの2種類ある。PUSHは直接Indexing APIを使ってAzure SearchにコンテンツをPOSTして更新。PULLは特定データソースに対してポーリングして更新で、Azure Searchの場合、DocumentDBとSQL Databaseの2種類のデータソースを対象にワンタイムもしくは定期的なスケジュール実行が可能となっている。ここではDocumentDBをデータソースとしてインデックスを更新する方法を紹介する。

サンプル構成と処理フローの説明

データソースにDocumentDBを利用する。データ「DOCUMENTDB PYTHON SDKとFEEDPARSERで作る簡易クローラー」においてクローリングされDocumentDBに保存されたブログ記事データを使用する。そしてDocumentDBを定期的にポーリングを行い更新があったレコードのみをAzure Searchインデックスに反映するためにDocumentDBインデクサーを設定する。全体構成としては下記の通りとなる。


documentdb-indexer

DocumentDBと更新先検索インデックスのフィールドのマッピング

DocumentDBをデータソースとしてAzure Searchインデックスに更新を行うためDocumentDBの参照先コレクションのフィールドと更新先Azure Searchインデックスのフィールドをマッピングを行う。マッピングはデータソース定義中のDocumentDB参照用Queryで行う。Azure SearchインデックスにインジェストするフィールドをDocumentDBのSELECTクエリー指定するのだが、Azure SearchとDocumentDBのフィールドが異なる場合は下図のようにSELECT “Docdbフィールド名” AS “Searchフィールド名”でインジェスト先フィールド名を指定する。データソース定義については後述の設定内容を確認ください。


documentdb-azuresearch-mapping

Configuration

以下1~4のステップでデータソースの作成、検索インデックスの作成、インデクサーの作成、インデクサーの実行を行う。

  1. データソースの作成
  2. credential.connectionStringで接続先DocumentDB文字列と対象データベースの指定を行う。container.(name|query)で対象コレクション名と参照用SELECT文を指定する。SELECT文はDocumentDBとインジェスト先Azure Searchのフィールドセット(フィールド名と数)が同じであれば省略可。詳細はこちらを参照。

  3. インデックスの作成
  4. 下記のスキーマでAzure Searchインデックスを作成する。

    {
        "name": "articles-test",
        "fields": [
            { "name":"itemno", "type":"Edm.String", "key": true, "searchable": false },
            { "name":"subject", "type":"Edm.String", "filterable":false, "sortable":false, "facetable":false},
            { "name":"body", "type":"Edm.String","filterable":false,"sortable":false, "facetable":false, "analyzer":"ja.lucene"},
            { "name":"url", "type":"Edm.String", "sortable":false, "facetable":false },
            { "name":"date", "type":"Edm.DateTimeOffset", "facetable":false}
         ]
    }
  5. DocumentDBインデクサーの作成
  6. DocumentDBインデクサー作成のための設定。DatSourceNameとtargetIndexNameにそれぞれ1で作成したデータソース名とインジェスト先のインデックス名を指定する。スケジュール実行させたい場合は下記の通りscheduleを設定する。ここではintervalをPT5Mとしているがこれは5分毎実行を意味する。詳しくはこちらを参照。

  7. インデクサーの明示的に実行
  8. スケジュール実行ではなくインデクサーをすぐに実行したい場合は下記のフォーマットでPOSTリクエストを送信する。

    POST https://[Search service name].search.windows.net/indexers/[indexer name]/run?api-version=[api-version]
    api-key: [Search service admin key]

実行結果確認方法


AzureSearchIndexerStatus

上記イメージの通りAzureポータル(preview)よりAzure Search → indexersタイルをたどることでインデクサーの実行結果や過去の履歴を確認することができる。ただし、ここではAPI経由で取得する方法を紹介する。
下記フォーマットでGETリクエストすることでインデクサーの現在の稼働状態と実行履歴を取得することができる。実行履歴は最後の実行結果だけではなく最近完了した50件の実行内容が含まれる。

GET https://[Search service name].search.windows.net/indexers/[indexer name]/status?api-version=[api-version]
api-key: [Search service admin key]

以下実行結果。executionHistoryが履歴、lastResultが最後の実行結果、statusが現在のインデクサーのステータスとなっている。

{
    "@odata.context": "https://yoichidemo.search.windows.net/$metadata#Microsoft.Azure.Search.V2015_02_28_Preview.IndexerExecutionInfo",
    "executionHistory": [
        {
            "endTime": "2015-06-25T05:55:01.393Z",
            "errorMessage": "Data source 'docdbds-article' does not exist",
            "errors": [],
            "finalTrackingState": null,
            "initialTrackingState": null,
            "itemsFailed": 0,
            "itemsProcessed": 0,
            "startTime": "2015-06-25T05:55:01.393Z",
            "status": "transientFailure"
        },
        ...(omit)...
        {
            "endTime": "2015-06-25T02:15:02.155Z",
            "errorMessage": null,
            "errors": [],
            "finalTrackingState": "1434871500",
            "initialTrackingState": "1434871500",
            "itemsFailed": 0,
            "itemsProcessed": 0,
            "startTime": "2015-06-25T02:15:01.452Z",
            "status": "success"
        },
        {
            "endTime": "2015-06-25T02:10:01.144Z",
            "errorMessage": null,
            "errors": [],
            "finalTrackingState": "1434871500",
            "initialTrackingState": "1434871500",
            "itemsFailed": 0,
            "itemsProcessed": 0,
            "startTime": "2015-06-25T02:10:00.022Z",
            "status": "success"
        }
    ],
    "lastResult": {
        "endTime": "2015-06-25T05:59:02.016Z",
        "errorMessage": "Data source 'docdbds-article' does not exist",
        "errors": [],
        "finalTrackingState": null,
        "initialTrackingState": null,
        "itemsFailed": 0,
        "itemsProcessed": 0,
        "startTime": "2015-06-25T05:59:02.016Z",
        "status": "transientFailure"
    },
    "name": "docdbindexer",
    "status": "running"
}

Content DBの有効性について

ここではAzure SearchのfeedingソリューションとしてDocumentDBを使用したPULLインデックス更新方法を紹介した。このようにクローリングされたデータを直接Azure SearchのIndex APIを使ってインデックスを更新するのではなく、今回のDocumentDBのように所謂Content DBに格納してからそれを元にインデックスを更新するのにはいくつか意味がある。例えば、インデックス構造を変更したい場合、再フィードが必要になるがContent DBがあれば再度クローリングする必要がない。通常クローリング(特にフルクローリング)のコストは大きい。また、Content DBがあれば別アカウント、リージョンにインデックスのレプリカの構築、他に、同一のデータを使って別構造のインデックを構築するといったことも容易に可能になる。
さらに、現時点でAzure Searchがデータ加工のためのパイプラインの仕組み(Lucene/Solr, ElasticSearchでいうところのカスタムAnalyzer)がないことから、インデックスに放り込む前の一時データ加工用のデータベースとしても有効であると考えている。

LINKS

DocumentDB Python SDKとfeedparserを使って簡易クローラーを作りましょうというお話。ここではDocumentDBをクローリング結果の格納先データストアとして使用する。クロール対象はAzure日本語ブログRSSフィード、これをfeedparserを使ってドキュメント解析、必要データの抽出、そしてその結果を今回使用するpydocumentdbというDocumentDB Python SDKを使ってDocumentDBに格納するというワークフローになっている。

DocumentDB Python SDK – pydocumentdb

Azureで提供されているどのサービスにもあてはまることであるが、DocumentDBを操作するための全てのインターフェースはREST APIとして提供されておりREST APIを内部的に使用してマイクロソフト謹製もしくは個人のコントリビューションによる複数の言語のSDKが用意されている。その中でもpydocumentdbはPython用のDocumentDB SDKであり、オープンソースとしてソースコードは全てGithubで公開されている。

Pre-Requirementsその1: Python実行環境とライブラリ

実行環境としてPython2.7系が必要となる。また、今回クローラーが使用しているDocumentDB Python SDKであるpydocumentdbとRSSフィード解析ライブラリfeedparserの2つのライブラリのインストールが必要となる。

Pre-requirementsその2: DocumentDBアカウント

Azure新ポータルより下記イメージのフローでDocumentDBアカウントを作成する。


DocumentDB-Account-Create

DocumentDBアカウントを作成したら今度はデータを格納するためのDocumentDBデータベースとコレクションを作成する必要がある。DocumentDBデータベースとコレクションはAzure新ポータルから作成可能であるが、今回のクローラープログラムではpydocumentdbを使って作成するようにしている。

RSSクローラーとその実行結果

RSSクローラーのソースコード(rssCrawler4Docdb.py)とその実行結果は下記の通り。もしクローリングを定期的に実行する場合はこのスクリプトをジョブスケジューラー(cronなど)に登録ください。指定のRSSフィードに新しく追加された記事のみがDocumentDBに反映されるようスクリプト上は重複チェックを入れている。

[実行結果]

database is created:feeddb
collection is created:article_collection
document is added:title:業界トップのクラウド アナリストがマイクロソフトに転身した理由
document is added:title:Azure Linux VM のインフラストラクチャの監視と診断
document is added:title:Azure App Service の Web アプリ用 Support Site Extension の追加更新
document is added:title:Azure CDN でカスタムのオリジン サーバーをサポート
document is added:title:Tinfoil Security の Azure App Service 向け Web 脆弱性スキャン機能
document is added:title:Azure Site Extensions の自動更新が可能に
document is added:title:DocumentDB へのデータのインポートがより高速、簡単に
document is added:title:Query Store: データベース版フライト データ レコーダー機能
document is added:title:Azure App Service の Web アプリで使用される TLS の中間証明書
document is added:title:Azure で Cloud Foundry をお試しください
document is added:title:Azure Media Indexer の更新版 v1.2.1 をリリース: 前回からの修正点について
document is added:title:クラウド環境でエンドツーエンドのビデオ ワークフローを構築する
document is added:title:Azure Media Player の更新と UserVoice フォーラムのお知らせ
document is added:title:Microsoft Azure 関連ニュース 2015 年 5 月のまとめ
document is added:title:Azure Media Services 向け Hyperlapse のパブリック プレビューを発表
document is added:title:6 月 1 日以降の Application Insights の料金設定
document is added:title:StorSimple Update 1 を発表: Azure Government で提供開始、他社クラウドと ZRS をサポート、5000/7000 シリーズからの移行に対応
document is added:title:成功から転落の危機に直面した Sleeve Music。経験豊かな開発者と適切なツールの選択で新たなアイデアを創出
document is added:title:Azure Storage に関する Build 2015 での発表
document is added:title:Visual Studio Code と Azure App Service の連携で実現できること
document is added:title:Azure Automation でグラフィック形式とテキスト形式の作成機能を導入
document is added:title:接続性の高い安定したハイブリッド クラウドの実現に向けた新しいネットワーク機能
document is added:title:DocumentDB の新しいインポート オプション
document is added:title:Azure Site Recovery が NetApp Private Storage for Microsoft Azure をサポート
document is added:title:Application Insights で ASP.NET 5 アプリケーションをサポート

Wikipediaのコンテンツは Creative Commons Licenseおよび GNU Free Documentation Licenseの下にライセンスされておりWikipedia財団は再配布や再利用のために惜しげもなくこの貴重なデータベースのダンプファイル(XMLファイル)を一般提供している。全文検索の検証で大量のデータが必要なときこのWikipediaのような生きたデータを使えるのは非常に有りがたい。これはこのWikipediaデータベースダンプ(日本語)を元にAzure Searchインデックスを生成してみましょうというお話。

利用するWikipedia XMLファイルとその定義

最新版日本語レポジトリには複数のXMLファイルが用意されているがここでは全ページのタイトル、ディスクリプションといった要約データを集約しているファイルjawiki-latest-abstract.xmlを利用する。XMLのフォーマットは次のとおり。この中からtitle, url, abstractを抽出してAzure Searchに投入する。

<doc>
<title>Wikipedia: 自然言語</title>
<url>http://ja.wikipedia.org/wiki/%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E</url>
<abstract>自然言語(しぜんげんご、)とは、人間によって日常の意思疎通のために用いられる、文化的背景を持って自然に発展してきた記号体系である。大別すると音声に>よる話し言葉と文字や記号として書かれる書き言葉がある。</abstract>
<links>
<sublink linktype="nav"><anchor>概要</anchor><link>http://ja.wikipedia.org/wiki/%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E#.E6.A6.82.E8.A6.81</link></sublink>
<sublink linktype="nav"><anchor>関連項目</anchor><link>http://ja.wikipedia.org/wiki/%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E#.E9.96.A2.E9.80.A3.E9.A0.85.E7.9B.AE</link></sublink>
</links>
</doc>

尚、実際にダウンロードしてみるとわかると思うがこのファイルはサイズが比較的大きく集約されているドキュメント数も実に多い。カウントしてみたところ現時点(2015/06/09)で969541 件あった。Azure Searchの料金プランのうちFreeプランは最大ドキュメント数が10,000であることからここで利用する料金プランはFreeではなく標準プランを選択する必要がある。

Index Schema

インデックス名はwikipedia、フィールドはキーフィールドのためのitemidフィールドと上記wikipedia XMLファイルのtitle, url, abstractを格納するための3フィールドを定義。

{
    "name": "wikipedia",
    "fields": [
        { "name":"itemid", "type":"Edm.String", "key": true, "searchable": false },
        { "name":"title", "type":"Edm.String", "filterable":false, "sortable":false, "facetable":false},
        { "name":"abstract", "type":"Edm.String", "filterable":false, "sortable":false, "facetable":false, "analyzer":"ja.lucene" },
        { "name":"url", "type":"Edm.String", "sortable":false, "facetable":false }
    ]
}

Azure Search投入用JSONデータの生成

Wikipedia XMLファイルからAzure Search投入用のJSONデータを生成するスクリプト(xml2json.pl)を作ってみた。巨大ファイルのDOM構造を一度にオンメモリ展開することは物理的に不可能であるため、XML::Twigモジュールを使ってドキュメント毎にコールバック関数からフィールドデータを抽出するように工夫を凝らしている。

次のように-cパラメータでインプットファイルを、-oパラメータでJSON出力のためのパスを指定する。実行すると-oパラメータで指定したパスにitems-N.json (N:0以上の整数)なファイルが大量に生成される。現時点でのデータから生成されたJSONファイルを数えてみたところitems-0.json …items-9692.jsonで合計9693個だった。

./xml2json.pl -c ./jawiki-latest-abstract.xml -o <path to output>

Azure SearchにJSONデータを投入

上記ステップで生成されたJSONファイルからAzure SearchにPOSTするための簡易スクリプト(AzureSearch-AddItemsFromFile.sh)を作る。

最後に大量に生成されたJSONファイル群をファイル名を1つずつこのスクリプトの第一引数に指定して実行していく。1万近いファイルなので全て投入が完了するまでに少々時間はかかります。

for i in `ls -1tr *.json`;do echo $i; ./AzureSearch-AddItemsFromFile.sh $i;done

cUrlはUNIX/Linux系では有名なURLを使ったデータ送受信コマンドで手軽にREST系処理を実行するときにとても重宝している。そんなcUrlコマンドを使ってAzure Searchをお手軽に使ってみようというお話。

はじめに

まだの人はAzureポータルよりAzure Searchサービスを作成してください。「ポータルでの Azure Search サービスの作成」に優しく手順が書かれているのでご参考に。料金プランは無料と標準プランがあるがテストであれば無料プランで十分。まずはAPIキーまで取得ください。API実行のためにはAPIキーが必要。

cURLでSearch Service REST APIを実行

Search Service REST APIの中からいくつか代表的なAPIをピックアップしてcUrlでクエリを組み立ててみる。ここではインデックス新規作成、そこにいくつかドキュメントを追加、そしてドキュメントを検索する・・といった基本的なシナリオを実行する。ポイントとしてはcUrlの-Hオプションでヘッダ定義、-XオプションでHTTPメソッド指定、-dオプションでリクエストボディを指定する・・・といったところ。尚、下記サンプルでは現時点(2015-06-05)で最新のAPIバージョン2015-02-28-Previewを使用している。

1. インデックス新規作成

articlesという名前のブログ記事を格納するためのインデックスを作成する。インデックス生成にはCreate Index (Azure Search Service REST API)を利用する。

2. ドキュメントの追加

1で作成したインデックスにいくつかドキュメントをアップロードしてみる。データソースはMicrosoft Azure Japan BlogRSS。インデックスへのドキュメントのアップロードAPIはAdd, Update or Delete Documents (Azure Search Service REST APを参照ください。

3. ドキュメントの検索

追加されたドキュメントを実際に検索してみる。下記サンプルは”DocumentDB”キーワードで検索をして上位5つの結果を取得、レスポンスフィールドとしてitemidとtitleフィールドを指定している。ドキュメントの検索APIはSearch Documents (Azure Search Service REST API)を参照ください。

ただし上記の実行結果は次のように日本語マルチバイト文字がすべて”\uHHHH”のようなユニコードの16進表現でエンコードされている。さらに1行レスポンスであるため結果のJSON構造が非常に分かりづらく可読性のない状況となっている。

{"@odata.context":"https://yoichikaecdemo0.search.windows.net/indexes('articles')/$metadata#docs(itemid,title)","@odata.count":2,"value":[{"@search.score":1.1298637,"itemid":"1","title":"DocumentDB \u306e\u65b0\u3057\u3044\u30a4\u30f3\u30dd\u30fc\u30c8 \u30aa\u30d7\u30b7\u30e7\u30f3"},{"@search.score":1.058217,"itemid":"4","title":"DocumentDB SDK \u3067\u30d1\u30fc\u30c6\u30a3\u30b7\u30e7\u30f3\u5206\u5272\u3092\u30b5\u30dd\u30fc\u30c8"}]}

そこで上記の結果出力に対して(1)JSONを見やすくPretifyして、(2)16進数表現でエンコードされたUnicodeをデコードする2つの処理を加えたい。まずは(1)JSON Pretifyだが、Python2.6以上の環境であればで「python -mjson.tool」コマンドでJSONをPretify出力することができる。次に(2)16進数表現のデコードはStackOverflowで見つけたワンライナー「perl -Xpne ‘s/\\u([0-9a-fA-F]{4})/chr(hex($1))/eg’」を使用する。先のcUrlコマンドにこの2つの処理をパイプでつなげて加えてみたのが下記コード。

以下の実行結果のとおり無事JSON Pretify+デコードされた出力となった。

{
    "@odata.context": "https://yoichikaecdemo0.search.windows.net/indexes('articles')/$metadata#docs(itemid,title)",
    "@odata.count": 2,
    "value": [
        {
            "@search.score": 1.1298637,
            "itemid": "1",
            "title": "DocumentDB の新しいインポート オプション"
        },
        {
            "@search.score": 1.058217,
            "itemid": "4",
            "title": "DocumentDB SDK でパーティション分割をサポート"
        }
    ]
}

REST UIツール

今回cUrlコマンドでのお手軽にAPI実行を紹介したがREST系処理の実行ということであればFiddlerやPostmanのようなツールのほうがエンコード・デコード、JSON/XMLの整形表示など面倒な処理を自動でやってくれる分楽だったりする。特にPostmanはおすすめ。