息長くNeo4Jを試していこう、のscala編
Neo4Jのクエリ言語Cypherそのものがscalaで書かれているわけで、一番容易に扱えそうな気がするが、github上では、マイナーな"さいきょうDSL"がいくつも出できて使い方に迷う。RDBとは作りが違うわけで、ライブラリ作者の皆さんも試行錯誤しながらなのだろう。
今回は、その中で無難そうなAnormCypherを試す(Anormは、Scala/JavaのWAF Play!の標準ORMの名前)。
1) AnormCypherお試し
他のライブラリで動かないを経験したばかりなので、
必要に応じ手を入れる気満々で、そのままcloneしてしまう。
ざっと眺めると、Neo4jRESTクラスでNeo4JのJSON APIに接続するらしい。
package org.anormcypher
import play.api.libs.json._, Json._
import play.api.libs.ws._
import scala.concurrent._
class Neo4jREST(wsclient: WSClient,
val host: String = "localhost", val port: Int = 7474, val path: String = "/db/data/",
val username: String = "", val password: String = "",
val cypherEndpoint: String = "cypher", val https: Boolean = false) {
private val headers = Seq(
"Accept" -> "application/json",
"Content-Type" -> "application/json",
"X-Stream" -> "true",
"User-Agent" -> "AnormCypher/0.7.0"
)
(略)
ということで、ライブラリの文書も参考に、
ダウンロードしたフォルダ直下あたりに、お試し準備のコードを書く。
package kmr
import org.anormcypher._
import play.api.libs.ws._
object TEST {
// Provide an instance of WSClient
val wsclient = ning.NingWSClient()
// Setup the Rest Client
implicit val connection = Neo4jREST(username="neo4j", password="aggrega4")(wsclient)
// Provide an ExecutionContext
implicit val ec = scala.concurrent.ExecutionContext.global
// create some test nodes
val result = Cypher("""
create (germany {name:"Germany", population:81726000, type:"Nation", code:"DEU"}),
(france {name:"France", population:65436552, type:"Nation", code:"FRA", indepYear:1790}),
(japan {name:"日本", population:115436552, type:"Nation", code:"JPN", indepYear:790}),
(monaco {name:"Monaco", population:32000, type:"Nation", code:"MCO"});
""").execute()
val allNations = Cypher("start n=node(*) where n.type = 'Nation' return n.code as code, n.name as name")
lazy val nations = allNations.apply().map(row =>
row[String]("code") -> row[String]("name")
).toList
// shut down WSClient
def close ()= wsclient.close()
}
sbtでREPL consoleを立ち上げる。
sbt console
(ここで、依存するライブラリが続々とダウンロードされる)
REPLでは、例えば、以下の様なお試しができる(日本語も問題なく永続化できている)。
scala> import kmr._
import kmr._
scala> val nations_lst = TEST.nations
nations_lst: List[(String, String)] = List((DEU,Germany), (FRA,France), (JPN,日本), (MCO,Monaco))
scala> nations_lst foreach print _
(DEU,Germany)(FRA,France)(JPN,日本)(MCO,Monaco)
Neo4Jのブラウザ側からは、例えば以下のようにしてインサート状況が確認できる。
MATCH (n) WHERE n.type = "Nation" RETURN n;
...複数回実行すると、その回数分だけインサートされている、、、
2) Play!でredis+neo4J !?
"Authentication & Social Graph API built on top of Redis, Neo4J and Play!"
という、social-graph-apiを試してはる。こちら、裏でanormcypherを使っている
準備作業
redisをインストールして、立ち上げておく(Macではbrewで簡単に入る)。
neo4Jも立ち上げたまま。
その上で、ソースコードを持ってきて、
各種設定をconfに適切に書く。
手直しなど...
activatorを立ち上げてrunしようとするのだが、anormcypherが入らないと怒られる。
何かというと、sbtが見に行くanormcypherのリポジトリが落ちているらしい。
しょうがないのでbuild.sbtのライブラリをコメントアウトする。
// "org.anormcypher" %% "anormcypher" % "バージョンは古かった",
先ほどのanormcypherのソースコードからsbt packageしてjarを生成して、social-graph-api側のlibフォルダにいれる(Play!の伝統的なbad practice)。
さらにバージョン違いで、org.anormcypher.Neo4jRESTが動かないので、ソースも一部変更。
ちょうど、Play!側からNeo4JをREST経由で呼び出すコードとして参考になるところでもあるので、載せておく:
package repository
import org.anormcypher.Neo4jREST
import play.api.Play
import play.api.Play.current
import play.api.libs.ws.ning.NingWSClient
import com.ning.http.client.AsyncHttpClientConfig
object Neo4JConnection {
// この部分の実装が近時変わったらしい
val builder = new AsyncHttpClientConfig.Builder()
val wsclient = new NingWSClient(builder.build())
def apply(): Neo4jREST = {
val config = Neo4JConfig()
if (config.path.isDefined && config.username.isDefined)
Neo4jREST(
host = config.host,
port = config.port,
path = config.path.getOrElse("/db/data/"),
username = config.username.getOrElse(""),
password = config.password.getOrElse(""),
cypherEndpoint = config.cypherEndpoint.getOrElse("cypher"),
https = config.https.getOrElse(false))(wsclient)
else
Neo4jREST()(wsclient)
}
(以下は元ソースと同じ)
お試しタイム
(前提) Play!2.4.3 Redis3.0.5 Neo4Jがいずれもディフォルトのポートで立ち上がっている。
live Demoもあるのだが : https://social-graph-api.herokuapp.com/
認証周りが動いていない(おそらく、redisの設定あたり)
API一覧はこんな感じ:
# Authentication
POST /auth/signin controllers.AuthController.signIn
POST /auth/signup controllers.AuthController.signUp
GET /auth/admin controllers.AuthController.adminAction
# Users API
GET /api/v1/users controllers.UserController.findUsers
GET /api/v1/users/:id controllers.UserController.findUserById(id: Long)
POST /api/v1/users controllers.UserController.createUser
DELETE /api/v1/users/:id controllers.UserController.deleteUser(id: Long)
# Social Graph API
GET /api/v1/followers/:id controllers.RelationshipController.findFollowers(id: Long)
GET /api/v1/friends/:id controllers.RelationshipController.findFriends(id: Long)
POST /api/v1/friendship controllers.RelationshipController.createFriendship
DELETE /api/v1/friendship controllers.RelationshipController.deleteFriendship
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
REST APIアプリなので、サインアップはこんな感じで行う。
curl -X POST -H "Content-Type: application/json" -d '{ "identifier": "kmry@github.com", "password":"123456" }' http://localhost:9000/auth/signup
再度のサインインはこちら。
curl -X POST -H "Content-Type: application/json" -d '{ "identifier": "kmry@github.com", "password":"123456" }' http://localhost:9000/auth/signin
レスポンスとして、tokenが返される。
ここまでは動作したのだが、tokenの処理がうまくいっていないらしく、
Users API&Social Graph APIが残念ながら使えない。
silhouetteライブラリ
大体の処理の流れはわかったので、本アプリの認証周りを提供してくれているsilhouetteライブラリの本家にいってみるとバックエンドにさまざまなデータベースを使った例題が並んでいる(割合とメジャーなライブラリらしい)。
http://silhouette.mohiva.com/v3.0/docs/examples
3) 次回 : Play!で、Postgres+Neo4J
silhouetteライブラリはなかなかしっかりしているので継続利用させてもらうこととした。ただし、redis以外のデータベースとneo4jを組み合わせることとしたい(いずれはNeo4J単体で認証周りを扱うのもありなのかな...)。
まずは、
"a reactive web application built on Play 2.4, ScalaJS, Silhouette, and postgres-async."という、気合の入った構成の以下をベースとして試していく。
事前準備は、ローカルでpostgresを立ち上げ、psql等でユーザーとデータベースを用意するだけ:
CREATE ROLE boilerplay WITH LOGIN PASSWORD 'password';
CREATE DATABASE boilerplay;
GRANT ALL PRIVILEGES ON DATABASE boilerplay TO boilerplay;
本boilerplayは、Play!本家と異なり、sbtのみで扱える。
後悔しないPlay Framework 2のbad partsの避け方の第一bad partsである
独自activatorは触らなくて良さそうだ。
git clone https://github.com/KyleU/boilerplay
cd biolerplay
sbt run
localhost:9000にアクセスする:
一通り無難に動作する。
さて、これを先ほどのsocial-graph-apiを参考に、どう料理していくか...(続く)