目次


XQuery によって XPath を拡張する方法

XPath では無理でも、XQuery では可能なこと

Comments

XPath と XQuery が実行する機能にはいくつか同じものもありますが、XPath は単純さをもたらし、XQuery は追加の機能と柔軟性をもたらすという点で異なります。さまざまな種類のクエリーにとって最適なツールとなるのは XPath で、例えば XML 文書の一部のレコードから電話番号の順不同のリストを作成するには、XPath を使うのが最も手軽な方法となります。その一方、クエリーで複雑なレコード選択基準を表現したり、結果セットの変換や再帰処理を行う場合には、XQuery が欠かせません。

XPath

XPath は DSL (Domain-Specific Language) の 1 つで、他の汎用言語の一部としてのその重要性が急速に高まっています。プログラミング言語では、モジュールやクラスを介して、あるいは言語の構文に直接 XPath を組み込むようになってきています。これは、かつて正規表現で起こった現象と同様です。

XPath の人気の理由は、開発者が XML 文書から特定のデータ部分を抽出するときの時間と作業をこの言語が大幅に削減するからです。XPath を今まで一度も使ったことがなかったとしても、その威力はすぐに利用することができます。例として、リスト 1 の XML フラグメントを見てください。

リスト 1. XML 文書
  <users>
    <user>
      <name>
        <first>Lola</first>
        <last>Solis</last>
      </name>
      <age>2</age>
    </user>
    <user>
      <name>
        <first>Nina</first>
        <last>Serafina</last>
      </name>
      <age>4</age>
      <visits>
        <first>2008-01-15</first>
        <last>2008-02-15</last>
      </visits>
    </user>
    <user>
      <name>
        <first>Tracy</first>
        <last>Keller</last>
      </name>
      <age>35</age>
    </user>
  </users>

例えばこの文書のなかから子供たちの名前 (ラストネーム) のリストを取得するには、以下の XPath 式を使用します。

リスト 2. 18 歳未満のユーザーの名前 (ラストネーム) を選択する方法
  /user[age lt 18]/name/last/text()

  (: Result
       Solis
       Serafina
  :)

このデータを抽出するためのコードを、XPath を使わずに作成したとしたらどうなるか想像してみてください。正規表現の助けを借りたとしても、visits ノードに含まれる last タグから値を排除する方法については多少の思案が必要になります。

上記の XPath 式は簡潔であるだけでなく、わかりやすさの点でも非常に優れています。XPath を知らない人でも、この式をひと目見れば内容が明らかにわかります。XPath がこれほど有用なのは、その強力さと、背景に長い歴史があることが理由となっています。この言語はどんなに複雑な XML 文書であっても、そこに含まれるノードと、さらに重要な、それらのノード間の関係を理解します。そのため、要素と要素の値だけでなく、属性、処理命令なども考慮した簡潔な表現を作成することができるというわけです。

XML クエリーの多くは、XPath 以外の方法で明確かつ簡潔に表現しようとしても簡単には行きません。しかしその一方、XPath が持つ DSL という特性、そしてこの言語の目的がプログラマーにかなり深刻な制限を課すことも確かです。以降のセクションでは、XQuery 言語について簡単に説明し、XPath 単独では解決できない問題を取り上げます。これらの問題に対処するには、プログラマーが XPath だけではなく、XQuery のようなツールにも手を広げる必要があります。

XQuery

XQuery は XPath を構文の一部としてネイティブにサポートするため、XQuery では当然、XPath で可能なことは何でもできます。しかし XQuery はチューリング完全の言語なので、汎用言語と見なすことができます。多少複雑になるという代償はありますが、XQuery を使えば XPath に伴う多くの制限事項を容易に克服することができます。

概要

