Xindice:無料で使えるXMLデータベース(2)

JavaアプリケーションによるXMLデータベース検索


2-2. 検索サンプルアプリケーションの実際

 サンプルアプリケーションの解説に入る前に、実際のアプリケーションを記述する際に必要と思われる部分についても触れておきましょう。

例外

 XML:DBのメソッドで何がしかのエラーが発生した場合、例外XMLDBExceptionが送出されます。これをcatchして、適切な対処を行う必要があります。

try {
  // XML:DBを利用した処理
} catch (org.xmldb.api.base.XMLDBException e) {
  // エラー処理
}

XPath

 検索そのものはXPathを利用しますので、XPathについて簡単に紹介しておきます。XPathをすでによくご存じの方はここを飛ばしていただいて構いません。

 XPathは、ちょうどファイルシステムのパスのような書式でXML文書の特定の部分を示すことができます。下記に例を2つ示します。

/child::books/child::book[@name='本の名前']
book//author[1]

 XPathの式ではロケーションパス、関数、数値などが許され、その式を評価した結果として、ノードセット(ノードの集合)、真偽値などが返されます。Xindiceでは、ノードセットを返す式のみ利用可能です。以下で、ロケーションパスおよびその構成要素であるロケーションステップについて説明します。

ロケーションパス

 ロケーションパスがXPathの基本です。結果としてノードセットが返されます。以下のような書式で記述します。

(/)ロケーションステップ/ロケーションステップ/……

 先頭の“/”は省略可能で、”/”があるときはドキュメントルートからのパス指定、なければ、そのコンテキストで選ばれているノードからの相対パス指定となります。

ロケーションステップ

 ファイルシステムのパスでディレクトリ名やファイル名に相当する部分をロケーションステップと呼びます。ロケーションステップは、Axis(軸)識別子、ノードテスト、Predicate(述語)からなっています。

Axis識別子::ノードテスト[Predicate式][Predicate式]……

 Axis識別子は、パスをたどる方向と範囲を示すものです(たどるノードのデフォルトのタイプも限定します)。ファイルシステムのパスであれば、ディレクトリの中のファイルというように直接の子ノードをたどることしかできませんが、XPathではこのAxis識別子の指定によって「属性をたどる」「子ノード方向にたどれるすべてのノードをたどる」といった多彩な指定ができます。以下によく使われるAxis識別子を挙げておきます。

Axis識別子 省略形 説明
attribute @ コンテキストノードの属性を示します /problem/@date
child (Axis識別子を省略するとchildになります) コンテキストノードの子ノードを示します /problem/child::description
descendant なし コンテキストノードから子ノード方向にたどれるすべてのノードを示します /problem/descendant::description
descendant-or-self /
(厳密には、「descendant-or-self::node()」の省略形)
コンテキストノードを含む子ノード方向にたどれるすべてのノードを示します //action
表1 よく使われるAxis識別子

 Axis識別子に続くノードテストは、ノードの名前あるいはタイプを選択するものです。名前の場合はネームスペースも区別します。

 ノードテストの「*」は、Axis識別子で指定された範囲のすべてのノードを選択します。例えば、「child::*」とすると、すべての子ノードを選択することになりますし、「descendant::*」とすると、子ノード方向にたどれるすべてのノードを選択することになります。タイプを指定する場合は、「child::node()」や「child::text()」のようにタイプ名に括弧を付けて表現します。

 Predicateは、フィルタあるいは制約条件くらいに認識すればよいと思います(誤解を承知でいえば、SQLでのWHEREのようなものと思ってもよいかもしれません)。Predicateで指定する条件式ですが、これ自体がXPathの式を記述します。そして、その式を評価した結果を真偽値に変換した結果が真となったノードだけが結果として残ります(一般的にはboolean関数で変換しますが、数値の場合のみノードセットの中での位置がその数値で示された位置かどうかという意味で解釈されます)。Predicateに記述した式はノードテストで選択されたノード1つ1つについて、おのおののノードをコンテキストノードとした評価が行われます。

 Predicateの例を以下に示します。

式の例 説明
problem[@status='open'] 要素<problem>の属性「status」が'open'であるものを指します
action[2] 要素<action>の2番目のものを指します
action[@who='X']/description 要素<action>の属性「who」が'X'であるものの子ノードである要素<description>を指します
problem[@status='open'][@title='Hung up'] 要素<problem>の属性「status」が'open'であるものの中から属性「title」が'Hung up'であるものを指します
表2 Predicateの例

Xindiceで検索するサンプルアプリケーション

  ここからは実際に、XML:DB APIを利用して検索するコマンドライン上のサンプルアプリケーションを2つ紹介します。1つ目はネームスペースを利用しない例、2つ目はネームスペースを利用する例になっています。

 このサンプルアプリケーションでは、前回「ネイティブXMLデータベースを立ち上げる!」で使ったデータベースインスタンスをそのまま利用しますので、ない場合は前回を参照してデータベースインスタンスを準備してください(具体的には、sampledbというコレクションに前回紹介した2つのXML文書が登録されている必要があります)。

 また、コンパイル・実行のためには以下のようにクラスパスを設定しておく必要があります。

