AzureSearch

de:code 2017で私が担当したAzure Searchセッションのスライドと動画が公開されたので、セッション中でもでお見せしたマテリアルと合わせてここでフォローアップさせていただきます。



セッションスライドはこちら。セッション持ち時間が50分と短い中での説明なのでいろいろとカットしたのですが、Appendixという形でカットしたスライドや補足情報を載せてあります。P47〜65です。


[DI08] その情報うまく取り出せていますか? ~ 意外と簡単、Azure Search で短時間で検索精度と利便性を向上させるための方法 from de:code 2017

その他、セッション中のデモで使用したツールやBotのソースコードはGithubにアップしております。

最後に、今回のセッションは検索サイド(クエリーを投げてインデックスにヒットして、ランキング処理されて結果が返却されるまで)に絞ってますが、他にもインデックスデータを生成するまでの話、モニタリングやオペレーション的な話、もう少しデープに言語処理的な話など取り上げたいトピックがまだまだあるので時間を見つけて私なりにブログやスライドに整理していきたいと思うております。

Azure Searchのアナライザーによるテキスト解析結果を出力する(だけの)ツールを作ってみたのでここで紹介します。その名もazure-search-ta(ta=Test Analyzer)。中身はAzure SearchのAnalyzer APIの出力結果を整形して表示させていているだけの単純なものでありますが、Azure Searchの全文検索チューニングやキーワードにヒットしない原因調査をする際には役に立つと思ってます。「どうしてこのキーワードがひっかからないの?」を突き詰めるには最終的にアナライザのテキスト解析結果と突き合わせる必要があるのと、アナライザーを選択する際にテキスト解析が視覚化されていると判断しやすいだろうと。ツールは2種類で (1)Web UIツールと(2)コマンドラインツール

Web UI Tool


azure-search-ta-capture
https://github.com/yokawasa/azure-search-ta

インストールは超簡単。(1)Githubからazure-search-taをclone (2)azure-search-ta/ui 配下のファイルをPHPが動くWebサーバにコピー (3)analyze-api.phpをエディタで開いてお使いのAzure Searchカウント名とAzure Search API Adminキーの値を設定ください。あとはazure-search-ta-ui.htmlにアクセスいただければ上記のようなUIが出力されるはずです。余談ですが、どうしてHTML/JSだけではなく間にPHPを挟んでいるのかについて、Azure SearchのAnalyze APIや管理系APIリクエストに位置付けられており、管理系APIはvia CORSでのリクエストを受け付けていないからなのです。

$ git clone https://github.com/yokawasa/azure-search-ta.git

$ vi azure-search-ta/ui/analyze-api.php

$azureSearchAccount="<Azure Search Service name>";
$azureSearchApiKey = "<Azure Search API Admin Key>";

Command-Line Tool

1. インストールと設定

pipでazure-search-taパッケージをインストール。既に古いバージョンをインストール済みでアップデートする際は――upgradeをつけて実行ください。

$ pip install --user azure-search-ta

次に、search.confにお使いのAzure Searchカウント名とAzure Search API Adminキーの値を設定ください。

# Azure Search Service Name ( never put space before and after = )
SEARCH_SERVICE_NAME=<Azure Search Service name>
# Azure Search API Admin Key ( never put space before and after = )
SEARCH_API_KEY=<Azure Search API Admin Key>

2. 使い方

いくつか使い方を紹介します。

usage: azure-search-ta [-h] [-v] [-c CONF] [-i INDEX] [-a ANALYZER]
                          [-t TEXT] [-o OUTPUT]

This program do text analysis and generate formatted output by using Azure
Search Analyze API

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -c CONF, --conf CONF  Azure Search Configuration file. Default:search.conf
  -i INDEX, --index INDEX
                        Azure Search index name
  -a ANALYZER, --analyzer ANALYZER
                        Azure Search analyzer name
  -t TEXT, --text TEXT  A file path or HTTP(s) URL from which the command line
                        reads the text to analyze
  -o OUTPUT, --output OUTPUT
                        Output format ("simple" or "normal"). Default:normal