XQuery が使用する単純な構文は、XML、XPath、コメント、関数、そしてこれらすべてを結び付ける特殊な式構文が組み合わせられたものです。XQuery コードは式のみで構成され、文はありません。すべての値はシーケンスであり、単純さが重要となるこの言語では、Hi2 * 2 という式はどちらも有効な XQuery コードで、事前の処理も変更することもなく、そのまま実行されます。XQuery は強い型付けの (副次効果が一切ない) 高級な関数型言語なので、XML 文書と大規模な XML 文書リポジトリーの両方からデータを取得するクエリーを表現するには理想的です。この最後の点においては SQL とよく似ていますが、XQuery ではさらに、結果セットのどのような変換をも表現することができます。XML 文書からデータを取得する場合に XPath を使用すると効果を発揮するのと同じく、大規模な XML 文書のリポジトリーからデータを取得して変換する場合には XQuery を使用するとかなりのメリットがあります。

結果セットの変換

XPath が持つ明らかな制約の 1 つは、結果セットの変換は一切行わないという点です。例えば、先ほどの XPath クエリー (リスト 2) の結果をリスト 3 のようにアルファベット順で返す必要があるとします。

リスト 3. アルファベット順の結果
Serafina
Solis

XPath ではアルファベット順で結果を返すことはできません。結果をこのようにして返すには、別の言語 (例えば XQuery など) でコードを作成するか、あるいは特殊な独自仕様の XPath 拡張を使って結果をソートしなければならないことになります。

その一方、XQuery では結果をソートすることも、HTML、CSV、SQL などのあらゆるテキスト・ベースのフォーマットに変換することもできます。XQuery がもっとも得意とする変換のタイプとして挙げられるのは、XML 同士の変換です。大規模な XML データベースには大抵の場合、クライアント・アプリケーションには必要のない、相互に関連する複雑な XML 文書がさまざまに含まれていますが、XQuery を使用すれば、クライアントはサーバーから返してもらいたい XML 文書のタイプを明確に記述することができます。XQuery インターフェースを提供することによって、サーバーがデータを複数のスキーマで維持しないようにできることも珍しくありません。その上、XQuery を使ってクライアント用にデータを変換するのは、Perl や Java、あるいはよく使われるその他のコンピューター言語でデータを変換するよりも遥かに簡単で、処理に要する時間も短縮されます。データの取得時に現行の実装で XQuery を使ってデータを変換するほうが、後で XSLT による変換を実行するよりも大幅な時間の短縮になることは確かです。

レコードの選択基準と結果の変換についての命令を結び付けるため、XQuery には FLWOR (「フラワー」と発音) 式という機能が用意されています。この頭字語が表しているのは、ForLetWhereOrder byReturn で、これらの要素を使って、FLWOR 式を構成することができます。FLOWR 式には、少なくともこれらの要素のいくつかが頭字語の順に含まれます。つまり、すべての FLOWR 式は for または let で始まり、return で終わります。SQL に慣れていれば、このことから私が何をしようとしているかはおわかりでしょう。以下の単純な FLOWR 式は、Edwin Markham の詩、「Outwitted」から引用したものです。

リスト 4. 単純な FLWOR 式
let $xml:= 
  <a>
    <one>She drew a circle that shut me out</one>
    <two>Heretic rebel, a thing to flout</two>
  </a>

return $xml//one/text()

(: Result
    "She drew a circle that shut me out"
:)

リスト 5 を見れば、リスト 1 の XML に単純な FLOWR 式を適用する方法がわかります (簡単のため、このリストでは実際の XML に含まれるテキストの代わりに _XML from Listing 1_ というテキストを使っています)。

リスト 5. 単純な FLWOR 式
let $xml:= _XML from Listing 1_
for $user in $xml//user[age lt 18]
order by $user/name/last
return $user/name/last/text()

(: Result
    Serafina
    Solis
:)

結果を番号付きリストとして表した HTML フラグメントを返すクエリーが必要な場合には、リスト 6 の XQuery を適用します。

リスト 6. HTML で番号付きリストを出力する単純な FLWOR 式
let $xml:= _XML from Listing 1_
return
  <ol>{
    for $user in $xml//user[age lt 18]
    order by $user/name/last
    return <li>{$user/name/last/text()}</li>
  }</ol>

(: Result
    <ol><li>Serafina</li><li>Solis</li></ol>
:)