編注:以下のリストでは、入力するコマンドが分かりやすいように、エンターキーを押下する位置に記号を記しました。

Windows
C:\> set XINDICE_LIB=%XINDICE_HOME%\java\lib
C:\> set CLASSPATH=.;%XINDICE_LIB%\xmldb.jar;% XINDICE_LIB%\xindice.jar;%XINDICE_LIB%\openorb-1.2.0.jar;%XINDICE_LIB%\xerces-1.4.3.jar;%XINDICE_LIB%\xalan-2.0.1.jar
UNIX系(Bシェル)
$ XINDICE_LIB=$XINDICE_HOME/java/lib
$ export CLASSPATH=$XINDICE_LIB/xmldb.jar:$XINDICE_LIB/xindice.jar:
$XINDICE_LIB/openorb-1.2.0.jar:$XINDICE_LIB/xerces-1.4.3.jar:$XINDICE_LIB/xalan-2.0.1.jar

ネームスペースを利用しない例

 最初は、ネームスペースを利用しない例です。前回登録したXML文書のうち、まだステータスがオープンの問題だけを検索し、そのタイトルと説明を表示します。

 コードは以下のようになります。

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;
import org.w3c.dom.*;

public class Example1 {
  public static void main(String[] args) throws Exception {
    Collection col = null;
    try {
      // 1. Database実装クラスの登録
      String driver =
        "org.apache.xindice.client.xmldb.DatabaseImpl";
      Class c = Class.forName(driver);
      Database database = (Database) c.newInstance();
      DatabaseManager.registerDatabase(database);

      // 2. コレクションの取得
      col = DatabaseManager.getCollection
         ("xmldb:xindice:///db/sampledb");

      // 3. XPathQueryServiceの取得
      XPathQueryService service =
        (XPathQueryService) col.getService("XPathQueryService", "1.0");

      // 5. XPathで検索(ステータスがオープンのもの)
      String xpath = "//problem[@status='open']";
      ResourceSet resultSet = service.query(xpath);

      // 6. ResourceSetから検索結果を取り出す
      ResourceIterator results = resultSet.getIterator();
      while (results.hasMoreResources()) {
        Resource res = results.nextResource();
        if (res.getResourceType().equals("XMLResource")) {
          org.xmldb.api.modules.XMLResource xmlres =
            (org.xmldb.api.modules.XMLResource)res;
          org.w3c.dom.Node node = xmlres.getContentAsDOM();
          System.out.println("Title :" + ((Element)
            node.getFirstChild()).getAttribute("title"));
          org.w3c.dom.Node desc =
            org.apache.xpath.XPathAPI.selectSingleNode
            (node, "problem/description");
          System.out.println("Description :" +
            desc.getFirstChild().getNodeValue());
        }
      }
    }
    catch (XMLDBException e) {
      System.err.println("XML:DB Exception occured " + e.errorCode);
    }
    finally {
      if (col != null) {
        // 7. コレクションのクローズ
        col.close();
      }
    }
  }
}
Example1.java ネームスペースを利用しない検索サンプルアプリケーション

 この例では、XML:DBのXPathで検索した結果を getContentAsDOMメソッドでDOMとして読み込んだ後、要素<problem>の属性「title」と要素<description>を表示しています。

 ここで、要素<description>の検索にはXalan(Xindiceに付属しています)のXPathを使用しています。このように、XML:DBでは比較的まとまった単位で取り出して、細かい要素や属性を取り出すにはDOMやXalanによるXPathを使うようにすると効率的です。

 上記のコードをファイル名Example1.javaとして保存し、コンパイル、実行すると、結果は以下のようになります。

Title :core dump
Description :
サーバがコア・ダンプする。
サンプルアプリケーションによる検索結果

ネームスペースを利用する例

 次に、先ほどの例をネームスペースを利用するように変更してみましょう。まず、ネームスペースを指定した以下のXML文書をデータベースに追加します。

<?xml version="1.0" encoding="Shift_JIS"?>
<p:problem p:id="ID0003" p:status="open" p:title="Hung up" p:date="2002/05/21"xmlns:p="http://sample_xindice/">
  <p:description>
    DBサーバがHung Upする。
  </p:description>
  <p:actions>
    <p:action p:who="A">
      <p:description>
        サポートに問合せ。
      </p:description>
    </p:action>
    <p:action p:who="B">
      <p:description>
        情報収集
      </p:description>
    </p:action>
  </p:actions>
</p:problem>
ネームスペースを利用したXML文書。これをXindiceに登録しておく

 ネームスペースを利用したドキュメントを検索するコードは、以下のようになります。

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;
import org.w3c.dom.*;

