インデックスを使用した検索

neo4jではApocという拡張コンポーネントパッケージを使用することでLuceneを使用した検索を行うことができる。これらの拡張機能を使用するにはneo4jのダウンロードページからneo4j-apoc-1.0.zipを取得し、展開したアーカイブのlibディレクトリに格納されているJARファイルをクラスパスに追加する必要がある。

Mavenで使用する場合はpom.xmlに以下の記述を追加しよう。

<dependency>
   <groupId>org.neo4j</groupId>
   <artifactId>neo4j-apoc</artifactId>
   <version>1.0</version>
   <type>pom</type>
</dependency>

インデックスは自動では作成されず、プログラム中でIndexServiceを使用して明示的に作成する必要がある。以下の例で使用しているLuceneIndexServiceはLuceneを使用してインデックスを作成するためのサービスで、完全一致による検索を行うことができる。

// DBの保存先ディレクトリを指定してDBのインスタンスを生成
GraphDatabaseService graphDb = new EmbeddedGraphDatabase("db");
// トランザクションを開始
Transaction tx = graphDb.beginTx();
// インデックスサービス
IndexService index = new LuceneIndexService( graphDb );

try {
  Node personNode = graphDb.createNode();
  personNode.setProperty( "name", "Naoki Takezoe" );

  // インデックスを作成
  index.index( personNode, "name", personNode.getProperty( "name" ) );

  // インデックスを使用して一致するノードのリストを取得
  IndexHits<Node> nodeList = index.getNodes("name", "Naoki Takezoe");
  for(Node node: nodeList){
    System.out.println(node.getProperty("name"));
  }

  // インデックスを使用して1件取得
  Node node = index.getSingleNode("name", "Naoki Takezoe");
  System.out.println(node.getProperty("name"));

  // 処理が成功
  tx.success();

} catch(Exception ex){
  // 処理が失敗
  tx.failure();

} finally {
  // トランザクションを終了してDBをシャットダウン
  tx.finish();
  index.shutdown();
  graphDb.shutdown();
}

LuceneIndexServiceの代わりにLuceneFulltextIndexServiceを使用してインデックスを作成することで全文検索も可能だ。部分一致で検索したいフィールドがある場合はこちらを使用するといいだろう。

Javaオブジェクトへのマッピング

neo4jではNodeオブジェクトとRelationshipオブジェクトを使用してグラフを表現するが、jo4neoを使用することでグラフをJavaオブジェクトにマッピングすることができる。

jo4neoを使用するにはneo4jのApocのJARファイルに加えてjo4neoのWebサイトからダウンロードしたJARファイルをクラスパスに追加する必要がある。Mavenで使用する場合はpom.xmlに以下の記述を追加しよう。

<repositories>
   <repository>
      <id>jo4neo-repository</id>
      <url>http://jo4neo.googlecode.com/svn/trunk/repo/</url>
   </repository>
</repositories>
...
<dependencies>
   <dependency>
      <groupId>thewebsemantic</groupId>
      <artifactId>jo4neo</artifactId>
      <version>0.4.1</version>
   </dependency>
   ...
</dependencies>

jo4neoでは以下のように@neoアノテーションを付与したJavaBeanでノードを定義することができる。関連を定義するには他のオブジェクトやjava.util.Collection型のフィールドを定義して@neoアノテーションを付与しておけばよい。また、JavaBeanには必ずjo4neo.Nodeid型のtransientフィールドを定義しておく必要があるので注意してほしい。

public class Employee {

  public transient Nodeid neo;

  @neo(index=true)
  public String name;

  @neo(index=true)
  public String mail;

}

jo4neoで指定可能なアノテーションには以下のようなものがある。

属性名 説明
@neo String name; フィールドをノードのプロパティとして永続化する
@neo Person friend; 関連するオブジェクトをノードとリレーションとして永続化する
String misc; フィールドを永続化しない
@neo(index=true) String name; フィールドを永続化し、インデックスを作成する
@neo(fulltext=true) String content; フィールドを永続化し、全文検索インデックスを作成する
@neo("HAS_JOB") Job job; リレーションを指定した名前で永続化する
@embed Address address; 関連するオブジェクトをリレーションではなくバイナリシリアライズして永続化する

このノードをデータベースに永続化するには以下のようにする。jo4netが提供するObjectGraphというインタフェースを使用して操作を行っている。検索や削除もこのインタフェースで行うことができる。

GraphDatabaseService graphDb = new EmbeddedGraphDatabase("db");
ObjectGraph graph = ObjectGraphFactory.instance().get(graphDb);

try {
  Person person = new Person();
  person.name = "Naoki Takezoe";
  person.mail = "takezoen@gmail.com";

  // オブジェクトを永続化
  graph.persist(person);

} finally {
  graph.close();
  graphDb.shutdown();
}

直接neo4jを用いる場合と同様、jo4neoでも検索にはいくつかの方法がある。

// オブジェクトの型を指定して全件取得
Collection<Person> list = graph.get(Person.class);

// IDを指定して1件取得
Person person = graph.get(Person.class, 1);

// 条件を指定して検索
Employee emp = new Employee();
emp = graph.find(emp).where(emp.name).is("Naoki Takezoe").result();

このようにjo4neoを使用するとneo4jをオブジェクトデータベースとして使用することができる。また、インデックスの作成もアノテーションだけで指定することができる。スキーマを予め定義できる場合はNodeオブジェクトやRelationshipオブジェクトを直接扱うよりもタイプセーフなJavaBeanのほうが扱いやすいだろう。neo4jにおけるオブジェクト/グラフマッパーとして積極的にjo4neoを活用したいところだ。

なお、jo4neoはJavaBeanへのマッピングを行うために各ノードにjo4neo.Nodeidというプロパティを自動的に追加する。そのためjo4netを使わずに作成した既存のデータベースをJavaBeanにマッピングすることはできないので注意してほしい。