このリスト 6 を見れば、XML と XQuery を使った FLWOR 式が極めて直観的かつ効果的であることがわかります。

複雑なレコード選択基準を表現する

元々、XQuery では取得したデータを変換できるだけでなく、データの検出にかけても断然 XPath に勝っています。XQuery と XPath は重複することがよくありますが、このことは、プログラマーがクエリーをより適切に表現する上で役に立っています。一例として、リスト 7 に XPath 式のフラグメント、age lt 18 を FLWOR 式の where 節に移す方法を示します。

リスト 7. XQuery で XPath の制約を表現する方法
let $xml:= _XML from Listing 1_
return
  <ol>{
    for $user in $xml//user
    where $user/age lt 18
    order by $user/name/last
    return <li>{$user/name/last/text()}</li>
  }</ol>

リスト 7 の式の結果は リスト 6 の結果とまったく同じですが、XQuery の where 節は XPath 構文よりも遥かに柔軟に制約を表現することができます。それは、XQuery の where 節はどんなに複雑にネストした式で構成することも可能で、その式には関数コールまで含めることができるからです。このように、レコード選択を表現する上で XQuery は何の制約も課すことはありません。

関数と再帰を使用する

XPath は関数をサポートしませんが、XQuery には多数の組み込み関数と演算子が揃っているだけでなく、ユーザーが独自の関数を定義することも可能です。XQuery 関数は、強い型付けがされ、再帰をサポートし、内部関数または外部関数として宣言することができます。内部関数とは、関数宣言の後に関数本体が続く標準関数のことです。一方の外部関数は関数宣言のタイプで、ユーザーが関数の本体をさまざまなプログラミング言語で定義して実装できるようにします。

再帰は、開発者の多くが行う日々のタスクには最適な手法ではないかもしれませんが、XML を操作するときにはしばしば役に立ちます。これは、XML には任意にネストされたノードが含まれることがあるためです。例えば、リスト 8 に定義した transform-names 関数を見てください。

