JanusGraphを使ってグラフDBを構築するには、大きく2つの方法がある。EmbeddedとGremlin Serverである。
Embedded
TinkerPop - Embedded
その名の通り、実行するJVM(Java Virtual Machine)上に組み込まれるグラフDBであり、最小構成ではスタンドアロンで運用できる。シンプルなスタイル。
この方法の手順は概ね以下の通りになる。
-
Graph
のインスタンスを生成する -
GraphTraversalSource
のインターフェースを取得する - クエリを発行し、結果を受け取る
簡単なソースコードで表すと以下のようになる(言語はgroovy)。
Graph graph = TinkerGraph.open(); // 空のグラフを生成
GraphTraversalSource g = graph.traversal();
g.addV("person").iterate();
グラフの生成の方法は様々ある。TinkerPopが用意するグラフ用のクラスはTinkerGraphやTinkerFactoryがあるが、これらはデータの永続化やトランザクション処理が出来ない。JanusGraphは標準の方法としてJanusGraphFactoryを提供しており、これなら永続化もトランザクションも出来る。
以下はGremlinコンソール上でJanusGraphFactoryを使ってみるサンプルである(Windows10上でJanusGraph0.5.1を使用)。
gremlin> graph = JanusGraphFactory.open("conf/janusgraph-berkeleyje-lucene.properties")
==>standardjanusgraph[berkeleyje:C:\******\janusgraph-0.5.1\conf\../db/berkeley]
gremlin> g = graph.traversal()
==>graphtraversalsource[standardjanusgraph[berkeleyje:C:\******\janusgraph-0.5.1\conf\../db/berkeley], standard]
gremlin> g.addV("person").property("name", "bob")
==>v[4112]
設定ファイルにはconf/janusgraph-berkeleyje-lucene.properties
を使用した。これはストレージ・バックエンドにBerkeley DB
、インデックス・バックエンドにLucene
ライブラリを使用する設定である。ストレージはdb/berkeley
に生成・保存される。
JanusGraphではドキュメント中のコードではEmbeddedグラフを使ったコードを記載しているので、まずはこちらに親しむのが得策と思われる。
Gremlin Server
サーバーについては既にJanusGraphによるグラフDB入門という記事の中で少し説明しているので、そちらも参照のこと。
こちらはEmbeddedとは違い、専用のサーバープロセスを立ち上げ、ポートを通じてクライアントと接続する。こちらの方法ではJanusGraphFactoryなどは使用できない。その代わりに以下の2種類の接続方法が使える。
-
DriverRemoteConnection
とAnonymousTraversalSource
を使った接続 -
Cluster
とClient
を使った接続
DriverRemoteConnection&AnonymousTraversalSource
TinkerPopドキュメント - Connecting Gremlin Server
こちらはEmbeddedの場合と似通っていて、分かりやすい手法である。
-
DriverRemoteConnection
を使ってサーバーとの接続を確立する -
AnonymousTraversal
を使ってGraphTraversalSource
(g)を獲得する - クエリを送信し、結果を取得する
- 終わったら接続を閉じる
connection = DriverRemoteConnection.using('localhost', 8182, 'g')
g = AnonymousTraversalSource.traversal().withRemote(connection)
vertices = g.V().id().toList()
connection.close()
この方法の場合の特徴は2つある
-
GraphTraversalSource
(g)は取得できるが、Graph
(graph)は取得できないので、ManagementSystem
(スキーマやインデックスの定義インターフェース)や、AddVertex
やaddEdge
といった"structure API"へのアクセスが制限される。 - 毎回のクエリ実行は1つのトランザクションでラッピングされて実行される。自分で複数のクエリを1つのトランザクションとして実行するような制御は出来ない。
TinkerPop javadoc - DriverRemoteConnection
TinkerPop javadoc - AnonymousTraversalSource
Cluster&Client
TinkerPopドキュメント - Connecting Via Drivers
こちらは比較的泥臭いスタイルとなる。
-
Cluster
を使ってサーバーとの接続を確立する(言語によっては不要) -
Client
を使って、クエリメッセージを送信できる状態にする - クエリを送信し、結果を受け取る
- Clientを使い終わったら、
Client.close
で処分 - Clusterを使い終わったら、
Cluster.close
で処分
cluster = Cluster.open() // 引数省略時はlocalhost:8182に接続
client = cluster.connect()
result = client.submit("g.V().count()") // 直接文字列で送信
print(result[0].object)
client.close()
cluster.close()
この場合、クエリは文字列で指定する。なおGremlinの場合もインジェクション攻撃が存在し、Gremlin Injection
と呼ばれている。対策については大体SQLの場合と同じ(Parameterized Scriptsを参照)
Cluster.connect
は2引数のオーバーライドconnect(sessionID, manageTransactions)
を持つ。第二引数のmanageTransactions
がfalse
の場合、自動でのトランザクション管理は行われず、ユーザーが自分でトランザクションを管理することができる(ただし正しく扱う責任が生じる)。true
の場合はsubmitを行うたびに自動的にトランザクションが開始され、送信したクエリの最後にコミットまたはロールバックが行われる。引数なしのconnect
を実行した場合はmanageTransactions=true
がデフォルトである。
cluster = Cluster.open()
client = cluster.connect("mysession", true) // managed
client.submit("g.addV('person').property('name', 'joseph').iterate()") // 登録される(自動コミット)
client.submit("g.tx().rollback()") // コミット済みなので取り消せない
client.close()
client = cluster.connect("mysession", false) // not managed
client.submit("g.addV('person').property('name', 'joseph').iterate()") // まだ登録されない
client.submit("g.tx().rollback()") // 取り消せる
client.close()
cluster.close()
Gremlinコンソールではリモート接続した際、ユーザーが打ち込んだコマンドをそのままClient.submitで送信している(コード)。つまりGremlinコンソール上のリモート送信(:>)で実行できることは全てこの方法で実行可能である。
TinkerPop Javadoc - Cluster
TinkerPop Javadoc - Client
サーバーの接続方法はどちらを使うべき?
どちらにも一長一短があるため、状況に応じて両方を併用するのが良いと思われる。
単純なクエリを扱うなら前者を、複雑またはトランザクションが必要なクエリなら後者を使うのが良さそうだ。
EmbeddedとServerはどちらがいいの?
練習用、あるいは単一プロセスで済むようなアプリケーションならEmbeddedで良さそう。
一方、複数のプロセスが並列にデータアクセスするような状況ではサーバーを立てるべきだと考えられる。
まとめ
方式 | DB接続方法 | リモート接続 | 手動トランザクション制御 | 備考 |
---|---|---|---|---|
Embedded | JanusGraphFactory | × | 〇 | |
Server | DriverRemoteConnection& AnonymousGraphTraversal |
〇 | × | Graphインターフェースに対するアクセスができない |
Server | Cluster&Client | 〇 | 〇 | クエリは文字列で送信(インジェクションに注意) |