【少しずつ追記】XMLデータベース「BaseX」のインストールからベンチマークまで

#XMLデータベース「BaseX」のインストールからベンチマークまで
##前書き
**がっつり正規化されたRDBMSでテーブルの山を見て嫌になっちゃいました。**
階層化されたデータを表現するために、親IDを格納するカラムを作って、再帰的に親や子を探すのにも疲れちゃいました。
大体そういうデータはRDBMSには向いてません。ドキュメントベースでスキーマレスなデータベースで行きましょう。

ドキュメントベースのDBとして私が思いついたのは、

- XML が基本となる「**BaseX**」
- JSON が基本となる「**mongo**」

あたりでしょうか。どちらもオープンソースです。
プロプライエタリなものでスッゴイものもあると思いますが、貧乏なのでお金がかけれません。

しかし、私の探し方が悪いのか、何故かどちらも日本語のドキュメントが少ない。

今回は「BaseX」に挑戦してみます。
構築しながら、ハりそうな（ハマった）ところなんかもメモしていければと思います。

-- 2016-01-19 23:27

##インストール
私の環境はArch Linux なので、一発です。

```bash
yaourt -S basex
```
多分、Ubuntu、Debian なんかも

```bash
apt-get install basex
```
でいけるんでないでしょうか。試してません。

Fedora、CentOS あたりは、yum のリポジトリを追加しないといけないかもしれません。
CentOS6 の時は追加しないといけませんでした。
以下のような内容のファイルを /etc/yum.repos.d/base.repo みたいな感じで作成します。
Fedora22の例です。

```bash:/etc/yum.repos.d/basex.repo
[home_basex_opensuse]
name=BaseX openSUSE Build (Fedora_22)
type=rpm-md
baseurl=http://download.opensuse.org/repositories/home:/basex:/opensuse/Fedora_22/
gpgcheck=1
gpgkey=http://download.opensuse.org/repositories/home:/basex:/opensuse/Fedora_22//repodata/repomd.xml.key
enabled=1
```