ja.microsoftアナライザーによるテキスト解析結果をnormalモードで出力する例。Analyzer APIはパラメータにインデックス名が必要なので、なんでもいいのでご自分のアカウントに設定されているインデックス名を指定ください。ここではインデックスtaでsimple.txtに対象のテキストを記入して実行しています。

$ cat sample1.txt
吾輩は猫である

$ azure-search-ta -c ./search.conf -i ta -a ja.microsoft --t sample1.txt
INPUT: 吾輩は猫である
TOKENS: [吾輩] [猫] [ある]

Azure Searchにビルトインされているアナライザーでけでなく皆さんが作成したカスタムアナライザーによるテキスト解析結果も当然出力可能です。以下は、インデックスtacustomにNグラム分割のカスタムアナライザーmy_ngramを作成したとしてmy_ngramアナライザーによるテキスト解析結果を出力する例です。カスタムアナライザーの定義はGithubページのほうに詳しく書いているのでよかったらどうぞ。

$ cat sample1.txt
吾輩は猫である

$ azure-search-ta -c ./search.conf -i tacustom -a my_ngram --t sample1.txt -o simple
'吾輩' '吾輩は' '吾輩は猫で' '吾輩は猫' '輩は猫であ' '輩は' '輩は猫' '輩は猫で' 'は猫であ' 'は猫で' 'は猫' 'は猫である' '猫であ' '猫で' '猫で ある' 'である' 'であ' 'ある'

他には、azure-search-taはインターネット上のページのテキスト解析機能も付いているので、そいつを試してみます。ja.luceneアナライザを使ってhttp://www.yahoo.co.jpトップページの内容を解析します。

$ azure-search-ta -c ./search.conf -i ta -a ja.lucene --t http://www.yahoo.co.jp -o simple
'html' 'public' 'w' '3' 'c' 'dtd' 'html' '4' '01' 'transitional' 'en' 'http' 'www' 'w' '3' 'org' 'tr' 'html' '4' 'loose' 'dtd' 'yahoo' 'japan' 'ヘルプ' 'yahoo' 'japan' 'トップページ' '機能' '正しく' 'ご' '利用' 'いただく' '下記' '環境' '必要' 'windows' 'internet' 'explorer' '9' '0' '以上' 'chrome' '最新' '版' 'firefox' '最新' '版' 'microsoft' 'edge' 'macintosh' 'safari' '5' '0' '以上' 'internet' 'explorer' '9' '0' '以上' 'ご' '利用' '場合' 'internet' 'explorer' '互換' '表示' '参考' '互換' '表示' '無効' '化' '試し' 'くださる' '東北' '自転車' 'イベント' '参加' '方法' '事前' 'チェック' 'こだわり' 'ご' '当地' 'スイーツ' '取り寄せる' '上海' 'マージャン' '定番' 'ゲーム' '無料' '遊ぶ' 'ニュース' '11' '時' '1' '分' '更新' '韓国' '北' '太陽' '政策' '回帰' '娘' '放置' '熱中' '症' '死なす' '逮捕' '保育' '死亡' '事故' '睡眠' '中' '注意' '三菱' 'ufj' '法人' '融資' '銀行' '集約' 'フレーバ' '水' '人気' '続く' '各国' '大' '規模' 'サイバ' '攻撃' '西武' '菊池' '沢村' '超' '驚異' '被' '打率' '寺島' 'しのぶ' '長男' '超' '英才' '教育' 'もっと' '見る' '記事' '一覧' 'ゴミ' '収集' '車' '子育て' '5' '月' '13' '日' '7' '時' '55' '分' '配信' '産経新聞' 'ショッピング' 'ヤフオク' '旅行' 'ホテル' '予約' 'ニュース' '天気' 'スポーツナビ' 'ファイナンス' 'テレビ' 'gyao' 'y' 'モバゲ' '地域' '地図' '路線' '食べる' 'ログ' '求人' 'アルバイト' '不動産' '自動車' '掲示板' 'ブログ' 'ビューティ' '出会い' '電子' '書籍' '映画' 'ゲーム' '占い' 'サービス' '一覧' 'ログイン' 'id' 'もっと' '便利' '新規' '取得' 'メール' 'メールアドレス' '取得' 'カレンダ' 'カレンダ' '活用' 'ポイント' '確認' 'ログイン' '履歴' '確認' '会社' '概要' '投資' '家' '情報' '社会' '的' '責任' '企業' '行動' '憲章' '広告' '掲載' '採用' '情報' '利用' '規約' '免責' '事項' 'メディア' 'ステートメント' 'セキュリティ' '考え方' 'プライバシ' 'ポリシ' 'copyright' 'c' '2017' 'yahoo' 'japan' 'corporation' 'all' 'rights' 'reserved'

