4.5. サーバープラグイン

プラグインは、Neo4j REST API に新機能を追加して拡張するための簡単な手段であり、独自に API を開発する必要はありません。プラグインは、ノードやリレーション、パス、プロパティなどの取得・操作の機能を追加することができるサーバーサイドスクリプトと考えることができます。

[警告]警告

API を独自に用意して完全にコントロールしたい場合、そのための必要な労力を払ってリスクを引き受ける覚悟があるなら、Neo4j Server に用意されている、JAX-RS をベースにしたアンマネージド拡張機能用のフックを利用することもできます。

プラグインを作成するには、コードで ServerPlugin クラスを継承しなければならず、このクラスで Node、Relationship、または Path (の Iterable) を生成し、各種のパラメータ、拡張ポイント、そしてもちろんアプリケーションロジックを指定できるようにする必要があります。次に示すのは、(ノードやリレーションではなく) データベースを拡張するプラグインの例です。

@Description( "An extension to the Neo4j Server for getting all nodes or relationships" )
public class GetAll extends ServerPlugin
{
  @Name( "get_all_nodes" )
  @Description( "Get all nodes from the Neo4j graph database" )
  @PluginTarget( GraphDatabaseService.class )
  public Iterable<Node> getAllNodes( @Source GraphDatabaseService graphDb )
  {
      return graphDb.getAllNodes();
  }


  @Description( "Get all relationships from the Neo4j graph database" )
  @PluginTarget( GraphDatabaseService.class )
  public Iterable<Relationship> getAllRelationships( @Source GraphDatabaseService graphDb )
  {
      return new NestingIterable<Relationship, Node>( graphDb.getAllNodes() )
      {
          @Override
          protected Iterator<Relationship> createNestedIterator( Node item )
          {
              return item.getRelationships( Direction.OUTGOING ).iterator();
          }
      };
  }

  @Description( "Find the shortest path between two nodes." )
  @PluginTarget( Node.class )
  public Iterable<Path> shortestPath(
            @Source Node source,
            @Description( "The node to find the shortest path to." ) @Parameter( name = "target" ) Node target,
            @Description( "The relationship types to follow when searching for the shortest path(s). Order is insignificant, if omitted all types are followed." ) @Parameter( name = "types", optional = true ) String[] types,
            @Description( "The maximum path length to search for, default value (if omitted) is 4." ) @Parameter( name = "depth", optional = true ) Integer depth )
  {
        Expander expander;
        if ( types == null )
        {
            expander = Traversal.expanderForAllTypes();
        }
        else
        {
            expander = Traversal.emptyExpander();
            for ( int i = 0; i < types.length; i++ )
            {
                expander = expander.add( DynamicRelationshipType.withName( types[i] ) );
            }
        }
        PathFinder<Path> shortestPath = GraphAlgoFactory.shortestPath( expander, depth == null ?4
                : depth.intValue() );
        return shortestPath.findAllPaths( source, target );
  }
}

コードをデプロイするには、コードをコンパイルして .jar ファイルにし、.jar ファイルをサーバーのクラスパス上 (慣習的に Neo4j Server のホームディレクトリの下の plugins ディレクトリ) に置きます。.jar ファイルには、実装クラスの完全修飾名の収められたファイル META-INF/services/org.neo4j.server.plugins.ServerPlugin が含まれていなければなりません。現在の例の場合、設定ファイルには次のように 1 つしかエントリがありませんが、一般には 1 行に 1 つずつ、複数のエントリを記述することができます。

org.neo4j.server.examples.GetAll
# Any other plugins in the same jar file must be listed here

上のコードでは、Neo4j Server からデータベースが提示されるときは常に、(@PluginTarget アノテーションを介して) データベース表現の中で拡張機能が見えるようにしています。@PluginTarget のパラメータを Node.class または Relationship.class に変更すれば、データモデルのうちこれらの部分をターゲットにすることができます。プラグインが提供する機能拡張は、ワイヤー上の表現の中で自動的にアドバタイズされます。たとえば、クライアントは、サーバーからレスポンスとして受け取った表現を調べることで、すなわちデフォルトデータベース URI に対して GET を実行することで、上のプラグインによって実装された拡張機能を簡単に見つけることができます。

curl -v http://localhost:7474/db/data/

GET リクエストに対するレスポンスには (デフォルトで) JSON コンテナが含まれており、この JSON コンテナ自身に "extensions" と呼ばれるコンテナが含まれていて、この中に利用可能なプラグインがリストアップされています。次に示す例では、サーバーに登録されているのは GetAll プラグインだけなので、このプラグインの拡張機能だけが利用可能です。

{
"extensions-info" : "http://localhost:7474/db/data/ext",
"node" : "http://localhost:7474/db/data/node",
"node_index" : "http://localhost:7474/db/data/index/node",
"relationship_index" : "http://localhost:7474/db/data/index/relationship",
"reference_node" : "http://localhost:7474/db/data/node/0",
"extensions_info" : "http://localhost:7474/db/data/ext",
"extensions" : {
  "GetAll" : {
    "get_all_nodes" : "http://localhost:7474/db/data/ext/GetAll/graphdb/get_all_nodes",
    "get_all_relationships" : "http://localhost:7474/db/data/ext/GetAll/graphdb/getAllRelationships"
  }
}

2 つの拡張 URI の一方に対して GET を実行すると、該当するサービスに関するメタ情報が返されます。

curl http://localhost:7474/db/data/ext/GetAll/graphdb/get_all_nodes
{
  "extends" : "graphdb",
  "description" : "Get all nodes from the Neo4j graph database",
  "name" : "get_all_nodes",
  "parameters" : [ ]
}

サービスを使うには、"description" で指定されたパラメータを指定してこの URL に対して POST を実行します (ただし、上の例では指定する必要のあるパラメータはありません)。

このようなモデルによって、どんなプラグインも、Neo4j が採用している汎用的なハイパーメディアスキームにごく自然に取り込むことができます。このため、クライアントはこれまでと同様に Node、Relationship、Path といった抽象化されたオブジェクトを活用する一方、プラグインによって強化されたサーバーをそのまま利用することができます (すなわち、古いクライアントが使えなくなくことはありません)。