ReactiveMongo

Asynchronous IO for MongoDB



http://slid.es/tristanlohman/reactivemongo

MongoDB

  • Document (JSON)
  • High Performance
  • Scalable
  • humongous

Casbah

  • Based on Java Driver
  • Synchronous

ReactiveMongo

  • Pure Scala
  • Fully Asynchronous
  • Low level

Basics

val mongo = new MongoDriver
val connection = mymongo.connection(List("localhost"))
val db = myConnection.db("rmDemo")
val users = db.collection[BSONDocument]("users")
Plugin for Play2 framework

CRUD

  • Create
  • Read
  • Update
  • Delete

CRUD

Create

 users.insert(BSONDocument("name" -> name, "age" -> 28))
 def insert[T](document: T, ...)(...): Future[LastError]
 users.insert(BSONDocument("name" -> name, "age" -> 28))
   .map(lastError => println(lastError.ok))

CRUD

Delete

 users.remove(BSONDocument("name" -> "Tristan"))
      .map(lastError => println(lastError.ok))

CRUD

Update

users.update(
  BSONDocument("name" -> name),
  BSONDocument("$inc" -> BSONDocument("age" -> 1))
)
.map(lastError => lastError.updated)
  • Upsert
  • Multi

CRUD

Find

 val cursor =
users.find(BSONDocument("name" -> "Tristan")).cursor[BSONDocument]
:Cursor[BSONDocument]

CRUD

Find

  • cursor.toList returns a Future[List[BSONDocument]]
    • Loads the entire dataset into memory
  • cursor.iterator returns an Iterator[BSONDocument]
    • onDemand
  • cursor.enumerate returns play2 style Enumerator

BSONDocument

  • Pretty much JSON
  • Immutable
  • Access to Data
case class User(name: String, age: Int)

BSONDocumentReader[T]

BSONDocument => DomainObject
def read(bson: BSONDocument): User =
  User(
    bson.getAs[String]("name").get,
    bson.getAs[Int]("age").get)

BSONDocumentWriter[T]

Domain Object => BSONDocument
def write(user: User): BSONDocument =
  BSONDocument(
    "name" -> user.name,
    "age" -> user.age)

Domain Object

case class User(name: String, age: Int)
implicit object User
  extends BSONDocumentReader[User] with BSONDocumentWriter[User] {
  def write(user: User): BSONDocument = BSONDocument(
    "name" -> user.name,
    "age" -> user.age
  )

  def read(bson: BSONDocument): User =
    User(bson.getAs[String]("name").get,
      bson.getAs[Int]("age").get)
}

CRUD Revisited

Find
val cursor = users.find(BSONDocument("name" -> name)).cursor[User]
Insert
users.insert(User("Tristan", 28))
  .map(lastError => println(lastError.ok))
Update
users.update(user, BSONDocument("$inc" -> BSONDocument("age" -> 1)))
  .map(lastError => println(lastError.ok))

I already have JSON mappings!


Specialized Collections!

Specialized Collections

Remember
val users = db.collection[BSONCollection]("users")
Lets use Play-JSON
val users = db.collection[JSONCollection]("users")

Domian Object Mapping

Reuse existing mapping
case class User(name: String, age: Int)
implicit val format = Json.format[User]

Putting it All Together

case class User(name: String, age: Int)
implicit val format = Json.format[User]

users.insert(User(name, 28))
  .map(lastError => println(lastError.ok))

val cursor = users.find(Json.obj("age" -> 28)).cursor[User]

users.update(user, Json.obj("$inc" -> Json.obj("age" -> 1)))
  .map(lastError => println(lastError.ok))
  
users.delete(user)
  .map(lastError => println(lastError.ok))

One Json API

Does Other Stuff Good Too!

  • Stream with Iteratees
  • Tail Capped Collections
  • GridFS
  • Aggregation DSL
  • Raw Commands (so anything)

Caveats

  • Not Production Ready
    • 1.0 is just around the corner
  • Scala 2.10 only
  • Dependency on play-iteratee

The End

  • Questions?
  • Comments?
  • Rude Gestures?


ReactiveMongo

By Tristan Lohman

ReactiveMongo

Talk on ReactiveMongo, given by Tristan Lohman at the DC Scala meetup on June 5th, 2013, hosted by Audax Health

  • 3,449