もう少しだけGithubのほうには詳しく書いてあるのでそちらも参考にしてください。

最後に

de:code 2017というマイクロソフトの開発者向けイベントでAzure Searchのセッション(DI08)を担当します。「その情報うまく取り出せていますか? 〜 意外と簡単、Azure Search で短時間で検索精度と利便性を向上させるための方法」と少々長いセッションタイトルでありますが、つまるところAzure Searchでの全文検索はどのような流れで結果をとってきて精度が向上させるにはどういった方法があるのか?について50分と短い時間ではあるもののできるだけ体系的にお話しようと思ってます。de:code参加される方よろしかったら是非。



decode2017-azure-search


https://www.microsoft.com/ja-jp/events/decode/2017/sessions.aspx#DI08

OCRとはOptical Character Recognitionの略で日本語にすると光学文字認識と訳されており、ざっくりと画像の中の文字をテキストに変換する技術のことを指す。テキストに変換されるということは勘が鋭い皆さんはお気づきだと思うが、テキストの全文検索であったり、テキストから音声への変換、さらには機械翻訳を使って多言語への変換といった展開が考えられる。そんな可能性を秘めたOCRであるが、ここではそのOCRの技術を使ってビデオファイルから抽出したテキストデータを元にビデオに字幕表示したり、動画中に表示される文字を全文検索をするデモを紹介したい。内容的には「Azure Media & Cognitiveデモ:Speech-To-Text」で紹介したデモのOCR版といったところ。



Video OCR Demo Screenshot

( デモサイト | Source Code )

主要テクノロジーと機能

Azure Media OCRメディアプロセッサによるテキスト抽出

このデモではAzure Media OCRメディアプロセッサー(MP)を使用してビデオファイル内のテキストコンテンツを検出してテキストファイルを生成している。OCRメディアプロセッサーは入力パラメータによりビデオ解析の挙動を調整することができる。主なパラメータとしては検索対象テキストの言語(日本語もサポート)、テキストの向き、サンプリングレート、ビデオフレーム内のテキスト検出対象のリージョンがあるが、本デモでの入力パラメータ(Video-OCR-Search-Python/src/ocr-detectregion.json)は以下の通り検索対象言語は日本語、1秒おきのサンプリングレート、テキスト検出対象のリージョンからビデオフレーム内の上部1/4を省く設定(検出対象をフレームトップから85 pixel以下を対象)にしている。

{
    "Version":"1.0",
    "Options":
    {
        "Language":"Japanese",
        "TimeInterval":"00:00:01.000",
        "DetectRegions":
        [
            {"Left":"0","Top":"85","Width":"1280","Height":"635"}
        ]
    }
}

そして、Azure Media OCRメディアプロセッサはビデオで検出された文字を下記のような表示時間に基づいてセグメント化された形で結果出力する。結果ファイルの完全版はこちら(azuresubs.json)を参照ください。

{
    "fragments": [
        {
            "start": 0
            "interval": 319319,
            "duration": 319319,
            "events": [
                [
                    {
                        "language": "Japanese",
                        "text": "Azure の 契 約 内 容 を 変 更 す る Microsoft Azure"
                    }
                ]
            ]
        },
        {  /* fragment1 */ },
        {  /* fragment2 */ },
        ...
        {  /* fragmentN */ }
    ],
    "version": 1,
    "framerate": 29.97,
    "height": 720,
    "width": 1280,
    "offset": 0,
    "timescale": 30000
}

