関数型言語XQueryで大量のXMLを楽にさばく


まえがき
=======
オープンデータ隆盛のこのご時世、意外と XML でデータを公開しているケースは多い。  XML形式で公開されているオープンデータを扱う際に **XMLデータベース** の一つである [BaseX](http://basex.org) を活用してみた経験についてざっと書いてみる。 

公開されている実世界のオープンデータを、事前知識なしで扱うのはなかなか大変だ。ドキュメントが整備されていなかったり、不揃いであったりして、ざっとデータの傾向を見てみないと活用しづらいケースが多々ある。 しかし、

* 帯域幅の制限もあって一回のアクセスでは十分な量のデータを得られない
* API があまり作り込まれていないため、欲しい情報をピンポイントで得るための検索ができない

などというのは日常茶飯事だ。 そこで試しにちょっとしたスクリプトを書いて、まとまった量のデータをマイルドな周期でダウンロードすることになるけれど、 書き捨てのスクリプト一々書くのは億劫だ。 期待通りのデータがちゃんと取得できていなければやりなおし、試行錯誤を繰り返すことにもなる。 XML を ロードして XPath で解析して、RDBMS に格納して…を繰り返していると、 こんなしょうもないコードを書いている暇はない！となる。

そこで偶々別の仕事をしていて思いついたのは、 APIのエンドポイントから取得して必要な形式に変換する前の、テンポラリーな XMLの格納場所として XMLデータベース ([BaseX](http://basex.org)) を使うのはどうか、ということだ。 実際 BaseX を少し触ってみると、 XQuery が案外リッチで**書いていて楽しかった**り、**同時に大量の XML ファイルを解析できて楽**だ。 また JDBC を使って MySQL とかに接続できるので次のフェーズとの相互運用性も悪くなかったりと、案外良いんじゃないか？ という気になってきた。 幸い、 BaseX の開発はいまのところ活発で、ここ2年でも色々と改善されているようだ。

そういうわけで、今回は BaseX のさわりをざっくり紹介する。

起動
====

http://basex.org/products/download/ から BaseX831.zip をダウンロードして展開後、

```sh
./bin/basex
```

で、スタンドアロンモードで起動できる（サーバーモードや GUIモードもあるが割愛）。 DBを操作する様々な [コマンド](http://docs.basex.org/wiki/Commands) が用意されている。 ``QUIT`` か ^D で抜けられるほか、 readline っぽいキーバインドの多くが使えるようになっている。

XQuery
------

* XQuery は 多くの部分を XPath と共有している。
* BaseX は [XQuery 3.0](http://www.w3.org/TR/xquery-30/) を実装している。
* XQuery を書くには XQUERY コマンドを使う。CUIだと改行はできず、ワンライナーになる。
* ワンライナーがきつい場合、 RUNコマンドで XQuery のスクリプトを指定したり、 basexコマンドの引数に与えたりすれば複数行にできる。

XQuery は関数型言語のようなものなので、DBにデータが無くとも何か書くことができる。 XQuery には文字列、数値、XML、列、マップ、第一級の関数など、プログラミング言語にありそうなリテラルは豊富にある。

```
XQUERY "Hello," || "World"
```

```
Hello, World
```

XQuery は XMLリテラルを持つので、XMLのタグ自体も正しい XQuery の式だ。

```
XQUERY <hello/>
```

XQuery で重要かつ厄介なのは 列式 (sequence expresssion) だ。 ``1,2,3`` のようにカンマで区切り、カッコは必須ではない。 他言語の配列と良く似ているが、ネストはできない。 例えば ``1,2,(3,4),5`` は ``1,2,3,4,5`` と等価だ。 ``()`` は空の列で、 ``('a', (), 'b', 'c')`` は ``('a', 'b', 'c')`` と等価だ。 ``n to m`` で数列も生成できる。

```
XQUERY (1 to 10)
```

```
1
2
...
10
```


列は、**FLWOR 式** でイテレートできる。 FLWOR 式とは ``for``, ``let``, ``where``, ``order by``, ``return`` からなる式のことで、 XQuery を特徴づけるものだ。

```xquery
XQUERY for $i in (1,2,3, (4, 5), 6) where $i<4 return $i
```
などと書けば、

```
1
2
3
```
が返ってくる。 まあ、list comprehension の一種といえばそうかもしれない。

``return`` 以外は必須ではない。 たとえば let 文で変数に式を束縛して何かを計算、というようなこともできる。 

```xquery
XQUERY let $elm := <root> <tag1>hello</tag1>, <tag2>world</tag2> </root> return $elm/tag1
```

```
<tag1>hello</tag1>
```


XQuery は XPath を包含しており、 XPath 式の結果を FLWOR でイテレートできる。 ここで、``html:parse`` や ``fetch:text`` は　BaseX の豊富な [XQuery Module](http://docs.basex.org/wiki/XQuery_Module) の一部だ。

```xquery
XQUERY let $html := html:parse(fetch:text('http://qiita.com/keigoi')) for $a in $html//a return $a/@href
```

```
href="/keigoi"
href="https://github.com/keigoi"
href="https://twitter.com/keigoi"
...
```

実のところ、これがもっとも XQuery らしい使い方だと思う。

``for`` や ``let`` は、束縛をカンマで区切ると連続して書ける。

```xquery
XQUERY for $i in (1,2), $j in (4,5) return $i+$j
```

```
5
6
6
7
```


関数は第一級である（``function`` と書くのは面倒だけど）。

```xquery
let $succ := function($x) { $x+1 } return (for $i in (10,20,30) return $succ($i))
```

```
11
21
31
```


データベースの構造とドキュメントの追加
-----------------------------------

```
CREATE DB testdb
```

で、testdb という名前の DB が作成される。 

BaseX の データはフラットなファイルシステムのようなもので、ディレクトリやファイルモードといった概念はない。 ここに、大量の XML *ドキュメント* を置いていくことになる。 


* ドキュメントを追加するには、[ADD コマンド](http://docs.basex.org/wiki/Commands#ADD) か、 XQuery の [db:add 関数](http://docs.basex.org/wiki/Database_Module#db:add) を使う。どちらも BaseX 固有のものだ。


```
XQUERY db:add('testdb', <hello>こんにちは世界</hello>, 'hello.xml')
```

とすれば、 test データベースに hello.xml を追加できる。

ドキュメントの一覧は LIST コマンドで確認できる。

```
> LIST testdb
Input Path  Type  Content-Type     Size  
---------------------------------------
hello.xml   xml   application/xml  3     

Resources.
>
```

ドキュメントの取得
----------------

```xquery
XQUERY doc('testdb/hello.xml')/hello
```

```
<hello>こんにちは世界</hello>
```

複数のドキュメントの一括取得
--------------------------

```xquery
XQUERY collection('testdb/path1/path2')
```

path1/path2 以下のドキュメントを一度に取得できる。
実のところ、この機能のおかげで大量のXMLドキュメントを解析できるといっても過言ではない。BaseX の構造自体はフラットだが、このようにリソースのパス毎にまとめて扱えるので、パスの工夫次第で色々と工夫できる。


あとがき
=======
やや中途半端だがこの記事はこれで終わり（何度か追記しているが）。 気が向けば続きを書こうと思う。 
あとは雑多なことを書いておく。

なぜこれを書いた
--------------

某所で、ひょんなことから バックエンドにXMLデータベースを使っている Webアプリを垣間みる機会があった。 最初は eXist だったが 今は BaseX を使っている。 どこかの業者が作ったようだけど、 XQuery をふんだんに使うというよりも、 XSLT を沢山使って HTML に変換していく、という悪夢のような構造をしていた。 

おお、**XMLデータベース**...

* XML はデータ授受のためのフォーマットであり、DBで管理するなんて馬鹿げている…
* 昔、 Webアプリの全てを XML と XSLT で書けば、RDBとのインピーダンスミスマッチがなくてサイコー！という人々は居たけれども、XSLT で処理を書くなんて悪夢以外の何物でもないし、やってられない
* そもそも XML なんて冗長きわまりないし、Javaとか流行ってた頃の全然イケてないレガシーな技術。 クールな tech guy なら RDBMS や NoSQL が基本だし、 フォーマットといえば JSON とか YAML でしょ

などと思ったものだった。

気に入ったこと、厄介なこと
------------------------
しかし今回 XQuery を触ってみて楽しかったのと、 XQuery が XPath 書き放題なスクリプト言語として使えるのとで、気に入りつつある。また、 HTTPサーバーに接続して HTMLをパースして…ということも可能なので、オープンデータの取得から MySQL への格納まで全てを BaseX で賄うのも可能…のようにも思えてくる。 Python に飽きてきた私には朗報だ！

ただ、そうは問屋が下ろさないのがこのネタのネタたる所以だ。やはり DBなので、1時間くらいかけて多くのXMLを取得するようなクエリが途中で失敗すると ACID 特性のせいで 途中まで取得したXMLがすべて消え失せたるといったことが起こったし、長時間かかるクエリの間ずっと DB全体がロックされ続けるという問題もある（これは BaseX が成熟していないせいか）。

BaseX には[多くのモジュール](http://docs.basex.org/wiki/Module_Library)があり、とても単なるDBクエリのためだけとは思えない側面がある。 ユニットテストとか。 掘り下げると面白そうな処理系なのだが、英語の壁なのか、あまり使ってる人が目に入ってこない。 日本語で XQUery 3.0 について書かれた記事は皆無だ。 XML 関連技術のネガティブイメージも大きいかもしれない。 しかし少なくとも XQuery はなかなか面白くて使える技術なので、もっと触る人が増えると良いと思う。

その他のトピック
--------------

* JOIN 演算のパフォーマンス
* RDBMS との接続
* パーミッション
* 関数宣言
* if式
* 名前空間
* 例外処理
* 副作用のマーキング
* ユーザー定義のモジュール

(以上)