[本家のサイトのダウンロードのページ](http://basex.org/products/download/all-downloads/)からコピってきました。Fedora23のリポジトリは書いてありませんでした。しかしなんで Suse なんだろう？
作成したら、

```bash
yum install basex
```
で、多分いけると思います。

インストールされるバイナリが、 9.92MB ! ちっちゃいなぁ…。
BaseX はJava で書いてありますので、依存関係でJava がインストールされるかもしれません。

あれ？起動方法がわかんない。

-- 2016-01-19 23:44

##起動
CentOS の場合、/usr/bin 以下にインストールされています。
Arch Linux の場合、/usr/share/java/basex/bin にインストールされていました。
というかGUIのメニューに basexgui が登録されていました。

|コマンド        |内容     |
|:--------------|:--------|
|basex          |スタンドアロンの対話モードで実行|
|basexgui       |GUIのクライアント|
|basexclient    |Client/Server モードのクライアント|
|basexserver    |Client/Server モードのサーバ|
|basexserverstop|サーバで起動したbasexを停止|
|basexhttp      |よくわかんないので今度にする|
|basexhttpstop  |http で起動したbasexを停止（多分）|

まずは素直にメニューからGUIクライアントを起動。

![Spectacle.GzU947.png](https://qiita-image-store.s3.amazonaws.com/0/39434/3aa69110-9393-a75b-e7fc-b35cb5372106.png)

素敵な画面が出てきました。これがあれば開発も楽そう。
ん？
これ、リモートのbasexサーバに繋げないのかな？
あとで考える。

スタンドアロンの対話モードもOK。

サーバの起動は、

```bash
/path/to/basex/bin/basexserver -S
```
ポートは1984がデフォルト。起動時に-pオプションで変えられるようです。
その他のオプションは--helpで。

起動スクリプトなど気の利いたものは用意されていません。
systemd の起動スクリプト書くのもめんどいので、/etc/rc.local に起動コマンドを書いて、rc.local のエグゼキュートビットを有効に設定。
これでOS起動時に自動的にサービスが起動するはず。

クライアントで接続してみる。ローカルホストなら省略できますが、

```bash
/path/to/basex/bin/basexclient -n localhost
```
パスワードが聞かれた。そんなの知らんぞ…。
http://docs.basex.org/wiki/Startup#Client.2FServer
ちゃんと書いてありました。
Username: admin
Password: admin
です。
しかし、あぶねぇパスワード。変えておきましょう。
ログインしたあとに、

```bash
> PASSWORD
```

一旦ここまでで、再起動してみます。

-- 2016-01-20 00:52

起動成功。今日はもう眠い。続きは明日。

-- 2016-01-20 00:59

##CREATE DB してみる
basexclient でローカルホストの basex サーバに接続して新規のデータベースを作成してみる。

```
> CREATE DB testdb
> XQUERY /
Stopped at ., 1/2:
[XPDY0002] root(): no context value bound.
```
ありゃ？
あぁ、ちゃんとOPENしないとダメでした。

```
> OPEN testdb
Database 'testdb' was opened in 1.48 ms.
> XQUERY /

Query executed in 0.3 ms.
```
次にデータを突っ込んでみる。
いきなりつまずいたが、データを突っ込むには先ほど作成したデータベース「testdb」の中にxmlを作らないといけないらしい。

```
> ADD TO sample1.xml "<root/>"
Resource(s) added in 71.61 ms.
> LIST testdb
Input Path   Type  Content-Type     Size  
----------------------------------------
sample1.xml  xml   application/xml  2      

Resources.
> XQUERY /
<root/>
Query executed in 0.55 ms.
```
出た。でも xml は指定してないぞ？

```
> ADD TO sample2.xml "<root2/>"
Resource(s) added in 6.98 ms.
> LIST testdb
Input Path   Type  Content-Type     Size  
----------------------------------------
sample1.xml  xml   application/xml  2     
sample2.xml  xml   application/xml  2     

Resources.
> XQUERY /
<root/>
<root2/>
Query executed in 1.16 ms.
```
なるほど。カレントのDB内のxmlすべてが検索対象なのね。

```
> XQUERY doc("testdb/sample1.xml")/root
<root/>
```
こうすれば、特定の xml ファイルに対して XQUERY が発行される。
DB名は省略できないらしい。めんどくさい。
OPENしていない状態でも実行可能。
ちょっと削除してみよう。

```
> DELETE /
0 resource(s) deleted in 1.76 ms.
> xquery /

Query executed in 0.35 ms.
>
> LIST testdb
Input Path  Type  Content-Type  Size  
------------------------------------

Resources.
```
**ぎゃーーーー。全部消えた！**
**DELETE** 、これは恐ろしいコマンドだ…。

くじけてしまったので、一旦 basexclient から抜ける。

ところでBaseXのファイルはどこにあるんだろう？

/root/BaseXData にあった。
実行したユーザのホームディレクトリにできるようだ。
そのディレクトリに下にDB名のディレクトリが作成され、その中には *.basex のファイルがあるけどバイナリのようだ。
これDBがでかくなったらどうなるんだろう？
それから、私のOSの場合、/root のファイルシステムはBtrfsなのだが、CoWの影響も気になる。
ま、でかいDB作ったあとでCoWを無効にして試してみよう。

少ししか試せなかったけど今日はここまで。
ベンチマークに使えそうな巨大な xml ファイルを探してこよう。

-- 2016-01-21 01:22

##データ投入
速度を測るにもデータをぶち込まなければ始まらない。
巨大なのxmlをダウンロードして来てぶち込んでみます。

その前にPCスペックは、
CPU Core i7-3930K 3.20GHz
メモリ 32GB
ディスク /root/BaseXData がある領域は、SSD 2台を Btrfs で RAID0 構成

インポートするファイルは、wikipedia のYahoo向けabstract、1.7GB

```
> add to wiki.xml /tmp/jawiki-20160111-abstract.xml
Resource(s) added in 102797.21 ms.
> XQUERY count(/feed/doc)
1603475
Query executed in 384.85 ms.
```
ほう、意外と速い。
検索してみる。

```
> xquery count(feed/doc/title[contains(text(),'ガンダム')])
1179
Query executed in 2191.71 ms.
```
キーワードについては見逃してください。そんな世代なんです。
BaseX はJava で書かれているので、日本語もばっちり。

インデックスを作ってみる。

```
> CREATE INDEX TEXT
Index 'TEXT' created in 24116.67 ms.
> xquery count(feed/doc/title[contains(text(),'ガンダム')])
1179
Query executed in 2122.68 ms.
```
あんましかわらん。私が想像してるインデックスとは別物なのか。

```
> CREATE INDEX FULLTEXT
Out of Main Memory.
```
うぎゃー。
ちなみに実ファイルの方は、

```
-rw-r--r-- 1 root root  25M  1月 21 02:34 atv.basex
-rw-r--r-- 1 root root    4  1月 21 00:23 atvl.basex
-rw-r--r-- 1 root root    0  1月 21 00:23 atvr.basex
-rw-r--r-- 1 root root  628  1月 22 00:01 inf.basex
-rw-r--r-- 1 root root   11  1月 21 02:34 pth.basex
-rw-r--r-- 1 root root   28  1月 21 23:59 swl.basex
-rw-r--r-- 1 root root 795M  1月 21 02:34 tbl.basex
-rw-r--r-- 1 root root 1.6M  1月 21 02:34 tbli.basex
-rw-r--r-- 1 root root 1.3G  1月 21 02:34 txt.basex
-rw-r--r-- 1 root root  43M  1月 21 23:58 txtl.basex
-rw-r--r-- 1 root root  29M  1月 21 23:58 txtr.basex
```
Btrfs の CoW を無効にしてみました。

```
# mv BaseXData BaseXData_old
# mkdir BaseXData
# chattr +C BaseXData
# cp -a BaseXData_old/* BaseXData
```
```
> xquery count(feed/doc/title[contains(text(),'ガンダム')])
1179
Query executed in 1827.73 ms.
```
少し早くなった。

もっと複雑な検索も試してみたいとこなのですが、結構いけるんではないでしょうか？

しかし、XQuery、XPathは難しいなぁ…。

-- 2016-01-22

##PHPから接続してみる
PHPから接続するには、[BaseXのClientsのページ](http://docs.basex.org/wiki/Clients)からBaseXClients.php をダウンロードしておきます。
で、PHPの手抜きなサンプル。
（手を抜いてないサンプルは、上記のリンクからもダウンロードできます）

```php:sample.php
<?php
// クラス定義ファイルのインクルード
include_once("./BaseXClient.php");

// DBへの接続とデフォルトDBのオープン
$session =  new Session("localhost", 1984, "username", "password");
$session->execute("OPEN testdb");

// クエリの発行
$input_xpath = "feed/doc/title[contains(text(),'ガンダム')]/text()";
$input_flwor = "for \$i in feed/doc/title where contains(\$i/text(), 'ガンダム') return \$i/text()";
$query = $session->query($input_flwor);

// データの表示
while($query->more()) {
    print $query->next() . "\n";
}

// クエリとDB接続のクローズ
$query->close();
$session->close();

```
クラス名がSession とか、xquery 内で改行が使えないとか、気に入らないところも…。
まぁラッパー作っちゃえばいいんですが。
あと、クラスの中でsocketが使われているので、PHP がsocket オプション付きでインストールされている必要がありそうです。

とりあえずここまで行けたら、必要なデータをxml とJSON とSQL でファイル出力していろいろ比較できそう。

-- 2016-01-24 23:12