Watson Discovery ServiceのData Crawlerでデータベース(MySQL)をクロールしてみた


当記事は[Watson Discovery Serviceが日本語対応したので、触ってみた【やってみた】編](https://qiita.com/ishida330/items/bb1bd4cf61da9b9c6da2)の続きです。

# Watson DiscoveryのData Crawlerとは

Data Crawlerとは、オンプレミス環境にあるファイル・サーバーやデータベース等のデータソースをクロールして、コンテンツをJSON形式に変換の上、クラウド上のWatson DiscoveryのCollectionに自動的に格納(Ingest)してくれるプログラムです。

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/2b159436-f396-354c-256f-4cf208e3c548.png)


- Data Crawlerのプログラムは別途ダウンロードして自分の環境にインストールします
- 現状、Linux(RHEL 6/7, Ubuntu 15/16)のみサポートしています
- 2017/12月時点では以下のデータソースをクロールするConnectorを提供しています
    1. ファイルシステム
    2. データベース(JDBC)
    3. CMIS (Content Management Interoperability Services)
    4. SMB , CIFS, Sambaでのファイル共有
    5. SharePoint / SharePoint Online
    6. Box


今回はDatabase Connectorを使ってMySQLのテーブルをクロールしてみます。

[Crawlerのドキュメント](https://console.bluemix.net/docs/services/discovery/data-crawler.html)

# やってみる

## 事前準備
私の環境は下記です

- VMWare Workstation上のUbuntu 16.4(LTS) Server (64bit)
- MySQL Ver 14.14 Distrib 5.7.20

以下の事前準備をしておきます（詳細は割愛)

