MongoDB Synchronous Scala Driver

MongoDB Synchronous Scala Driver (mssd) is based on the plain old legacy mongodb java driver and adds some Scala friendliness to it. It's similar to Casbash but with a different api which was inspired by the Play2 JSON API for the bson documents and by the mongodb-async-driver for it's general API.

This driver is work in progress and not complete yet. Please feel free to contribute. You can find the Github project page here. For general discussion and help there is a google group for that.

Current Version

Current version is 0.1.14

Installation

To use it in your sbt based project:

libraryDependencies ++= Seq(
    "com.github.mssd" %% "mssd" % "0.1.14"
)

resolvers += "fab mvn releases" at "https://github.com/fkoehler/fkoehler-mvn-repo/raw/master/releases/"

or if you are more into build.sbt style sbt syntax:

libraryDependencies += "com.github.mssd" %% "mssd" % "0.1.14"

resolvers += "fab mvn releases" at "https://github.com/fkoehler/fkoehler-mvn-repo/raw/master/releases/"

Some simple use cases

In order to get a collection to work on you setup the driver like shown below. Of course you can specify any normal options. There might not be a wrapper for everything yet.

val client = MongoClient("hostname", 27017,
    new MongoClientOptions.Builder()
        .autoConnectRetry(true)
        .maxAutoConnectRetryTime(30000)
        .threadsAllowedToBlockForConnectionMultiplier(1000)
        .connectionsPerHost(1000)
        .build())

val db = client("dbname")
val coll = db("collectionname")

Save a doc, get it back and access some values

val simpleDoc = Bson.doc("_id" -> 1, "name" -> "mssd")
coll.insert(simpleDoc)
val docOpt = coll.findOneById(1)  // => Option[BsonDoc]

for(doc <- docOpt)
  println(doc.as[String]("name))

More complex find

coll.save(Bson.doc("_id" -> 80, "name" -> "fabian"))

val cursor = Find()
    .query(Bson.doc("name" -> "fabian"))
    .limit(1)
    .key(Bson.doc("name" -> 1))
for(doc <- cursor)
  println(doc.as[String]("name"))

Using case classes for mapping

There is no magic involved here. You have to manually define implicit converters which convert either a BsonElement or a BsonDoc into whatever you want. This was a clear decision in the beginning that we wanted to have manually mappings as we tend to define short keys (to save space) in our docs or do other conversion stuff.

Define a case class which we want to map

case class DnsRecord(host: String = "", ttl: Long = 0, otherProps: Map[String, String] = Map())

case object DnsRecord {

  implicit object DnsRecordToBsonElement extends ToBsonElement[DnsRecord] {
    def toBson(v: DnsRecord): BsonElement = DnsRecordToBsonDoc.toBson(v)
  }

  implicit object DnsRecordFromBsonElement extends FromBsonElement[DnsRecord] {
    def fromBson(v: BsonElement): DnsRecord = DnsRecordFromBsonDoc.fromBson(v.asInstanceOf[BsonDoc])
  }

  implicit object DnsRecordFromBsonDoc extends FromBsonDoc[DnsRecord] {
    def fromBson(d: BsonDoc): DnsRecord = DnsRecord(
      d[String]("host"),
      d[Long]("ttl"),
      d[Map[String, String]]("op")
    )
  }

  implicit object DnsRecordToBsonDoc extends ToBsonDoc[DnsRecord] {
    def toBson(m: DnsRecord): BsonDoc = Bson.doc(
      "host" -> m.host,
      "ttl" -> m.ttl,
      "op" -> m.otherProps
    )
  }

}

Save and restore it from the db

coll.save(DnsRecord("test.de", 4456, Map("p2" -> "val1")))
for (r <- coll.findAs[DnsRecord](Bson.doc("host" -> "test.de")))
  println(r.host)

Please have a look in the source code and tests for more details. It's not too hard to understand I guess as it basically wraps over the existing driver.