mb_language("uni"); mb_internal_encoding("utf-8"); mb_http_input("auto"); mb_http_output("utf-8"); ?>
このチュートリアルでは、最初のチュートリアルで学習したクエリーの基本的なパスと式を発展させます。このチュートリアルでは、結果シーケンスの順序を並べ替える方法、新しいXMLエレメントと属性を構築する方法(さらに、その中にデータベースからXMLフラグメントを埋め込む方法)を示します。また、(For、Let、Where、Returnを表す)FLWRステートメントを使って、変数に値を割り当て、シーケンスを反復処理し、条件に従って結果に含める、または結果から除外する方法についても説明します。
このチュートリアルでは、4文書からなる野球データベースをサンプルとして使用します(ここからダウンロードできます)。
通常、NeoCoreのクエリーの結果は、定義されない順序で戻されます。結果シーケンスを特定の順序で並べ替えたい場合には、以下のような「sortby」キーワードを使用します。
コード |
string(/ND/player/@name) sortby(. descending) |
これは4人のプレーヤーからなるシーケンスを対象とし、プレーヤーの「@name」属性を抽出して、それぞれをストリングに変換します。次に、(4つのストリング値からなる)結果シーケンスを降順でソートし、以下の結果を出力します。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> Willie Mays Hank Aaron Barry Bonds Babe Ruth </Query-Results> |
複数のキーを指定してソートすることができます。各キーごとに、昇順(既定)または降順でソートできます。指定される最初のキーは、それ以降のすべてのキーよりも優先されます。シーケンス内の要素が、最初のキーに関して同じ値を持つ場合、2番目以降のキーに基づいて序列化します。
コード |
/ND/player sortby(bats descending, @name) |
コード |
<?xml version="1.0" encoding="UTF-8" ?> <Query-Results> <player name="Hank Aaron"> <bats>R</bats> ... </player> <player name="Willie Mays"> <bats>R</bats> ... </player> <player name="Babe Ruth"> <bats>L</bats> ... </player> <player name="Barry Bonds"> <bats>L</bats> ... </player> </Query-Results> |
この例では、プレーヤーの記録が、まず「bats」(打席)フィールドに応じてアルファベット降順でソートされます。その結果、R(右打席)がL(左打席)よりも先に来ます。次に、同じ「bats」値を持つ副次シーケンスが、「name」属性に従って昇順でソートされます。
ソート対象の各項目ごとに、各ソートキーが単一の値を評価する必要があります。 (たとえば明示的な変換関数の結果として)ソートキー値の型が明らかでない場合には、項目のストリング値が使用されます。それぞれのキー位置ごとに、キーの型がすべて適合しなければなりません。たとえば、最初のキーがすべて整数で、2番目のキーがすべてストリングである場合、どちらのキーも、整数とストリングの混合であってはなりません。
式の結果をソートキーにすることもできます。これによって、多様な組み合わせが可能になります。以下のクエリーは、プレーヤーの現役通算ホームラン数によってソートした後、それぞれの名前のストリング値をシーケンスとして抽出します。
コード |
string((/ND/player sortby(sum(integer(batting/homers)) descending))/@name) |
結果は次のようになります。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> Hank Aaron Babe Ruth Willie Mays Barry Bonds </Query-Results> |
以下のクエリーは、打者としてプレーした年数に応じて降順でプレーヤーをソートした後、ピッチャーとしてプレーした年数でソートし、さらに名前でソートします。
コード |
string((/ND/player sortby(count(batting) descending, count(pitching) descending, @name ascending))/@name) |
クエリーの結果は次のようになります。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> Hank Aaron Willie Mays Babe Ruth Barry Bonds </Query-Results> |
以下のクエリーは、プレーヤーが50本を超えるホームランを打った年を、ホームラン数に応じて(降順で)ソートした後、年でソートします。
コード |
/ND/player/batting[homers > 50] sortby(integer(homers) descending, @year) |
結果は以下のリストのようになります。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <batting year="2001" team="SF" league="NL"> ... <homers>73</homers> ... </batting> <batting year="1927" team="NY" league="AL"> ... <homers>60</homers> ... </batting> ... etc. </Query-Results> |
XMLコンストラクタ
データベースに対してクエリーを実行するとき、データのフォーマットを出力用に再構築したいことがよくあります。これを行うには、XMLコンストラクタを使用します。 XMLコンストラクタは、単純なXML文書のように見える式です(エレメントと属性だけが存在し、名前空間とXML宣言は使用できません)。さらに、これらのエレメントまたは属性の中に、中括弧{}で囲んだ式を含めることができます。結果の出力が作成されるとき、このような式は評価されて、XMLフラグメントに埋め込まれます。以下の一連のコンストラクタは、どれも同じ結果を作成します。
コード |
<message>Hello, world</message>
<message>{"Hello, world"}</message> <message>Hello, {"world"}</message> <message>{("world", "Hello, ") sortby(.)}</message> |
出力は、以下のようになります。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <message>Hello, world</message> </Query-Results> |
また、XMLコンストラクタに属性を含めることもできます。
コード |
<career-total homers={sum(/ND/player[@name="Babe Ruth"]/batting/homers)}/> |
子を持たないこのコンストラクタによって、以下が戻されます。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <career-total homers="714"/> </Query-Results> |
XMLコンストラクタをFLWRステートメントおよびソートと併用すると、とくに便利です。 これらの機能を合わせて利用すれば、XMLフラグメントからなるシーケンス全体を構築し、計算された複数の値をそれぞれに埋め込んで、 (オプションで)出力をソートすることができます。 この組み合わせによって、ほとんどどんな操作も可能です!
FLWRステートメント(For、Let、Where、Return)
FLWRステートメントを使用すれば、以下が可能です。
● シーケンスを反復処理して、変数に個々の値を割り当てる(for)
● 式の結果を変数に割り当てる(let)
● シーケンス内のいくつかの値を選んで、それらを無視する(where)
さらに
● これらの変数を使って、式の出力を計算する(return)
1つのFLWRステートメントは、1つまたは複数の「for」または「let」文節、オプションの「where」文節、および1つの「return」文節からなります。 たとえば、次のようになります。
コード |
for $x in /ND/player[@name="Babe Ruth"]/batting[@year
>= 1933]/homers return <h>{integer($x)}</h> |
これによって、選択されたそれぞれのホームラン数の値ごとに、1つのXMLエレメント「h」が戻されます。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <h>34</h> <h>22</h> <h>6</h> </Query-Results> |
「for」文節は、以下のように、「for」というワード、1つの変数名、「in」というワード、および1つの式からなります。
コード |
for $pl in /ND/player |
「let」文節は、以下のように、「let」というワード、1つの変数名、「:=」という記号、および1つの式からなります。
コード |
let $x := sum(/ND/player/batting/homers) |
「for」文節は、式によって作成されるシーケンス内のそれぞれの値を順番に(一定の順序で)変数に割り当て、そのような値ごとに、FLWR式の残りの部分を一度ずつ評価します。 これに対して、「let」は、式によって作成されるシーケンス全体を変数に割り当て、FLWRの残りの部分を一度だけ評価します。
FLWRステートメントで使用される変数は、ドル記号で始まる名前です(XMLエレメント名と同様に、文字、数字、ダッシュを名前に含めることができます)。 変数名の有効範囲は、FLWRステートメントの残りの部分、つまり「return」式の終わりまでです。 言い換えると、1つの「for」または「let」文節で割り当てられた変数を、後続の「for」または「let」文節や、「where」または「return」文節の中で使用することができます。 同じFLWRステートメント内で同一の変数名が2度以上出現した場合には、ステートメントの残りの部分において、より内側の割り当てがより外側の割り当てに置き換わります。
「where」文節は述部のように機能し、先行する「for」および「let」文節によって割り当てられた変数に適用されます。 「where」内の式が真と評価される場合、「return」が実行されます。そうでなければ、それらの変数値に関しては「return」がスキップされます。 たとえば、以下の例は、50ホームランを超える「batting」エレメントを集めた1つのエレメントを構築し、構築された「year」エレメントのストリング値に従ってソートします。
コード |
for $y in /ND/player/batting where $y/homers > 50 return <year>{string($y/@year)}</year> sortby(.) |
この例の重要なポイントは、「sortby」演算子がFLWR式よりも緩やかにバインドしていることです。そのため、最終的な「sortby(.)」は「for」の結果シーケンス全体に適用されます。 結果はこうなります。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <year>1920</year> <year>1921</year> <year>1927</year> <year>1928</year> <year>1955</year> <year>1965</year> <year>2001</year> </Query-Results> |
この場合、「where」文節は以下の述部と厳密に同じ働きをします。
コード |
for $y in /ND/player/batting[homers >
50] return <year>{string($y/@year)}</year> sortby(.) |
ただし、述部で実行できない機能を「where」で実行できるケースがあります。 最も一般的なケースは、以下のように2つの文書の値を比較する機能であり、これはSQLの結合操作と同等です。
コード |
for $p1 in /ND/player for $p2 in /ND/player where sum($p1/batting/homers) > sum($p2/batting/homers) return <hey>{string($p1/@name)} hit more homers than {string($p2/@name)}!</hey> |
このクエリーによって、以下が戻されます。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <hey>Willie Mays hit more homers than Barry Bonds!</hey> <hey>Babe Ruth hit more homers than Barry Bonds!</hey> <hey>Babe Ruth hit more homers than Willie Mays!</hey> <hey>Hank Aaron hit more homers than Barry Bonds!</hey> <hey>Hank Aaron hit more homers than Willie Mays!</hey> <hey>Hank Aaron hit more homers than Babe Ruth!</hey> </Query-Results> |
FLWRステートメントをネストさせることもできますが、ほとんどの場合、同じステートメント内で「for」文節と「let」文節を組み合わせ、各文節の入力に対して中間的にソートを実行すれば、同じ結果を達成できます。 最後の例として、以下のクエリーは一連のプレーヤーを反復処理し、各プレーヤーの打撃成績が最も良かった年を抽出して、それらの年の成績を表す数値を含むエレメントを作成します。(なお、「two-bagger」は二塁打、「dinger」は一発(ホームラン)、「extra-base」は長打です。)
コード |
for $hitter in /ND/player sortby (sum(batting/homers) descending) for $season in $hitter/batting[hits >= 200 or homers >= 50] sortby(@year) let $extra-bases-on-long-hits := $season/doubles + 2*$season/triples + 3*$season/homers where $extra-bases-on-long-hits > 100 return <good-year name={string($hitter/@name)} year={string($season/@year)}> <hits>{integer($season/hits)}</hits> <two-baggers>{integer($season/doubles)}</two-baggers> <three-baggers>{integer($season/triples)}</three-baggers> <dingers>{integer($season/homers)}</dingers> <extra-bases>{$extra-bases-on-long-hits}</extra-bases> {$season/stolen-bases} </good-year> sortby (extra-bases descending, hits descending, @name) |
このプログラム(もはや簡単なクエリーとは呼べません!)の結果は、次のようになります。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <good-year name="Barry Bonds" year="2001"> <hits>156</hits> <two-baggers>32</two-baggers> <three-baggers>2</three-baggers> <dingers>73</dingers> <extra-bases>255</extra-bases> <stolen-bases>13</stolen-bases> </good-year> <good-year name="Babe Ruth" year="1921"> <hits>204</hits> <two-baggers>44</two-baggers> <three-baggers>16</three-baggers> <dingers>59</dingers> <extra-bases>253</extra-bases> <stolen-bases>17</stolen-bases> </good-year> <good-year name="Babe Ruth" year="1927"> <hits>192</hits> <two-baggers>29</two-baggers> <three-baggers>8</three-baggers> <dingers>60</dingers> <extra-bases>225</extra-bases> <stolen-bases>7</stolen-bases> </good-year> <good-year name="Babe Ruth" year="1920"> <hits>172</hits> <two-baggers>36</two-baggers> <three-baggers>9</three-baggers> <dingers>54</dingers> <extra-bases>216</extra-bases> <stolen-bases>14</stolen-bases> </good-year> <good-year name="Babe Ruth" year="1928"> <hits>173</hits> <two-baggers>29</two-baggers> <three-baggers>8</three-baggers> <dingers>54</dingers> <extra-bases>207</extra-bases> <stolen-bases>4</stolen-bases> </good-year> <good-year name="Willie Mays" year="1955"> <hits>185</hits> <two-baggers>18</two-baggers> <three-baggers>13</three-baggers> <dingers>51</dingers> <extra-bases>197</extra-bases> <stolen-bases>24</stolen-bases> </good-year> <good-year name="Babe Ruth" year="1923"> <hits>205</hits> <two-baggers>45</two-baggers> <three-baggers>13</three-baggers> <dingers>41</dingers> <extra-bases>194</extra-bases> <stolen-bases>17</stolen-bases> </good-year> <good-year name="Babe Ruth" year="1924"> <hits>200</hits> <two-baggers>39</two-baggers> <three-baggers>7</three-baggers> <dingers>46</dingers> <extra-bases>191</extra-bases> <stolen-bases>9</stolen-bases> </good-year> <good-year name="Willie Mays" year="1965"> <hits>177</hits> <two-baggers>21</two-baggers> <three-baggers>3</three-baggers> <dingers>52</dingers> <extra-bases>183</extra-bases> <stolen-bases>9</stolen-bases> </good-year> <good-year name="Hank Aaron" year="1959"> <hits>223</hits> <two-baggers>46</two-baggers> <three-baggers>7</three-baggers> <dingers>39</dingers> <extra-bases>177</extra-bases> <stolen-bases>8</stolen-bases> </good-year> <good-year name="Hank Aaron" year="1963"> <hits>201</hits> <two-baggers>29</two-baggers> <three-baggers>4</three-baggers> <dingers>44</dingers> <extra-bases>169</extra-bases> <stolen-bases>31</stolen-bases> </good-year> <good-year name="Willie Mays" year="1958"> <hits>208</hits> <two-baggers>33</two-baggers> <three-baggers>11</three-baggers> <dingers>29</dingers> <extra-bases>142</extra-bases> <stolen-bases>31</stolen-bases> </good-year> <good-year name="Hank Aaron" year="1956"> <hits>200</hits> <two-baggers>34</two-baggers> <three-baggers>14</three-baggers> <dingers>26</dingers> <extra-bases>140</extra-bases> <stolen-bases>2</stolen-bases> </good-year> </Query-Results> |
この例には、注目すべき機能がたくさんあります。
● 構築されるタグは、元のデータと無関係であっても差し支えありません。 ここでは、「dingers」(一発)などのタグを独自に作成しました。
● 同じように、必要に応じて変数名を作成することができます。元の文書から派生させる必要はありません。
● 「let」を使用して、複雑な式を変数に割り当てることができます。こうすれば、クエリーの構造が明確になり、さまざまなコンテキストで同じ値を再使用できるようになります
● 「sortby」文節は、ソート対象のシーケンス内の項目から見た相対パスを使用します。 つまり、最後のソートにおける「stolen-bases」(盗塁)は、FLWRステートメントによって構築された各XML項目内の同じ名前のエレメントを参照します。
● (データベースの)XMLフラグメントへの参照をXMLコンストラクタに挿入すると、そのタグ構造も導入されます(「stolen-bases」を見てください)
● 構築されたXMLタグは元の内容の型を保持します(型が存在する場合)。したがって、「integer(extra-bases)」と指定する代わりに、「extra-bases」フィールドだけでソートを実行できます。 一方、データベースのXMLフラグメントの型はストリングと想定されるため、「stolen-bases」エレメントを数値として扱いたい場合には、明示的に変換する必要があります。
最後のヒントとして、すべての式が値のシーケンスを戻し、それを他の式で使用できることに注目してください。 つまり、上記の例に基づいて、次のようなクエリーを作成できます。
コード |
let $good-years := {...上記のクエリー...} return <total-hits>{sum($good-years/hits)}</total-hits> |
これによって、このクエリー全体が次のように凝縮されます。
コード |
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results> <total-hits>2496</total-hits> </Query-Results> |
このパワフルかつ柔軟な機能をぜひ最大限に利用して、あなたのクエリープログラミングの生産性を大いに高めてください!
▲このページのTOPへ