技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

Salat でMongoDBのデータをCaseClassにマップする

Scala から MongoDB へアクセスする Casbah を真面目に使ってみる - 謎言語使いの徒然 の続き。

  • CaseClass にマップさえできればいい(極論それ以外はKVSだけあればどうとでもなる)
  • Lift-mongodb にしようかと思ったら assembla がダウンしててドキュメントがなかった

ので Salat 君に決めたっ。

Twitter を眺めてると assembla ちょくちょくダウンしてるらしい。 複雑な機能を有するライブラリでドキュメントのダウンタイムが長いのは導入の敵。

ライブラリ設定

scalaVersion := "2.10.2"

resolvers ++= Seq(
  "repo.novus rels" at "http://repo.novus.com/releases/",
  "Sonatype OSS" at "https://oss.sonatype.org/content/repositories/releases"
)

libraryDependencies ++= Seq(
  "org.mongodb" %% "casbah" % "2.6.4",
  "com.novus" %% "salat" % "1.9.4"
)

insert

import com.mongodb.casbah.Imports._
import com.novus.salat._
import com.novus.salat.global._

case class User(uid:String, provider:String, age:Int)

object Sample extends App {
  val col = DB.get().getCollection("test_collection")
  val userObj = grater[User].asDBObject(User("Azalea", "Facebook", 30))
  val result = col.insert(userObj)
  println(result)
}

シンプルにほげっと。

find

  val result = col.find()

  import scala.collection.JavaConversions._
  result.iterator().foreach(dbo => {
    println(grater[User].asObject(dbo))
  })

シンプルだわっ。

あえてNullをマップするとどうなるか?

  val col = DB.get().getCollection("test_collection")
  val userObj = grater[User].asDBObject(User("Azalea", null, 30))
  val tgt     = MongoDBObject("uid" -> "Azalea")

  println(col.update(tgt, userObj))

  val result = col.findOne(tgt)
  println(s"Result : $result")
  println(grater[User].asObject(result))

としたとき、

{ "serverUsed" : "/127.0.0.1:27017" , "updatedExisting" : true , "n" : 1 , "connectionId" : 3 , "err" :  null  , "ok" : 1.0}
Result : { "_id" : { "$oid" : "529319fa0cf2b37a655f3a07"} , "uid" : "Azalea" , "age" : 30}
[error] (run-main) java.lang.Exception: class models.connections.User requires value for 'provider'
java.lang.Exception: class models.connections.User requires value for 'provider'
    at com.novus.salat.DefaultArg.safeValue$lzycompute(Grater.scala:418)
    at com.novus.salat.DefaultArg.safeValue(Grater.scala:414)
    at com.novus.salat.ConcreteGrater.safeDefault(Grater.scala:375)

設定する場合はノードが削除される扱いになるらしい。

マップするときに『そんなフィールドねぇよ(#`-_ゝ-)』となる。

ちなみにCaseClassってマップできるのかなぁとか思って

  case class User(uid:String, provider:Option[String], age:Int)

  val col = DB.get().getCollection("test_collection")
  val userObj = grater[User].asDBObject(User("Azalea", None, 30))
  val tgt     = MongoDBObject("uid" -> "Azalea")

  println(col.update(tgt, userObj))

  val result = col.findOne(tgt)
  println(s"Result : $result")
  println(grater[User].asObject(result))

None で正しくマップされるっぽい

{ "serverUsed" : "/127.0.0.1:27017" , "updatedExisting" : true , "n" : 1 , "connectionId" : 4 , "err" :  null  , "ok" : 1.0}
Result : { "_id" : { "$oid" : "529319fa0cf2b37a655f3a07"} , "uid" : "Azalea" , "age" : 30}
User(Azalea,None,30)
[success] Total time: 15 s, completed 2013/11/25 18:53:13

Scala で null 使う方がイレギュラーなので、None サポートしてれば万々歳ですね。 (Kotolin は「?」入れないと null をそもそも書けないのでこれでよいかも?)