入力パラメータと出力形式共に詳細はこちらのドキュメントを参照いただくとしてAzure Media OCRメディアプロセッサ利用の注意点として次の2つがある:

字幕(Closed Caption)データフォーマットへの変換

まず上記Azure Media OCRメディアプロセッサー(MP)から出力されたJSONファイルの内容を元に字幕用のデータフォーマットであるWebVTTフォーマットファイルを生成している。そして「Azure Media & Cognitiveデモ:Speech-To-Text」でも紹介したようにHTML5のtrackタグエレメントによるビデオファイルの字幕表示機能使ってOCRの内容の字幕表示を実現している。本デモではHTML5に下記のように動画(TransferanAzuresubscriptionJP.mp4)をVideoソースとしてtrackエレメントにWebVTTファイル(azuresubs.vtt)を指定している。

<video id="Video1" controls autoplay width="600">
    <source src="TransferanAzuresubscriptionJP.mp4" srclang="en" type="video/mp4">
    <track id="trackJA"  src="azuresubs.vtt"  kind="captions" srclang="ja" label="OCR Subtitle" default>
</video>

Azure Searchによる全文検索

デモページ上部にある検索窓にキーワードを入力してGoボタンを押すとビデオコンテンツからOCR抽出されたテキストを元に生成された字幕データを全文検索してキーワードにマッチしたテキストとその表示時間に絞り込むことができる。仕組みは「Azure Media & Cognitiveデモ:Speech-To-Text」と全く同じで、Azure Searchを使用して字幕データを解析して字幕表示時間とその対応テキストを1ドキュメントレコードとしてAzure Searchにインジェストしてその生成されたインデックスに対してキーワードを元に全文検索することで実現している。検索用のインデックススキーマもまったくおなじで次のように字幕表示時間とその対応テキストをレコード単位となるように定義している。