public class Example2 {
  public static void main(String[] args) throws Exception {
    Collection col = null;
    try {
      // 1. Database実装クラスの登録
      String driver =
        "org.apache.xindice.client.xmldb.DatabaseImpl";
      Class c = Class.forName(driver);
      Database database = (Database) c.newInstance();
      DatabaseManager.registerDatabase(database);

      // 2. コレクションの取得
      col = DatabaseManager.getCollection
        ("xmldb:xindice:///db/sampledb");

      // 3. XPathQueryServiceの取得
      XPathQueryService service = (XPathQueryService)    
        col.getService("XPathQueryService", "1.0");

      // 4. ネームスペースの設定(追加)
      String prefix = "p";
      String uri = "http://sample_xindice/";
      service.setNamespace(prefix, uri);

      // 5. XPathで検索(xpathを変更)
      String xpath = "//p:problem[@p:status='open']";
      ResourceSet resultSet = service.query(xpath);

      // 6. ResourceSetから検索結果を取り出す(プリフィックスを付けるよう変更)
      ResourceIterator results = resultSet.getIterator();
      while (results.hasMoreResources()) {
        Resource res = results.nextResource();
        if (res.getResourceType().equals("XMLResource")) {
          org.xmldb.api.modules.XMLResource xmlres =     
            (org.xmldb.api.modules.XMLResource)res;
          org.w3c.dom.Node node = xmlres.getContentAsDOM();
          System.out.println("Title :" + ((Element)
            node.getFirstChild()).getAttributeNS
            ("http://sample_xindice/", "title"));
          org.w3c.dom.Node desc =
            org.apache.xpath.XPathAPI.selectSingleNode
            (node, "p:problem/p:description", node);
          System.out.println("Description :" +
            desc.getFirstChild().getNodeValue());
        }
      }
    }
    catch (XMLDBException e) {
      System.err.println("XML:DB Exception occured " + e.errorCode);
    }
    finally {
      if (col != null) {
        // 7. コレクションのクローズ
        col.close();
      }
    }
  }
}
Example2.java ネームスペースに対応したXML文書の検索サンプルアプリケーション。下線のあるところが、主に変更されたところ

 ネームスペースに対応するため、4を追加し、5と6を変更していますが、XML:DB、DOM、Xalanでネームスペースを扱うための方法が異なっていてややこしいので注意が必要です。

 全体としては、XPathによる検索およびDOMによる属性の取り出しにおいて、「http://sample_xindice/」というURIを指定するよう変更しています。こうすることで、前回の記事で登録したネームスペースの指定のないドキュメントではなく、今回登録したネームスペースの指定のあるドキュメントを扱うようになります。

 変更部分のコードがどう変わったかというと、4と5では、先に説明したXML:DBでのネームスペースの設定および、設定したプリフィックスを使ってXPathの式を記述しています。6では、属性「title」を取り出すところで、DOMのgetAttributeメソッドに代えて、URIの指定できるgetAttributeNSメソッドを使っています。また、要素<description>を取り出すところで、XalanのselectSingleNodeメソッドの第3引数にネームスペースのプリフィックスを参照するためのノード(ここではDocumentそのもの)を指定し、XPathの式でプリフィックスを使って記述しています。

 上記のコードをファイル名Example2.javaとして保存し、コンパイル・実行すると、以下のように新規に追加した問題が表示されます。

Title :core dump
Description :
サーバがコア・ダンプする。
サンプルアプリケーションによる検索結果

次回はインデックスの使い方

 次回は、今回に続いて検索を取り上げ、サーブレットやXSLTを使った例や、インデックスの使い方を見ていきます。

4/9

Index
Xindice:無料で使えるXMLデータベース
  Xindiceをインストールする(Windows/UNIX)
・Javaで作られたXindice
・ネイティブXMLデータベースの特徴
・Xindiceの特徴
・Xindiceをインストールする
  XMLデータベースの作成と基本的な検索
・起動と停止
・コマンドラインからXindiceを利用する
・コレクションとドキュメント
・では、検索してみよう
・Xindiceの主なコマンド一覧
  Javaプログラムによる検索の手順
・検索用APIの使い方
検索サンプルアプリケーションの実際
・例外
・XPath
・Xindiceで検索するサンプルアプリケーション
・ネームスペースを利用しない例
・ネームスペースを利用する例
  サーブレットからXindiceを呼び出す
・サーブレット環境の準備
・Webアプリケーションの準備
  Xindiceを呼び出すサンプルサーブレット
・XPath式の結果をXML文書として返す
・結果をXSLTでHTMLに変換する
・変換のためのXSLTは別ファイル
  インデックスで高速化
・インデックス管理コマンド
・インデックスの有無によるスピード比較
・測定結果
  XUpdate言語の使い方
・現在ドラフト中のXUpdate
・ノードの挿入・追加
・ノードの更新
ノードの削除
・ノード名変更
  Javaアプリケーションから更新・削除を実行する
・更新用APIの使い方
・サンプルアプリケーション
・実行準備


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間