TinkerPop でグラフ操作 - Kotlin
前回 の処理を Kotlin で実装してみました。
- Kotlin 1.1.3-2
- Apache TinkerPop
ソースは http://github.com/fits/try_samples/tree/master/blog/20170724/
a. ビルド定義・設定ファイル
今回は Gradle のサブプロジェクトとして実行します。
build.gradle (Gradle ビルド定義ファイル)
buildscript { ext.kotlin_version = '1.1.3-2' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" } } // サブプロジェクト共通設定 subprojects { apply plugin: 'kotlin' apply plugin: 'application' mainClassName = 'AppKt' sourceCompatibility = JavaVersion.VERSION_1_8 compileKotlin { kotlinOptions.jvmTarget = '1.8' } repositories { jcenter() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlin_version}" compile 'org.apache.tinkerpop:neo4j-gremlin:3.2.5' runtime 'org.neo4j:neo4j-tinkerpop-api-impl:0.6-3.2.2' runtime 'org.slf4j:slf4j-nop:1.7.25' } run { if (project.hasProperty('args')) { args project.args.split(' ') } } }
サブプロジェクトは以下の通り
settings.gradle (Gradle 設定ファイル)
include 'add-data' include 'find-data'
TinkerPop の設定ファイルは基本的に 前回 と同じものを使いますが、Gradle のサブプロジェクトとして実行する関係でディレクトリのパスを変えています。
conf/setting.properties (TinkerPop 設定ファイル)
gremlin.graph=org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph gremlin.neo4j.directory=../neo4jdb
b. グラフデータ作成
処理内容は 前回 と同じです。
add-data/src/main/kotlin/App.kt
import org.apache.tinkerpop.gremlin.structure.Graph import org.apache.tinkerpop.gremlin.structure.Vertex import org.apache.tinkerpop.gremlin.structure.util.GraphFactory fun main(args: Array<String>) { val conf = args[0] GraphFactory.open(conf).use { g -> g.tx().use { tx -> createData(g) tx.commit() } } } fun createData(g: Graph) { val p = addNode(g, "Principals", "principals") val u1 = addNode(g, "User", "user1") val u2 = addNode(g, "User", "user2") val ad = addNode(g, "User", "admin") val g1 = addNode(g, "Group", "group1") listOf(u1, u2, ad, g1).forEach { it.addEdge("PART_OF", p) } u2.addEdge("PART_OF", g1) val r = addNode(g, "Resources", "resources") val s1 = addNode(g, "Service", "service1") val s2 = addNode(g, "Service", "service2") val s2o1 = addNode(g, "Operation", "service2.get", "get") val s2o2 = addNode(g, "Operation", "service2.post", "post") listOf(s2o1, s2o2).forEach { s2.addEdge("METHOD", it) } listOf(s1, s2).forEach { r.addEdge("RESOURCE", it) } u1.addEdge("PERMIT", s1) g1.addEdge("PERMIT", s2o2) ad.addEdge("PERMIT", r) } fun addNode(g: Graph, label: String, id: String, name: String = id): Vertex { val node = g.addVertex(label) node.property("oid", id) node.property("name", name) return node }
add-data サブプロジェクトを実行するため :add-data:run
とします。
実行
> gradle :add-data:run -q -Pargs="../conf/setting.properties"
c. 経路の探索
Kotlin では __
や as
が予約語なので、`as`
のようにエスケープする必要があります。
また、Groovy と違って outE
や has
では型を指定する必要がありました。
find-data/src/main/kotlin/App.kt
import org.apache.tinkerpop.gremlin.structure.Vertex import org.apache.tinkerpop.gremlin.structure.util.GraphFactory import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.`__`.* import org.apache.tinkerpop.gremlin.structure.Element fun main(args: Array<String>) { val conf = args[0] val start = args[1] val end = args[2] GraphFactory.open(conf).use { g -> g.tx().use { val p = g.traversal().V() .has("oid", start) .repeat(outE<Vertex>().`as`("e").inV()) .until(has<Vertex>("oid", end)) .where(select<Vertex, Vertex>("e").unfold<Vertex>().hasLabel("PERMIT")) .path() p.forEach { println(it.objects().asSequence().map(::toStr).joinToString(" -> ")) } } } } fun toStr(n: Any) = when(n) { is Element -> "${n.label()}[${n.id()}]{${n.properties<String>().asSequence().joinToString(", ")}}" else -> "" }
実行結果
> gradle :find-data:run -q -Pargs="../conf/setting.properties user2 service2.post" User[2]{vp[oid->user2], vp[name->user2]} -> PART_OF[4]{} -> Group[4]{vp[oid->group1], vp[name->group1]} -> PERMIT[10]{} -> Operation[9]{vp[oid->service2.post], vp[name->post]}