{
    "name": "ocr",
    "fields": [
        { "name":"id", "type":"Edm.String", "key": true, "searchable": false, "filterable":false, "facetable":false },
        { "name":"contentid", "type":"Edm.String","searchable": false, "filterable":true, "facetable":false },
        { "name":"beginsec", "type":"Edm.Int32", "searchable": false, "filterable":false, "sortable":true, "facetable":false },
        { "name":"begin", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
        { "name":"end", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
        { "name":"caption", "type":"Edm.String", "searchable": true, "filterable":false, "sortable":false, "facetable":false, "analyzer":"ja.microsoft" }
     ]
}

デモデータ作成手順

GithubプロジェクトページVideo-OCR-Search-Pythonの1. Preparationと2. Batch executionを実施いただければOCR抽出されたテキストを元に字幕データ*.vttファイルが生成され、そのテキストがAzure Searchに格納されてデモページ表示のための準備は完了する。最後に表示用のページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlとsearch.jsの変更のみでいけるはず。

本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

Enjoy Video OCR demo!

ビデオコンテンツを音声認識エンジンでテキスト化してそれを元にスピーチ検索するデモコンテンツを紹介したい。これは過去にde:code2016というマイクロソフトの開発者向けイベントで行ったブレイクアウトセッション「DEV-18: Azure Search Deep Dive」にて紹介したビデオコンテンツのスピーチ検索デモを簡略化して再利用しやすいものにしたものである。



Video STT Demo Screenshot

( デモサイト | Source Code )

主要テクノロジーと機能

Azure Media Indexer 2 Previewによる音声からテキスト抽出

このデモではAzure Media Indexer 2 Preview メディア プロセッサー (MP)を使用してビデオコンテンツからテキストを抽出している。このAzure Media Indexer 2 Previewは自然言語処理(NLP)や音声認識エンジンを駆使してビデオコンテンツより字幕用データ(時間やテキスト)や検索可能にするためのメタデータを抽出することができる。Indexer 2という名前の通り前のバージョンであるAzure Media Indexerが存在するが、これと比較すると、Azure Media Indexer 2 Previewは、インデックス作成が高速化され、より多くの言語をサポートしていることが特徴である。2016年11月6日時点で英語、スペイン語、フランス語、ドイツ語、イタリア語、中国語、ポルトガル語、アラビア語などがサポートされている(残念ながら日本語はまだ未サポート)。

下イメージはAzure Media Indexer 2 (Preview)で生成されるTTMLWebVTTという代表的な字幕データフォーマット。


AzureMediaIndexer-ClosedCaption

HTML5と字幕(Closed Caption)

HTML5にはtrackタグエレメントを使ってビデオファイルに字幕を表示する機能が標準的に実装されている。本デモではHTML5に下記のように動画(Python_and_node.js_on_Visual_Studio.mp4)をVideoソースとしてtrackエレメントに字幕WebVttファイル(build2016breakout.vtt)を指定している。

<video id="Video1" controls autoplay width="600">
    <source src="Python_and_node.js_on_Visual_Studio.mp4" srclang="en" type="video/mp4">
    <track id="trackJA"  src="build2016breakout.vtt"  kind="captions" srclang="ja" label="Closed Captions" default>
</video>

Azure Searchによる全文検索

デモページ上部にある検索窓にキーワードを入力してGoボタンを押すとビデオコンテンツの字幕データを全文検索してキーワードにマッチしたテキストとその表示時間に絞り込むことができる。ここでは全文検索エンジンにAzure Searchを使用し、Azure Media Indexer 2 (Preview)より抽出された字幕データを解析して字幕表示時間とその対応テキストを1ドキュメントレコードとしてAzure Searchにインジェストしてその生成されたインデックスに対してキーワードを元に全文検索することで実現している。字幕データ検索用のインデックススキーマは次のように字幕表示時間とその対応テキストをレコード単位となるように定義している。

{
    "name": "stt",
    "fields": [
        { "name":"id", "type":"Edm.String", "key": true, "searchable": false, "filterable":false, "facetable":false },
        { "name":"contentid", "type":"Edm.String","searchable": false, "filterable":true, "facetable":false },
        { "name":"beginsec", "type":"Edm.Int32", "searchable": false, "filterable":false, "sortable":true, "facetable":false },
        { "name":"begin", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
        { "name":"end", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
        { "name":"caption", "type":"Edm.String", "searchable": true, "filterable":false, "sortable":false, "facetable":false, "analyzer":"en.microsoft" }
     ]
}

デモデータ作成手順

GithubプロジェクトページVideo-STT-Search-Pythonの1. Preparationと2. Batch executionを実施いただければ字幕データ*.vttファイルが生成され、そのテキストがAzure Searchに格納されてデモページ表示のための準備は完了する。最後に表示用のページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlとsearch.jsの変更のみでいけるはず。

本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

Enjoy Video Speech-to-text demo!

UPDATED:

Here is a list of fluentd plugins for Microsoft Azure Services.

Plugin NameTarget Azure ServicesNote
fluent-plugin-azurestorageBlob StorageAzure Storate output plugin buffers logs in local file and upload them to Azure Storage periodicall
fluent-plugin-azureeventhubsEvent HubsAzure Event Hubs buffered output plugin for Fluentd. Currently it supports only HTTPS (not AMQP)
fluent-plugin-azuretablesAzure TablesFluent plugin to add event record into Azure Tables Storage
fluent-plugin-azuresearchAzure SearchFluent plugin to add event record into Azure Search
fluent-plugin-documentdbDocumentDBFluent plugin to add event record into Azure DocumentDB
fluent-plugin-azurefunctionsAzure FunctionsAzure Functions (HTTP Trigger) output plugin for Fluentd. The plugin aggregates semi-structured data in real-time and writes the buffered data via HTTPS request to HTTP Trigger Function.
fluent-plugin-azure-loganalyticsLog AnalyticsAzure Log Analytics output plugin for Fluentd. The plugin aggregates semi-structured data in real-time and writes the buffered data via HTTPS request to Azure Log Analytics.

(as of Nov 23, 2016)



fluentd

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

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はおすすめ。