1. Watson Discovery 上で日本語環境のCollectionを作成しておく (手順は[この記事](https://qiita.com/ishida330/items/bb1bd4cf61da9b9c6da2)参照)
2. Java Runtimeのインスト―ル( openjdk-8-jre )
3. MySQLのJDBCドライバー([Connector/J](https://dev.mysql.com/downloads/connector/j/))のダウンロード
    - MySQLの場合はライセンスの関係でData Crawlerにバンドルできないので、自分でダウンロードしておく必要があるそうです
4. MySQLで簡単なテーブルを作成

```sql
create table SALES_HIST(
	CUSTNO varchar(8) not null,
	NAME varchar(20),
	SALES_DATE date,
	AMOUNT  INTEGER,
	PRICE   DECIMAL(10),
	TOTAL   DECIMAL(10,3)
);

mysql> select * from SALES_HIST;
+--------+--------+------------+--------+-------+-----------+
| CUSTNO | NAME   | SALES_DATE | AMOUNT | PRICE | TOTAL     |
+--------+--------+------------+--------+-------+-----------+
| 1      | 山田   | 2011-01-12 |     70 |   510 | 35700.000 |
| 2      | 鈴木   | 2012-05-19 |     20 |  1120 | 22400.000 |
| 3      | 佐藤   | 2011-10-12 |     70 |   270 | 18900.000 |
| 4      | 太田   | 2016-04-10 |     30 |  1200 | 36000.000 |
| 5      | 中村   | 2014-08-16 |     70 |   790 | 55300.000 |
+--------+--------+------------+--------+-------+-----------+
5 rows in set (0.00 sec)

```

## Crawlerのダウンロードとインストール

IBM Cloudにログイン後、Watson Discovery Serviceのインスタンスを選択～「管理」のパネルの**下の方**にDataCrawlerの導入イメージがあります。私はUbuntuなのでDEB形式のものをダウンロードしました。

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/2b56502b-e532-3eab-92f1-3b4fcb6cea35.png)

導入はdpkg -i ファイル名であり、一瞬で終わります。DEB/RPMの場合はPATHも通ってます。

```bash
root@ubuntu:~# ls
crawler_2.1.4_all.deb
root@ubuntu:~# dpkg -i crawler_2.1.4_all.deb
Selecting previously unselected package crawler.
(Reading database ... 58624 files and directories currently installed.)
Preparing to unpack crawler_2.1.4_all.deb ...
Unpacking crawler (2.1.4) ...
Setting up crawler (2.1.4) ...
Processing triggers for man-db (2.7.5-1) ...
root@ubuntu:~#

```
## 構成
### MySQLのJDBCドライバーのコピー

入手したJDBCドライバーのjarファイル(mysql-connector-java-5.1.45-bin.jar)を/opt/ibm/crawler/connectorFramework/crawler-connector-framework-0.1.18/lib/java/databaseにコピーします

### DBパスワードのvcryptによる生成

後でMySQLのデータベース権限のあるユーザーのIDとパスワードを構成ファイルに登録する必要があるのですが、パスワードはData Crawlerに添付されているvcryptというツールで暗号化しておく必要があります。この手順がWDSのドキュメントに詳しく書いてないので、Forumのエントリー[How do I encrypt password for Data Crawler using its vcrypt ?](https://developer.ibm.com/answers/questions/337080/how-do-i-encrypt-password-for-data-crawler-using-i.html)を参考に下記のように実施しました。

- 下記の"password"の部分を皆様のDBのパスワードに変更してください。あとはそのままでいいと思います
- 結果、暗号化された長ーい文字列がmy_secret_data.txtに格納されます

```bash
root@ubuntu:/opt/ibm/crawler/bin# ls
crawler  __get-java-property  __get-java-property.bat  vcrypt
root@ubuntu:/opt/ibm/crawler/bin#  ./vcrypt --encrypt --keyfile ../share/examples/config/id_vcrypt -- "password" > my_secret_data.txt
root@ubuntu:/opt/ibm/crawler/bin# cat my_secret_data.txt
[[vcrypt/3]]xxxxxxxxX0jIDov05ZLyKuDGdcZPT25g|7xH26XwIt6UAcA1lS+n5h8QQ3Fk5bu/9
root@ubuntu:/opt/ibm/crawler/bin#
```
最終的に
>[[vcrypt/3]]xxxxxxxxX0jIDov05ZLyKuDGdcZPT25g|7xH26XwIt6UAcA1lS+n5h8QQ3Fk5bu/9

という文字列が入手できました（末尾は改行なし)

### Configファイル群の設定
Data Crawlerは**/opt/ibm/crawler**にインストールされます。以下がディレクトリ構成の一部ですが、share/examples/configの下に複数の構成ファイル(*.conf)があります。このconfig以下を任意のディレクトリーにコピーして、そちらを編集します。

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/d83bf790-17c6-0990-ad2e-b67d06c08799.png)

コピーした先(私の場合は/my)の下の下記４つの構成ファイルを編集します。以下が編集すべきファイルと保有している情報です。
![image](https://qiita-image-store.s3.amazonaws.com/0/108535/6913ecea-eca4-2cab-e127-e1b01ee778f4.png)

ドキュメントには色々書いてありますが、私が変更した箇所を下記にお示しします。（要はこの9か所を設定すれば動きます、ってこと）

**config/crawler.conf**
① .. database.confに書き換え（元はfilesystem.confのため)
② .. database-seed.confに書き換え（元はfilesystem-seed.confのため)
③ .. connectorFramework/lib/java/databaseにMySQLのJDBCドライバーをコピーしたので、相対位置としてディレクトリを指定

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/8dc9c7cd-503a-b07f-3af2-4cd12ac07e4a.png)

**seed/database-seed.conf**

④ .. このURLは「data crawlerのお約束」で、jdbcのものではありません。色々試した結果では、現状（下記⑥⑦のjdbcの指定がきちんとしてあれば）テーブル名以外は「なんでもいい」ようです。例えばここをvalue="**anystring://hoge:9999/hogedb**/SALES_HIST?per=1000" のように適当に書いても普通に動きました。　

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/1414bd76-aadb-f232-ea80-5dd09fb9f0f5.png)

- クロール対象のテーブルや条件を指定する方法として、以下のようにdatabase-seed.confでsql=でカスタムSQLを指定する方法もあります。（URLエンコード要)

