Type safe mongodb with Scala #montotokyo

  • 6,605 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • 参考になりました。
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
6,605
On Slideshare
0
From Embeds
0
Number of Embeds
5

Actions

Shares
Downloads
0
Comments
1
Likes
11

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Type-safe MongoDB in Scala - 静的型付け言語とMongoDBの付き合い方 -Fungoing LLC / Satoshi MiyauchiTwitter : @bibrost
  • 2. Profile 宮内 聖 / Satoshi Miyauchi @bibrost フゔンゴー゗ング合同会社代表 兼 株式会社監査と分析 システム開発担当 Webデザ゗ン、映像編集、Webサービスの開発など・・・いろいろ 引きこもりフリーランス生活 Scala+Lift+MongoDBの組み合わせを昨年11月より利用中。 自社サービス1つ、クラ゗ゕントワーク1つでScala+MongoDBを利用。 Page : 1
  • 3. Case Example 勝間和代氏の提供する有料コンテンツ会員向けコミュニテゖKatsumaweb.net Scala2.8+Lift2.2+MongoDB1.6+Rogueで開発 Page : 2
  • 4. Outline 1. 静的型付けについてのお話 2. 静的型付け言語とMongoDBの付き合い方 3. Scala+Rogueを使った実装 4. Katsumaweb.netのコレクション構造 5. まとめ Page : 3
  • 5. 1. 静的型付けについてのお話 Page : 4
  • 6. そもそも静的型付けってどうなの・・・?いちいち宣言するの面倒だし柔軟じゃなさそう Page : 5
  • 7. そんなことないよ Scalaなど、型推論がある言語なら書くのも楽! 柔軟でないかわりのメリットもあるから 総合的な生産性は十分です。 ただ、僕Java(言語)は嫌いです。 ※Haskell、F#なんかだといいのかも? Page : 6
  • 8. メリット 変なことが起きない コンパ゗ル時に型が決まるのでデプロ゗された コードで予期しないことが起きることが少ない。 バグがあっても追跡しやすい・・・と感じる。 Page : 7
  • 9. 例 ) トイレの実装で考えてみる 男子ト゗レ、女子ト゗レ、共用ト゗レ をたとえにして、型に意味を持たせる プログラミングと静的型付けの意義 を説明していきます。 Page : 8
  • 10. Human class abstract class Human class Man extends Human class Woman extends Human Human Man Woman Page : 9
  • 11. Toilet class & trait trait Toilet[A <: Human]{ def enqueue( in:A ) = “ok” } class MenToilet extends Toilet[Man] class WomenToilet extends Toilet[Woman] class AnyToilet extends Toilet[Human] Toilet Men Women Any Toilet Toilet Toilet Page : 10
  • 12. 期待する動作 MenToilet.enqueue(Man) = OK WomenToilet.enqueue(Woman) = OK AnyToilet.enqueue(Man) = OK AnyToilet.enqueue(Woman) = OK MenToilet.enqueue(Woman) = NG WomanToilet.enqueue(Man) = NG Page : 11
  • 13. 型の定義だけでプログラムを制御可 静的型付け言語なら、最初の定義(9行) だけでコンパ゗ラが型チェックを 行うため間違ったト゗レに入って しまう事はない。 これが動的型付け言語だと、 実行時に何らかのチェックをしないと 安全にならない。 Page : 12
  • 14. Compile errorの例 女子ト゗レに男が入ろうとする (new WomenToilet).enqueue(new Man) <console>:11: error: type mismatch; found : Man required: Woman (new WomenToilet).enqueue(new Man) ^ Page : 13
  • 15. Child mix-in : 子供ならOKとか abstract trait Child class WomenToilet extends Toilet[Woman]{ def enqueue( in:Child ) = “ok” } // NG (new WomanToilet).enqueue(new Man) // OK (new WomanToilet).enqueue(new Man with Child) Page : 14
  • 16. コンパ゗ルが通らない=デプロ゗不可なので間違ったト゗レに入りうる状態のまま運用されるリスクが減る。 Page : 15
  • 17. 動的型付けだと。。。// 事前にチェック(トイレの前に見張り式)if( $person instanceof Women ) ){ $womenToilet->enqueue( $person );}// 例外処理(間違って入ってきたらつまみ出す式)try { $womenToilet->enqueue( $person );}catch( GenderMismatchException $e ){ // ~}// 何もしない(善意に任せる式)$womenToilet->enqueue( $person ); Page : 16
  • 18. 静的型付けで書けば不毛なコードやケゕレスミスが発端のバグを減らし楽に安全な運用ができるんじゃな゗カ? Page : 17
  • 19. イメージINPUT 解答 型と関数を使って、入力から望む解答が導かれる回路を 構築していく゗メージ。各ポ゗ントで型が制限されるので 想定外のことが起きるケースが少なくなります Page : 18
  • 20. 2. 静的型付け言語とMongoDBの付き合い方 Page : 19
  • 21. Q. 静的型付け言語だとMongoDBの特徴を 最大限に使う事はできない? Page : 20
  • 22. Yesこれは仕方ありません。しかし静的型付け言語ならではの使い方があります。 Page : 21
  • 23. “Type-safe MongoDB” = Persistence for Model Class 「モデルクラスの永続化」に用途を絞ると 静的型付け言語からいい感じにMongoDBが使えます。 ゕプリ側だけでほぼ定義が簡潔し、柔軟に使えて 強力なクエリ機能もありとても使いやすい。 そして何より安全です。 Page : 22
  • 24. メリット 1.コンパ゗ル時に型チェックされる安全なオブジェクトを 手軽に永続化できる。 2.ゕプリケーション側だけにモデル定義を書けばよい。 O/Rマッパでも近いことはできるがRDBのスキーマが なくなるわけではないので裏側を気にしないといけない 3.MongoDBのクエリを使って検索ができる ※KVS、フゔ゗ル、単純なシリゕラ゗ズ等だとゕプリ側で対応が必要 4.プロパテゖを増やす時のコストが少ない ※RDBのO/Rマッパで構成変更をかけると Alter Tableが必要になって再構築コストがひどい。 Page : 23
  • 25. モデルクラスの永続化手段と割り切ってMongoDBを使えばいい感じになりました Page : 24
  • 26. 3. Scala+Rogueを使った実装 Page : 25
  • 27. 利用するもの Scala 2.8.x Lift 2.2 (Rogueが2.2以上を要求) ※Lift=Scala用のWebフレームワーク Rogue ※Foursquare社が公開したLift用のクエリ生成DSL Page : 26
  • 28. ライブラリの構造 Record MetaRecord Field MongoRecord MongoMetaRecord xxxField ModelClass PropertyO/Rマッパと似た機能を提供するMongoRecordトレ゗トを利用。各種Fieldクラスを継承したPropertyを組み合わせてModel制作 Page : 27
  • 29. MongoRecord ゗ンスタンスをドキュメントへマッピングするための機能を提供。 ※createRecord,saveなどclass Model extends MongoRecord[Model] with MongoId[Model]{ def meta = Model object count extends LongField(this) object name extends StringField(this) object create_at extends DateTimeField(this) object labels extends MongoListField[Model,String](this) object refUser extends DBRefField[Model, User](this, User) object refUserId extends ObjectIdField(this){ def fetch = User.find(value) }} Page : 28
  • 30. MongoMetaRecordfind、updateなどコレクション全体へのゕクセスを提供。Rogueを使う場合隠蔽されるので直接使うケースは少ない。 objetc Model extends Model with MongoMetaRecord[Model]{ }おまじない的に↑のコードを書いておけばOK Page : 29
  • 31. Field ObjectIdField, StringField, LongField, DateTimeField MongoListField, MongoMapField など、データ型に応じた クラスを継承して利用する。 object longParam extends LongField(this){ override def defaultValue = 0 override def validations = … } のように初期値やValidation ruleの設定も可能。 Page : 30
  • 32. Insert Documentval model = Model.createReocrd // ゗ンスタンスの生成 model.count(0)// model.count(“0”) <- compile error model.name(“miyauchi”)// model.name(0) <- compile error model.save // MongoDBに保存 createRecordで゗ンスタンスを設定し、各種プロパテゖを 設定したうえでsaveするだけ。 Page : 31
  • 33. Rogue を利用した Findimport com.foursquare.rogue.Rogue._// 単純なfindAllval data = Model where ( _.count > 0 ) fetch// List[Model] が返ってくる。形推論があるため型宣言を省略している。//val data = Model where ( _.conut > 0 ) fetch <- compile error//val data = Model where ( _.count > “0” ) fetch <- compile error// skip と limit を 使ったクエリーval data2 = Model where ( _.count > 0 ) skip(0) fetch(10)// 配列内の要素をサーチするクエリーVal data3 Model where ( _.count > 0 ) and ( _.labels contains “label”) fetch Page : 32
  • 34. Rogue を利用した updateimport com.foursquare.rogue.Rogue._Model where ( _._id eqs ObjectId(“xxxx”) ) modify ( _.count inc 1 ) updateOnedb.models.update({_id:”xxxx”},{$inc:{count:1}}) こちらもコンパ゗ラが型チェックしてくれるので安全。 Page : 33
  • 35. 4.katsumaweb.netのコレクション構造 Page : 34
  • 36. Collections User 1 id 1 name image 1 1 1 1 Post Comment id 1 n id author author [tags,,,] parentid 1 1 n n Bookmark Like 1 id id 1 user user parentid parentid Page : 35
  • 37. Get Feedsval post = Post where (_.enabled eqs true) orderDesc(_.update_at) skip(cur) fetch(pageSize)val user = post.refUser.getval comments = post.comments/* * Comment where ( _.parentId eqs new ObjectId(post._id) ) orderAsc(_.comment_at) fetch * が定義されているだけ */ Post Comment User id id id author author name [tags,,,] parentid image Post Comment User Post Comment User Post User Post id name Post image Page : 36
  • 38. Get Bookmarked Feedsdef getFeed( userId:ObjectID, cur:Int, pageSize:Int ) = { val bm = Bookmark where (_.refUserId eqs userId ) orderDesc(_.post_at) skip(cur) fetch(pageSize) bm.flatMap( bm => { Post where (_._id eqs bm.refPostId.is ) fetch(1) })}//といった形でBookmarkのリストからPostのリストを取得する Bookmark Post Comment User id id id id owner author author name parentid [tags,,,] parentid image Bookmark Post Comment User Bookmark Post Comment User Bookmark Post User Bookmark Post id name Bookmark Post image Page : 37
  • 39. 使っていてのいろいろ・あまりEmbedをしてないのでクエリの回数がかなり多い。 一度にまとめて読み込まない&Ajaxで細かく処理するなど無駄な クエリが出ないように工夫する予定。・今のところ速度面で大きな問題は出ていない。 ただ大勢が使うサービスではない事もある。 ※Amazon EC2のLarge Instanceを利用・ゕドホックな機能追加やプロパテゖ追加が多いので、 実装のうえでは非常に楽になっている。RDBではやりたくないタ゗プの仕事。 Page : 38
  • 40. 5.まとめ Page : 39
  • 41. 1.静的型付け言語も楽しいよ! → ただしScala、Haskell等の型推論できる言語に限る2.モデルクラスの永続化に特化して使うとナ゗ス → 本気で柔軟さがほしいときは別の言語使おう3.Scala+Lift+Rogue使うと型チェックもよしなに → 記述も単純。欠点はScala+Lift専用なこと・・・私はいいんですが。4.ゕドホックな機能調整が頻発する開発に便利 → コンシューマ向けのコミュニケーションサービスとか。 foursquare自体がまさにそのもの Page : 40
  • 42. ありがとうございました。Satoshi Miyauchi @bibrost Page : 41