XQueryの特徴
以前、データベースプログラマである同僚の一人が、初期のXQueryの実装をしばらく使ってみて、この言語をニコニコ言語と評しました。それは言語のわかりやすさと関係するのかと訊いたところ、彼はハンガリー訛りの英語でこう答えました。「まさか、言語自体は難物だよ。だけど、ニコニコマーク (: :) がコメントの区切り記号になっているから、たとえデータベースがずたずたでもニコニコしてしまうわけさ」。
彼の意見はこの言語をあからさまに非難するものではありませんが、XQueryについてのある事実、つまりXQueryの構造は一般の言語に似ているが、その違いの部分で大きくつまずく場合があるということをよく示しています。XQueryは習得の困難な言語ではありませんが、どうしてうまく動かないか知ろうという気にさせる言語でもあります。
XQueryの制御構造
XQueryの制御構造はFLOWRという風変わりな頭字語で呼ばれてきました。これは、その言語で使われる特に重要なXQuery構造(すべてではない)の略称で、FLOWR自体は次の5つの操作を表します。
- For
- Let
- Order by
- Where
- Return
このうちの4つについては、SQLに類似の構造があります。
- SELECT
- SET
- ORDER BY
- WHERE
これらの用語は集合に項目を割り当てる、または集合から項目を取り出すために使われます。
XQueryの使いどころ
XQueryは''集合操作''言語です。単一のスカラー値よりも情報の集合を操作することに主眼が置かれます。また、集合操作言語という点で、その仕様に組み込まれているXPath 2.0言語を置き換えるよりも、それを拡張することを目指しています。実際、突き詰めていくと、XQueryの大部分はXPathの制御言語をラップする仕組みの1つに過ぎず、そのやり方はXSLTがXPathにテンプレート言語を結び付けるのとどこか似ています。
こうした理由により、XQueryを操作するとき、この言語を最も効果的に使うには、最初にXPath 2内で可能な限り多くのことを行い、XPath 2ではもう立ち行かなくなったところでXQueryのコマンド構造を利用するという方法をとるべきです。
たとえば、従業員レコードのコレクションで構成されるXMLデータソースがあると仮定します。現在、それらはファイル「employees.xml」に入っています(リスト1を参照)。
<?xml version="1.0" encoding="UTF-8"?> <employees> <employee id="be129"> <firstname>Jane</firstname> <lastname>Doe</lastname> <title>Engineer</title> <division>Materials</division> <building>327</building> <room>19</room> <supervisor>be131</supervisor> </employee> <employee id="be130"> <firstname>William</firstname> <lastname>Defoe</lastname> <title>Accountant</title> <division>Accts Payable</division> <building>326</building> <room>14a</room> </employee> <employee id="be131"> <firstname>Jack</firstname> <lastname>Dee</lastname> <title>Engineering Manager</title> <division>Materials</division> <building>327</building> <room>21</room> </employee> <employee id="be132"> <firstname>Sandra</firstname> <lastname>Rogers</lastname> <title>Engineering</title> <division>Materials</division> <building>327</building> <room>22</room> </employee> <employee id="be133"> <firstname>Steve</firstname> <lastname>Casey</lastname> <title>Engineering</title> <division>Materials</division> <building>327</building> <room>24</room> </employee> <employee id="be135"> <firstname>Michelle</firstname> <lastname>Michaels</lastname> <title>COO</title> <division>Management</division> <building>216</building> <room>264</room> </employee> </employees>
このとき、各従業員に順にコンテキストを設定するにはfor文を使用します。次のコードは、リスト中の順番で各従業員を出力する単純なXQueryスクリプトです。
for $employee in doc("employees.xml")/employees/employee return $employee[/s]
この例では、従業員要素のシーケンスが、対応する各従業員の本体と共に返されます。もちろん、このシーケンスが正確にどうレンダリングされるかは、使用するXQueryの実装によって異なります。たとえば、Saxon 9のXQueryエンジンを使用した場合、出力はリスト2のようになります。
result:sequence xmlns:result="http://saxon.sf.net/xquery-results" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <result:element> <employee id="be129"> <firstname>Jane</firstname> <lastname>Doe</lastname> <title>Engineer</title> <division>Materials</division> <building>327</building> <room>19</room> <supervisor>be131</supervisor> </employee> </result:element> <result:element> <employee id="be130"> <firstname>William</firstname> <lastname>Defoe</lastname> <title>Accountant</title> <division>Accts Payable</division> <building>326</building> <room>14a</room> </employee> </result:element> </result:element> <!-- more results --> </result:sequence>
一方、eXist XQueryエンジンを使用した場合は、コンテナやクロージャを持たない要素ノードのシーケンスが返されます。その主な理由は、Saxonでは出力がXMLオブジェクトになるものと仮定されるのに対し(そのために何らかのコンテナが必要となる)、XQueryではそのような仮定がないことです。クエリ全体をXMLコンテナに入れれば、このラッパー問題を回避できます。
<employee_set>{ for $employee in doc("employees.xml")/employees/employee return $employee} </employee_set>
Saxonの場合、この出力はよく似ていますが、完全に同じにはなりません(リスト3を参照)。
<result:sequence xmlns:result="http://saxon.sf.net/xquery-results" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <result:element> <employee_set> <employee id="be129"> <firstname>Jane</firstname> <lastname>Doe</lastname> <title>Engineer</title> <division>Materials</division> <building>327</building> <room>19</room> <supervisor>be131</supervisor> </employee> <employee id="be130"> <firstname>William</firstname> <lastname>Defoe</lastname> <title>Accountant</title> <division>Accts Payable</division> <building>326</building> <room>14a</room> </employee> <!-- more employees --> </employee_set> </result:element> </result:sequence>
for $item in $seq
という式は、やや誤解を招く可能性があります。基本的にfor
文はシーケンス内を反復処理するので、$item
変数にはシーケンス内の各項目への内部ポインタが順に渡され、その$item
のコピーが渡されることはありません。つまり、$item
コンテキスト変数は、基本となるXML(または関連する)データモデル内の構造を参照していることと、その結果がこのコンテキストに基づくシーケンスになるという点において、「ライブ(live)」であると言えます。
たとえば次の式では、
for $employee in doc("employees.xml")/employees/employee order by $employee/lastname ascending return $employee
従業員のリストが従業員の姓の順に返されます。要するに、order by
文はリストを指定の条件で並べ替えた仮想的なシーケンスを作ります。
for $employee in doc("employees.xml")/employees/employee order by $employee/lastname ascending return $employee
上記のうち、太字部分の式が仮想的なシーケンスを表します。