例) SELECT * FROM SALES_HIST WHERE PRICE=510を実行
![image](https://qiita-image-store.s3.amazonaws.com/0/108535/a7f802b8-5c8e-a765-1a2c-fd9c62bf9e64.png)


⑤..データベースに接続する際に使うユーザーIDとパスワード。パスワードは前項でvcryptにより生成したパスワード文字列を指定します。

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/7c1f2e86-33f4-347f-8165-04b634cd0d9b.png)

⑥⑦..通常のMySQLのJDBCドライバークラスとJDBC接続文字列を指定します。構成ファイルのコメントでは「オプション」的扱いに読めましたが、指定しないとJDBCドライバーが見つからない、などのエラーになりますので「必須」です。

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/4660aa18-65ce-b615-d096-1c8a3757a5c9.png)

**discovery/diceovery-service.conf**
WDSインスタンスの接続情報を指定します。
⑧ .. Enviironment IDなどはDiscoveryのUI Toolingから参照できます
⑨ .. Discoveryのサービス資格情報から参照できます

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/305b7b6c-ae62-e5bd-a7ba-8a2e8d529837.png)

上記は「必要最低限」の設定です。それ以外の細かい情報はオンラインのドキュメント [Configuring Database crawl options](https://console.bluemix.net/docs/services/discovery/data-crawler-seeds.html#configuring-database-crawl-options)や[Configuring the Data Crawler](https://console.bluemix.net/docs/services/discovery/data-crawler-discovery.html#configuring-the-data-crawler)、またはcrawler導入先のshare/docやshare/manにあります。
## 実行
### crawlerコマンド
詳しい説明は:green_book:[Crawling your data repository](https://console.bluemix.net/docs/services/discovery/data-crawler-run.html#crawling-your-data-repository)をご参照ください

| コマンド | 意味 |
|:--|:--|
| crawler testit | クローラーの構成をテストします|
| crawler crawl | (初回) クロールを実行します |
| crawler refresh | (2回目以降) クロール対象を最新状態にリフレッシュします |
| crawler resume | クローリングを一時停止します |
| crawler restart | 一時停止したクローリングを再開します |
| crawler --help | コマンドのヘルプを表示します |



### テスト(crawler testit )

構成の確認に crawler testitコマンドが使えます。
- crawler testitはデフォルトでは現在位置の下のconfig/crawler.confを探します

```bash
root@ubuntu:/my# pwd
/my
root@ubuntu:/my# ls
config  connector-fatal-error  logs  storage  tmp
root@ubuntu:/my# crawler testit
2017-12-20 06:27:30,467 INFO: Connector Framework service will start and connect to crawler on port 38833
2017-12-20 06:27:30,866 INFO: This crawl is running in Test-It mode
2017-12-20 06:27:30,867 INFO: At most one item will be sent to the configured output adapter, if it matches the configured seed.
2017-12-20 06:27:30,867 INFO: After the seed is processed, at most 5 subsequently enqueued URLs will be displayed, but not retrieved.
2017-12-20 06:27:30,868 INFO: Running a crawl...
2017-12-20 06:27:30,875 INFO: URLs matching these patterns will be not be processed: (?i)\.(xlsx?|pptx?|jpe?g|gif|png|mp3|tiff)$
2017-12-20 06:27:30,922 INFO: HikariPool-1 - Starting...
2017-12-20 06:27:31,131 INFO: HikariPool-1 - Start completed.
Sending document to Output Adapter: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0
2017-12-20 06:27:33,421 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0
2017-12-20 06:27:44,629 INFO: DocumentID (e1c01375afb372ff0d535160acb569b335e4735f6ed3416e8ec20454257a5585) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0) upload verified as available.
2017-12-20 06:27:47,305 INFO: The service for the Connector Framework Input Adapter was signaled to halt.
Attempting to shutdown the crawler cleanly.
root@ubuntu:/my#
```
よって現在位置が間違っていると構成ファイルが見つからないのでエラーになりますが、--configで明示的に構成ファイルを指定可能です。(詳しくは--help)

```bash
root@ubuntu:/my# cd ..
root@ubuntu:/# crawler testit
com.typesafe.config.ConfigException$IO: config/crawler.conf: java.io.FileNotFoundException: config/crawler.conf (No such file or directory)
An expected config file was not found.
This is the default configuration file. Pass --config to specify a configuration file.
Checking Java version since crawler exited irregularly...
Your Java version is 1.8.0_151, which Crawler supports. Inspect logging output for errors.
root@ubuntu:/# crawler testit --config my/config/crawler.conf
2017-12-20 06:30:41,988 INFO: Connector Framework service will start and connect to crawler on port 37259
2017-12-20 06:30:42,392 INFO: This crawl is running in Test-It mode
2017-12-20 06:30:42,393 INFO: At most one item will be sent to the configured output adapter, if it matches the configured seed.
2017-12-20 06:30:42,394 INFO: After the seed is processed, at most 5 subsequently enqueued URLs will be displayed, but not retrieved.
2017-12-20 06:30:42,394 INFO: Running a crawl...
2017-12-20 06:30:42,403 INFO: URLs matching these patterns will be not be processed: (?i)\.(xlsx?|pptx?|jpe?g|gif|png|mp3|tiff)$
2017-12-20 06:30:42,454 INFO: HikariPool-1 - Starting...
2017-12-20 06:30:42,650 INFO: HikariPool-1 - Start completed.
Sending document to Output Adapter: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0
2017-12-20 06:30:44,896 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0
2017-12-20 06:30:56,975 INFO: DocumentID (e1c01375afb372ff0d535160acb569b335e4735f6ed3416e8ec20454257a5585) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0) upload verified as available.
2017-12-20 06:30:59,966 INFO: The service for the Connector Framework Input Adapter was signaled to halt.
Attempting to shutdown the crawler cleanly.
root@ubuntu:/#

```

### 実行(crawler crawl )
では、実際にクロールして結果をDiscoveryにIngestしましょう。crawler crawlコマンドです。

```bash
root@ubuntu:/my# crawler crawl
2017-12-20 06:58:17,205 INFO: Connector Framework service will start and connect to crawler on port 40026
2017-12-20 06:58:17,617 INFO: This crawl is running in CrawlRun mode
2017-12-20 06:58:17,617 INFO: Running a crawl...
2017-12-20 06:58:17,622 INFO: URLs matching these patterns will be not be processed: (?i)\.(xlsx?|pptx?|jpe?g|gif|png|mp3|tiff)$
2017-12-20 06:58:17,686 INFO: HikariPool-1 - Starting...
2017-12-20 06:58:17,905 INFO: HikariPool-1 - Start completed.
2017-12-20 06:58:20,467 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0
2017-12-20 06:58:20,470 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=1
2017-12-20 06:58:20,543 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=3
2017-12-20 06:58:20,546 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=2
2017-12-20 06:58:20,574 INFO: Uploading Indexable: mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=4
2017-12-20 06:58:33,806 INFO: DocumentID (8966de40318af6f73606fe2db660ebdda1dc6b0dc892d14523cb4cb74d2d0236) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=2) upload verified as available.
2017-12-20 06:58:34,198 INFO: DocumentID (c931548fe065bc85124a865e2916b0454f472155a6eaa678303c942f2d6855ff) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=3) upload verified as available.
2017-12-20 06:58:34,342 INFO: DocumentID (079d99d65f3c4bf778342d121f4deafde534431012aff9aaf7096c5fff75a2fa) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=4) upload verified as available.
2017-12-20 06:58:34,391 INFO: DocumentID (e1c01375afb372ff0d535160acb569b335e4735f6ed3416e8ec20454257a5585) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=0) upload verified as available.
2017-12-20 06:58:38,251 INFO: DocumentID (1c90510f7cc988a2f7c178ce770f7107688ac135badb9f4f4d97f4972a468bdd) as URI (mysql://192.168.118.129:3306/demo/SALES_HIST?key-val=1) upload verified as available.
2017-12-20 06:58:40,630 INFO: HikariPool-1 - Shutdown initiated...
2017-12-20 06:58:40,667 INFO: HikariPool-1 - Shutdown completed.
2017-12-20 06:58:40,669 INFO: The service for the Connector Framework Input Adapter was signaled to halt.
Attempting to shutdown the crawler cleanly.
root@ubuntu:/my#


```

### 結果の確認

DiscoveryのUI Toolで見ると確かに5件アップロードされています。
![image](https://qiita-image-store.s3.amazonaws.com/0/108535/a2ef61de-230a-14ee-bfaf-9fd6c9e32d35.png)

### リフレッシュについて

いったんクロールしたコンテンツを最新状態に保つためには**crawler refresh**コマンドを実行します。ここで同じデータソースに対してのリフレッシュの結果は「Discovery上に文書があれば更新、なければ挿入」＝DBで言うUPSERT的な振る舞いになります。(crawler crawlで再度全クロールしても同じ）「何度も実行すると、どんどんAppendされてしまう」という動きにはなりませんのでご安心ください。これを実現するためにCrawlerはローカルでdocIdとuriを保持しているそうです。（セキュリティの観点からコンテンツはローカルには保持しません。読んで、WDSに送って、終わり、です）

ここで、もし２回目以降のクロールをrefreshでなく初回と同じcrawlで実行したらどうなるでしょうか。以下、自分でやった結果からの所見ですが、database-seed.confのURL部分(当記事の構成のところで「なんでもいいみたい」と書いたところ）を変えていなければ、結果はREFRESHと同じ=UPSERT、です。しかし初回と変えた場合は「新規の文書がある」と判定されて置換ではなくて追加になってしまいます。（まあレアケースですが）

★ココの話です
![image](https://qiita-image-store.s3.amazonaws.com/0/108535/20eea6c0-e6ff-90b1-771c-437a991eafb8.png)

以下はやってみた結果です。
1. まず以下のURLで初回に5件格納します（crawler crawl)

>      name ="url",
>      value="mysql://192.168.118.129:3306/demo/SALES_HIST"

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/8f085dbe-fb49-56b9-8a25-ddc3b2af8bd2.png)

2.念のためリフレッシュ(crawler refresh)します。→　件数は変わりません

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/458c9d6d-8f7a-f316-8ee8-688d0c86e623.png)

3.URL部分を以下のように書き換えてリフレッシュ(crawler refresh)します→でもREFRESHなら件数は変わりません）

>      name ="url",
>      value="hoge://hoge:3306/hoge/SALES_HIST"

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/245cad72-321a-9280-a14e-9899f8bee71f.png)

4.このまま定義は変更せず、クロール( crawler crawl ) します→件数が10件になりました。=＞**新規の文書とみなされてAppendされてしまいました**
![image](https://qiita-image-store.s3.amazonaws.com/0/108535/395ee051-09c1-669b-6a8d-7a884113f1df.png)

5.このままもういちどクロール( crawler crawl ) します→今度は(URLが同じなので)件数もかわりません。

![image](https://qiita-image-store.s3.amazonaws.com/0/108535/0ffa8ad8-541d-7f90-b112-2b5544426a80.png)

ウダウダ書きましたが、要は「**２回目以降はrefreshで**」ってことです。

# その他の補足・注意点など

- :warning: 現状、テーブルの数値カラムはDiscovery上は「文字」として格納されるようです。（クローラーからPOSTされるJSON中で既にデータがクオートで囲まれています)　ゆえにDiscovery上で当該カラムに対してのSUM/AVERAGEなどの数字の集約操作はできません。Discovery本体はJSONで数字として表現しておけば（＝データをクオートで囲まなければ）ちゃんと数字として扱えるので、このクローラーの仕様はなんだかなー、という気もしますが、まあよく考えてみるとDiscoveryはドキュメントなどの非構造化データを検索するのが本筋ですから、数字データを扱えなくても大した話ではないのかな～とも思います。（DBの数字データを集約したいなら、もっといい方法は他にいくらでもありますからね）

- クローリングの性能を改善する設定としてcrawler.confにthrottlingのセクションにいくつかの指定項目があります。confのコメントに説明が書いてあるので必要に応じお試し下さい。 


以上です。