[Neo4J]　⑤AnormCypher編(バッチ処理+Play!下調査)

息長くNeo4Jを試していこう、のscala編

Neo4Jのクエリ言語Cypherそのものがscalaで書かれているわけで、一番容易に扱えそうな気がするが、github上では、マイナーな"さいきょうDSL"がいくつも出できて使い方に迷う。RDBとは作りが違うわけで、ライブラリ作者の皆さんも試行錯誤しながらなのだろう。

今回は、その中で無難そうなAnormCypherを試す(Anormは、Scala/JavaのWAF Play!の標準ORMの名前)。

#1) AnormCypherお試し

他のライブラリで動かないを経験したばかりなので、
必要に応じ手を入れる気満々で、そのままcloneしてしまう。
> git clone https://github.com/AnormCypher/AnormCypher

ざっと眺めると、Neo4jRESTクラスでNeo4JのJSON APIに接続するらしい。

```src/main/scala/org/anormcypher/Neo4jREST.scala
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"
  )
(略)
```

ということで、ライブラリの文書も参考に、
ダウンロードしたフォルダ直下あたりに、お試し準備のコードを書く。

```otameshi.scala
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
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も立ち上げたまま。

その上で、ソースコードを持ってきて、
> git clone https://github.com/gvolpe/social-graph-api

各種設定を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経由で呼び出すコードとして参考になるところでもあるので、載せておく:

```改訂版_app/repository/Neo4JConnection.scala
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一覧はこんな感じ:

```conf.routes
# 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."という、気合の入った構成の以下をベースとして試していく。

https://github.com/KyleU/boilerplay

事前準備は、ローカルで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の避け方](http://qiita.com/bigwheel/items/44cb874ced4be204c09c)の第一bad partsである
独自activatorは触らなくて良さそうだ。

```shell-session
git clone https://github.com/KyleU/boilerplay
cd biolerplay
sbt run
```

localhost:9000にアクセスする:

![スクリーンショット 2015-11-15 9.51.35.png](https://qiita-image-store.s3.amazonaws.com/0/16455/f4911725-5919-2e75-6cc7-794b88e9566b.png)

一通り無難に動作する。
さて、これを先ほどのsocial-graph-apiを参考に、どう料理していくか...(続く)