ScalaでMongoその2(Casbahの使い方)

この前の続き
前は単なるMongoDBの紹介みたいになってしまったので、今回はもう少しCasbahの具体的な使い方を書こうかと思います。
scalaは2.8.1、casbahは2.1.2で説明します。この記事を書いている時点での最新版です。

まず、sbtとかMavenとか使って、ライブラリを使用できる状態にセットアップしてください。

自分は、Maven使ってるのですが、その際に公式ページのTutorial通りに、

<dependency>
    <groupId>com.mongodb.casbah</groupId>
    <artifactId>casbah_2.8.1</artifactId>
    <version>2.1.2</version>
<dependency>

って書いても使えなくて、はぁ?( ゚д゚ )とか思ってたら、どうやら

<dependency>
    <groupId>com.mongodb.casbah</groupId>
    <artifactId>casbah_2.8.1</artifactId>
    <version>2.1.2</version>
    <type>pom</type>
</dependency>

っていうように、リポジトリにpomしか置いてない場合にはtypeをpomで指定しないといけないらしいです。

おそらく、必ず使うことになるのが、

の4つのclassです。まずMongoConnectionのコンパニオンオブジェクトのapplyメソッドで、MongoConnectionクラスを生成します。引数なしだと、デフォルトのlocalhostでport27017につなぎにいくようです。
他には、明示的にhostやport番号を指定するもの、optionを指定するものがあるようです。
Casbah使っていく上での注意点として、前回も書きましたがCasbah自体はJavaのライブラリのラッパーなので、Javaのclassを直接使用する場合もあります。たとえば、MongoConnectionのインスタンス作成する場合で、Option指定する場合、MongoConnectionのオーバーロードされたapplyの中で、MongoOptionsというclassを引数にとるメソッドがありますが、これのScalaのラッパーは無いようなので直接

val opt = new MongoOptions()
opt.autoConnectRetry = true
val con = MongoConnection("localhost",opt)

というように

って、式が3つ必要でめんどくさい感じに・・・(´・ω・`)

これはJavaのライブラリを使う上ではよくあることなのでしょうがないのですが。

追記: すいませんHelperはあるみたいです。
http://api.mongodb.org/scala/casbah/2.1.2/scaladoc/com/mongodb/casbah/MongoOptions$.html
applyに、すべてデフォルト値が定義されているので、カスタマイズしたいものだけ、パラメータの名前を指定して

MongoOptions( autoConnectRetry = true )

というように使うみたい。

次に、MongoDBクラスの生成ですが、今度は生成したMongoConnectionのインスタンスに、applyメソッドがあるので、それにDatabase名を渡します。

val db = con("mongoTestDB")

Mongoにはスキーマが存在せず、必要になったときにDatabaseもcollectionも作成されるので、上記のように呼び出したとき、mongoTestDBという名前のdatabaseが存在してもしなくても、エラーにはなりません。
そして、上記のコードを実行した時点ではmongoTestDBというdatabase自体も作成されません。


そして、次にMongoCollectionの作成ですが、今度はMongoDBのインスタンスのapplyメソッドを呼びます。

val collection = db("test")

これも、MongoDBのインスタンスの作成と同様に、MongoCollectionのインスタンスを生成した時点では、実際にDBにその名前のcollectionがあってもなくても、エラーは起きません。

MongoDBのインスタンスの作成や、MongoCollectionのインスタンスの作成はapply呼ぶ仕様になってるのはscalaらしくて便利ですねヽ(´ー`)ノ

このMongoCollectionがいわゆるRDBMのテーブルにあたるものなので、あとはこのMongoCollectionのclassに対して、

  • find
  • insert
  • update

などのメソッドを呼んで、データの操作を行います。このMongoCollectionのインスタンスがdatabaseへの参照を保持しているので、insertやupdateのメソッドを呼んだ際には、そのタイミングですぐにdatabaseに反映されます。


このMongoCollectionのscaladocをみると、だいぶメソッドの数が多いのですが、MongoCollection自体がScalaのcollectionのclassを継承しています!よって、いろいろな操作ができて便利です*1

そして、findやinsertなどに渡すのが、MongoDBObjectというクラスのインスタンスです。

このMongoDBObjectのインスタンスの生成方法ですが、このへんで、

  val MongoDBObject = com.mongodb.casbah.commons.MongoDBObject
  val DBObject = MongoDBObject

というaliasが定義されているので、実際のclassの名前はMongoDBObjectですが、以下のようにDBObjectという名前で使用できます。

val obj = DBObject("java" -> "gosling" , "scala" -> "odersky" )

そして、たとえばinsertする場合ならば、いま生成したMongoDBObjectのインスタンスをinsertの引数に渡すだけです。

  collection.insert(obj)

それと、MongoDBObject自体を生成する際の仕組みですが、MongoDBObject.scalaで以下のようにapplyが定義されています。

  def apply[A <: String, B <: Any](elems: (A, B)*): DBObject = (newBuilder[A, B] ++= elems).result
  def apply[A <: String, B <: Any](elems: List[(A, B)]): DBObject = apply(elems: _*)

Mongoに保存できるのはJson形式*2のオブジェクトなので、KeyとValueの形式をしていなければならず、そしてKeyはStringでなければいけません。最終的にdatabaseに保存する際にはScalaのStringは、自動的にJavaScriptのStringに変換されます。

とりあえず、最低限の使い方を説明してみました。なんだか中の仕組みまで説明していたら結局たいして詳しいこと書けてないェ・・・


次回*3は、

$inc ("foo" -> 5)

とかのScalaらしいDSLっぽく書ける部分を説明しようかと思います(´・ω・`)

*1:がしかし、REPLでの補完とかのときなど、scalaのcollectionから継承したメソッド以外のメソッドを探すのに不便 なんてことはないです!

*2:とその他数種類

*3:があるかどうかは気分次第だが