2011年2月15日火曜日

Lift用Scala DSL"Rogue"を使ってMongoDBにアクセス(1)~概要編~

実際に利用しはじめたので少しずつメモ。
今日はまず最初ということで
  1. Rogueの説明
  2. 静的型付け言語とMongoDB
  3. net.liftweb.mongodb.recorddbの問題
  4. タイプセーフにMongoDB
あたりを書いていこうと思います。

Scala DSL"Rogue"について

位置情報サービスのFourSquareが公開したMongoDB用のScala DSL。同社はLiftのユーザ企業としても有名でかなりノウハウが蓄積しているはずなのでとても期待できそうです。
詳細な↓の記事にありますが、タイプセーフにMongoDBのクエリを操作できることが特徴のようです。

Rogue: A Type-Safe Scala DSL for querying MongoDB

GitHub:foursquare / rogue

静的型付け言語とMongoDB

利用を始める前に、そもそもScalaからMongoDBを利用するのってどうなの?という話から。
ある程度MongoDBを理解しているという前提で書いていきますが、MongoDBの特徴としてスキーマレスであることが挙げられます。データはJSONに似たBSONで格納されておりとても自由にデータを格納していけます。

これをRuby、PHP、JavaScript等の動的型付け言語から使うぶんには非常に楽なのですが、静的型付けの場合なんらかの型に変換しないといけません。

Liftの場合、標準で利用できる方法として
  1. JavaのMongoDBライブラリを利用
  2. MongoDocumentを利用してオブジェクトにマッピング
  3. MongoRecordを利用してオブジェクトにマッピング(Rogueで利用)
があり、Rogueは3をベースに利用しています。

実際に使っていて感じるのは、やはりユーザーに提供する部分ではスキーマレスの自由さは一長一短だなということ。使いやすくはあるのですがなんでもありなのでテストや検証に結構手間がかかります。

その点、Scalaのオブジェクトにマッピングしてしまえばコンパイル時にほとんどの問題を解決してくれるので意外と相性いいんじゃないの?という印象。Javaから使うと泣ける気はしますが。

しかし、LiftのMongoRecordにはとんでもない問題があります。。。

net.liftweb.mongodb.recordの問題

結論からいうと、createとかfindしてとってきた後のアクセスはタイプセーフなのにfind用クエリやupdate用クエリがタイプセーフにならないLiftwebのWiki(http://www.assembla.com/wiki/show/liftweb/lift-mongodb-record)に掲載されている次のサンプルコードをご覧ください。

class ArticleDoc private() extends MongoRecord[ArticleDoc] with MongoId[ArticleDoc] {
def meta = ArticleDoc
object name extends StringField(this, 20)
object price extends DoubleField(this)
}
object ArticleDoc extends ArticleDoc with MongoMetaRecord[ArticleDoc] {
override def collectionName = "articles"
}
ArticleDoc.createRecord.name("milk").price(25).save
import net.liftweb.json.JsonDSL._
ArticleDoc.findAll("name" -> "milk")
ArticleDoc.findAll("price" -> ("$gte"->25))
ArticleDoc.findAll(("name" -> "milk")~("price" -> ("$gte"->25)))

最後の三行、findAllの引数がひどいですね。JsonをDSLで書いてるだけなのでまったくタイプセーフじゃありません。うっかりタイポして変な値が入る可能性がありそうです。
これじゃ書くのがめんどくさいだけの存在になってしまいます。Recordはタイプセーフなのでそこそこましではあるでしょうが・・・。

タイプセーフにMongoDB

そこでScala DSL"Rogue"の出番です。Rogueを使うとfindやupdateのクエリをタイプセーフに書くことができます。きっとFoursquareの中の人も上記MongoRecordの仕様に憤慨したんでしょう・・・。

先ほどのfindAllをRogueを使って書き直すとこうなります。

ArticleDoc where ( _.name eqs "milk" ) fetch
ArticleDoc where ( _.price >= 25 ) fetch
ArticleDoc where ( _.name eqs "milk" ) and ( _.price >= 25 ) fetch
この書き方だと存在しないプロパティを指定するとコンパイルエラーになります。あわせて記述もすっきりするのでいいことづくめですね。

LiftでMongoDBを利用する場合、今後はRogueを併用するのがよさそうです。

今後の予定

  • Rogue導入
  • MongoRecordの定義
  • Rogueの各種機能
あたりを順次書いていくつもり。

0 件のコメント:

コメントを投稿