リスト 8. すべての XML 文書でノード名を変更する単純な関数
(: Part 1 :)
define function transform-names($node as node()) as node() {
  element{replace(name($node), "_", "-")} {
    $node/text(), for $subnode in $node/* return transform-names($subnode)
  }
}

(: Part 2 :)
let $xml:=
<item>
  <item_type>book</item_type>
  <contributors>
    <author>
      <first_name>Charles</first_name>
      <last_name>Edward</last_name>
      <home_address>
        <home_street>206 S. Solomon St.</home_street>
        <home_city>New Orleans</home_city>
        <home_state>LA</home_state>
        <home_zip>70119</home_zip>
      </home_address>
    </author>
    <artist>
      <last_name>Salinas</last_name>
    </artist>
  </contributors>
</item>
return transform-names($xml)

(: Result
    <item>
      <item-type>book</item-type>
      <contributors>
        <author>
          <first-name>Charles</first-name>
          <last-name>Edward</last-name>
          <home-address>
            <home-street>206 S. Solomon St.</home-street>
            <home-city>New Orleans</home-city>
            <home-state>LA</home-state>
            <home-zip>70119</home-zip>
          </home-address>
        </author>
        <artist>
          <last-name>Salinas</last-name>
        </artist>
      </contributors>
    </item>
:)

transform-names 関数は、リスト 8 の Part 1 に示されている単純なコードですが、この関数はどんなに複雑な XML 文書またはノードでも許容します。この関数は、すべての XML タグ名のアンダーバー (_) をダッシュ (-) に置き換えます。

この場合の再帰は、関数が簡単に文書の構造をトラバースできるようにします。その結果、関数は簡潔 (わずか 3 行) で保守しやすいものになり、属性を使用しない妥当な XML 文書やノードを処理することになります。最初はこの関数を完全に理解するのは難しそうに思えても (特に、再帰を用いることがめったにないプログラマーにとっては)、そのうちすぐに、関数を変更してアンダーバーをダッシュで置き換える代わりに削除する方法の見当がつくようになるはずです。

結合を表現する

XPath には、クエリー内で XML ノードを結合する手段はありません。しかし、SQL では自然な構文を使ってクエリー内でテーブルの結合を表現できるのと同様に、XQuery では XML ノードのセットを (少なくとも SQL ユーザーにとっては) 直観的に結合できるようになっています。リスト 9 のコードに、XQuery でどのようにして結合が行われるかを示します。

リスト 9. XQuery での結合の表現
(: Part 1 :)
let $authors:= 
  <authors>
    <author>
      <name>Harold Abelson</name>
      <books>
        <isbn>978-0-07-000422-1</isbn>
        <isbn>978-0-262-01063-4</isbn>
      </books>
    </author>
    <author>
      <name>Paul Graham</name>
      <books>
        <isbn>978-0-13-370875-2</isbn>
        <isbn>978-0-13-030552-7</isbn>
        <isbn>978-0-596-00662-4</isbn>
      </books>
    </author>
    <author>
      <name>Apostolos-Paul Refenes</name>
      <books>
        <isbn>978-0-471-94364-8</isbn>
        <isbn>978-981-02-2819-4</isbn>
      </books>
    </author>
  </authors>

(: Part 2 :)
let $books:= 
  <books>
    <book>
      <title>Structure and Interpretation of Computer Programs</title>
      <isbn>978-0-07-000422-1</isbn>
    </book>
    <book>
      <title>Turtle Geometry</title>
      <isbn>978-0-262-01063-4</isbn>
    </book>
    <book>
      <title>ANSI Common LISP</title>
      <isbn>978-0-13-370875-2</isbn>
    </book>
    <book>
      <title>On LISP</title>
      <isbn>978-0-13-030552-7</isbn>
    </book>
    <book>
      <title>Hackers and Painters</title>
      <isbn>978-0-596-00662-4</isbn>
    </book>
    <book>
      <title>Neural Networks in the Capital Markets</title>
      <isbn>978-0-471-94364-8</isbn>
    </book>
    <book>
      <title>Neural Networks in Financial Engineering</title>
      <isbn>978-981-02-2819-4</isbn>
    </book>
    <book>
      <title>Handbook of Artificial Intelligence</title>
      <isbn>978-0-201-16889-1</isbn>
    </book>
    <book>
      <title>Artificial Intelligence Programming</title>
      <isbn>978-0-89859-609-0</isbn>
    </book>
    <book>
      <title>A New Guide to Artificial Intelligence</title>
      <isbn>978-0-89391-607-7</isbn>
    </book>
    <book>
      <title>Artificial Intelligence</title>
      <isbn>978-0-08-034112-5</isbn>
    </book>
    <book>
      <title>Artificial Intelligence</title>
      <isbn>978-0-631-18385-3</isbn>
    </book>
  </books>

(: Part 3 :)
return
  <books-complete-info>{
    for $book in $books/*
      for $author in $authors/*
        where $book/isbn = $author//isbn
          and (
            contains($book/title, "LISP")
            or contains($book/title, "Neural"))
    order by $book/title      
    return <book>{$book/*, $author/name}</book>
  }</books-complete-info>

リスト 9 の Part 1 と Part 2 では、XML 文書をそれぞれ authors 変数と books 変数に割り当てています。books に含まれるノードの一部は authors に含まれるノードと関係します。具体的には、book ノードの ISBN は、author ノードに記載されている ISBN のうちの 1 つであるということです。

XQuery での結合の表現はリストの Part 3 に記載されています。この式によって組み立てられる新規 XML 文書、books-complete-info (リスト 10 を参照) には、著者の名前が組み込まれた book ノードが含まれます。

リスト 9 の Part 3 に記載したコードには、いくつか注目すべき点があります。まず、コードの最初のほうにある 2 つの for によって、このコードは結合を表現するであろうことを XQuery に示唆しています。where 節は、SQL で結合を実現するために作成する内容と概念的には似ていますが、author ノードには複数の ISBN を持たせることができます。この場合、where 節では実質的に「book の ISBN は author の ISBN に含まれる」ことを意味しなければなりません。これは、SQL の where 節に含まれる subselect と近いものがありますが、XQuery 構文のほうがより直観的で自然です。しかも、XQuery 式のほうが簡潔であることは明らかです。

リスト 10. XQuery での結合の表現による結果
<books-complete-info>
  <book>
    <title>ANSI Common LISP</title>
    <isbn>978-0-13-370875-2</isbn>
    <name>Paul Graham</name>
  </book>
  <book>
    <title>On LISP</title>
    <isbn>978-0-13-030552-7</isbn>
    <name>Paul Graham</name>
  </book>
  <book>
    <title>Neural Networks in the Capital Markets</title>
    <isbn>978-0-471-94364-8</isbn>
    <name>Apostolos-Paul Refenes</name>
  </book>
  <book>
    <title>Neural Networks in Financial Engineering</title>
    <isbn>978-981-02-2819-4</isbn>
    <name>Apostolos-Paul Refenes</name>
  </book>
</books-complete-info>

まとめ

XML 文書やリポジトリーの奥深くに埋め込まれたデータの一部を取得する手段としては、成熟した DSL である XPath が第一候補となるはずです。しかし、XPath はさまざまな問題に対処するようには設計されていません。この記事で説明したように、XPath を大々的に拡張する XQuery は、データ選択の要件が複雑な場合や、結果をソートして特にフォーマットに従った形にして返さなければならない場合、あるいは変換した結果を返す必要がある場合に最適なツールとして頭角を現してきています。


ダウンロード可能なリソース


関連トピック

  • XQuery 1.0 仕様に関する W3C 勧告: さまざまなタイプの XML データ・ソースに適用するように設計されたこの問い合わせ言語の詳細を読んでください。
  • XQuery 1.0 および XPath 2.0 の関数と演算子に関する W3C 勧告: このカタログで、XPath 2.0、XML Query 1.0、および XSLT 2.0 に必要な関数と演算子を調べてください。
  • W3C XML Query Use Cases: XQueryの使用例が紹介されています。
  • What is XQuery」(Per Bothner 著、O'Reilly xml.com、2002年10月): XQuery を全体的にとらえたこの概論では、この言語に本格的に取り組んだり、あるいは実際に使用してみる前に理解しておかなければならない主要な概念を説明しています。
  • XQuery入門」(Howard Katz 著、developerWorks、2006年1月): XQuery の背景にある歴史、文書化までのロード・マップ、そして現在の XQuery 仕様についての寸評を読んでください。
  • XQueryの神話と誤解を暴く」(Frank Cohen 著、developerWorks、2005年5月): XQuery にまつわる数多くの俗説や誤解を詳しく解明しています。
  • XML Schema Part 2: Datatypes Second Edition: XML スキーマ言語仕様を熟読してください。この仕様では、XML スキーマやその他の XML 仕様で使用するデータ型を定義するための機能を規定しています。
  • XPath に関する勧告: XPath についての詳細を読んでください。XML 文書に部分的に対処するためのこの言語は、XSLT と XPointer の両方で使用できるように設計されています。
  • XML Path Language (XPath) 2.0: XPath の今後の展開を参照してください。
  • チューリング・テスト: ウィキペディアで、ある機械が知的かどうかを判断するために考案されたテストについて読んでください。
  • IBM XML 認証: XML や関連技術の IBM 認定開発者になる方法について調べてください。
  • XML technical library: 広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM レッドブックについては、developerWorks XML ゾーンを参照してください。
  • developerWorks XML ゾーン: XML のすべてについて学んでください。
  • pureXML™ を使用した DB2 9: DB2 9 をダウンロードして、XML をリレーショナル・データと統合する pureXML の機能について理解を深めてください。アプリケーション開発の迅速化につながるはずです。
  • DB2 Express-C 9.5: このチュートリアルに使用した XML データベースをダウンロードして使ってみてください。
  • IBM 試用ソフトウェア: developerWorks から直接ダウンロードできるトライアル・ソフトウェアで、次の開発プロジェクトを構築してください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=304320
ArticleTitle=XQuery によって XPath を拡張する方法
publish-date=04012008