Reactive Scala Driver for MongoDB

Asynchronous & Non-Blocking

Overview of the ReactiveMongo BSON library

The Biːsən library of ReactiveMongo implements the BSON format, or Binary JSON, which is used by MongoDB to encode data.

The library is designed with the following points in mind:

It can be configured in a build.sbt as below.

libraryDependencies += "org.reactivemongo" %% "reactivemongo-bson-api" % "1.0.0-rc.2"

See Scaladoc

Documents and values

There is one Scala class for each BSON type, all in the reactivemongo.api.bson package.

import scala.util.Try
import reactivemongo.api.bson._

val bsonDouble = BSONDouble(12.34D)

val bsonStr = BSONString("foo")

val bsonInt = BSONInteger(2345)

// BSON array: [ 12.34, "foo", 2345 ]
val bsonArray = BSONArray(bsonDouble, bsonStr, bsonInt)

val bsonEmptyDoc: BSONDocument = BSONDocument.empty

/* BSON Document:
{
  'foo': 'bar',
  'lorem': 2,
  'values': [ 12.34, "foo", 2345 ],
  'nested': {
    'position': 1000,
    'score': 1.2
  }
}
*/
val bsonDoc = BSONDocument(
  "foo" -> "bar", // as BSONString
  "lorem" -> 2, // as BSONInteger
  "values" -> bsonArray,
  "nested" -> BSONDocument(
    "position" -> 1000,
    "score" -> 1.2D // as BSONDouble
  )
)

val bsonBin = BSONBinary(Array[Byte](0, 1, 2), Subtype.GenericBinarySubtype)
// See Subtype

val bsonObjectID = BSONObjectID.generate()

val bsonBool = BSONBoolean(true)

val bsonDateTime = BSONDateTime(System.currentTimeMillis())

val bsonRegex = BSONRegex("/foo/bar/", "g")

val bsonJavaScript = BSONJavaScript("lorem(0)")

val bsonJavaScriptWs = BSONJavaScriptWS("bar", BSONDocument("bar" -> 1))

val bsonTimestamp = BSONTimestamp(45678L)

val bsonLong = BSONLong(Long.MaxValue)

val bsonZeroDecimal = BSONDecimal.PositiveZero

val bsonDecimal: Try[BSONDecimal] =
  BSONDecimal.fromBigDecimal(BigDecimal("12.23"))

val bsonNull = BSONNull

val bsonMinKey = BSONMinKey

val bsonMaxKey = BSONMaxKey

Documents

A document is represented by BSONDocument, which is basically an immutable list of key-value pairs. Since it is the most used BSON type, one of the main focuses of the ReactiveMongo BSON library is to make manipulations of BSONDocument as easy as possible.

import reactivemongo.api.bson._

/* Document: {
  'title': 'Everybody Knows this is Nowhere',
  'releaseYear': 1969
} */
val album = BSONDocument(
  "title" -> "Everybody Knows this is Nowhere",
  "releaseYear" -> 1969)

val albumTitle = album.getAsOpt[String]("title")

albumTitle match {
  case Some(title) =>
    println(s"The title of this album is $title")

  case _           =>
    println("""This document does not contain a title 
(or title is not a BSONString)""")
}

The API for BSONDocument provides the function getAsOpt and getAsTry to get the field values, and getOrElse.

import scala.util.Try
import reactivemongo.api.bson._

def bar(doc: BSONDocument): Unit = {
  val i: Option[Int] = doc.getAsOpt[Int]("fieldNameInt")
  val d: Option[Double] = doc.getAsOpt[Double]("fieldNameDouble")
  val l: Try[Long] = doc.getAsTry[Long]("fieldNameLong")
  val s: Try[String] = doc.getAsTry[String]("fieldNameStr")

  val fallback: String = doc.getOrElse[String]("strField", "defaultValue")
  // Equivalent to: doc.getAsOpt[String]("strField").getOrElse("defaultValue")
}

Field utilities are provided for the most common types:

import reactivemongo.api.bson._

def foo(doc: BSONDocument): Unit = {
  val i: Option[Int] = doc.int("fieldNameInt")
  val d: Option[Double] = doc.double("fieldNameDouble")
  val l: Option[Long] = doc.long("fieldNameLong")
  val s: Option[String] = doc.string("fieldNameStr")
  val a: Option[Seq[BSONValue]] = doc.array("fieldNameArr")
  val b: Option[Boolean] = doc.booleanLike("fieldNameBool")
  val c: Option[BSONDocument] = doc.child("nestedDoc")
  val _: List[BSONDocument] = doc.children("arrayOfDocs")
}

Numeric & Boolean values

The traits BSONNumberLike and BSONBooleanLike are also kept in the new API, to generalize the handling of numerical and boolean values.

import scala.util.Try
import reactivemongo.api.bson._

val doc = BSONDocument("ok" -> 1.0D /* BSON double */ )

val bsonNumLike: Try[BSONNumberLike] = doc.getAsTry[BSONNumberLike]("ok")
val intLike: Try[Int] = bsonNumLike.flatMap(_.toInt) // =Success(1)

val bsonBoolLike: Try[BSONBooleanLike] = doc.getAsTry[BSONBooleanLike]("ok")
val boolLike: Try[Boolean] = bsonBoolLike.flatMap(_.toBoolean) // =Success(true)

Now Float is handled as a BSON double (as Double, as it’s now possible to have several Scala types corresponding to the same BSON type).

The Decimal128 type introduced by MongoDB 3.4 is also supported, as BSONDecimal, and can be read or write as java.math.BigDecimal.

import scala.util.Try
import reactivemongo.api.bson._

def readFloat(
  doc: BSONDocument,
  n: String
)(implicit r: BSONReader[Float]): Try[Float] = doc.getAsTry[Float](n)

Still to make the API simpler, the BSON singleton types (e.g. BSONNull) are also defined with a trait, to be able to reference them without .type suffix.

import reactivemongo.api.bson.BSONNull

def useNullBefore(bson: BSONNull.type) = println(".type was required")

def useNullNow(bson: BSONNull) = print("Suffix no longer required")

Binary values

The BSONBinary extractor now only bind subtype:

import reactivemongo.api.bson.{ BSONBinary, Subtype }

def binExtractor = {
  BSONBinary(Array[Byte](0, 1, 2), Subtype.GenericBinarySubtype) match {
    case genBin @ BSONBinary(Subtype.GenericBinarySubtype) =>
      genBin.byteArray

    case _ => ???
  }
}

Miscellaneous

Summary

BSON Description JVM type
BSONBinary binary data Array[Byte]
BSONBoolean boolean Boolean
BSONDateTime UTC Date Time java.util.Date
BSONDecimal Decimal128 java.math.BigDecimal
BSONDouble 64-bit IEEE 754 floating point Double
BSONInteger 32-bit integer Int
BSONJavaScript JavaScript code None
BSONJavaScriptWS JavaScript scoped code None
BSONLong 64-bit integer Long
BSONMaxKey max key None
BSONMinKey min key None
BSONNull null None
BSONObjectID 12 bytes default id type in MongoDB None
BSONRegex regular expression None
BSONString UTF-8 string String
BSONSymbol deprecated in the protocol None
BSONTimestamp special date type used in MongoDB internals None
BSONUndefined deprecated in the protocol None

All these classes extend BSONValue.

Next: The readers and writers

Suggest changes