Reactive Scala Driver for MongoDB
Asynchronous & Non-Blocking
BSON readers and writers
In order to get and store data with MongoDB, ReactiveMongo provides an extensible API to define appropriate readers and writers.
Getting values follows the same principle using
getAs(String) method. This method is parametrized with a type that can be transformed into a
BSONValue using a
BSONReader instance that is implicitly available in the scope (again, the default readers are already imported if you imported
reactivemongo.bson._.) If the value could not be found, or if the reader could not deserialize it (often because the type did not match),
None will be returned.
BSONReader for a custom value class:
When reading a numeric value from MongoDB, it’s recommended to use the typeclass
BSONNumberLike, to benefit from numeric conversions it provides.
Once a custom
BSONDocumentReader) is defined, it can be used in
BSONDocumentReader for a custom case class:
Once a custom
BSONDocumentReader can be resolved, it can be used when working with a query result.
Each value that can be written using a
BSONWriter can be used directly when calling a
Note that this does not use implicit conversions, but rather implicit type classes.
BSONDocumentWriter is available, an instance of the custom class can be inserted or updated to the MongoDB.
To ease the definition of reader and writer instances for your custom types, ReactiveMongo provides some helper Macros.
A type parameter (e.g. with
Macros.reader[Person]) defines a type for a case class, or for a sealed trait with subclasses.
This type will be the basis for the auto-generated implementation.
Some other types with matching
unapplymight work but behaviour is undefined. Since macros will match the
unapplypair you are free to overload these methods in the companion object.
Case class mapping:
For the case classes, the fields get mapped into BSON properties with respective names, and BSON handlers are pulled from implicit scope to (de)serialize them (in the previous
Person example, the handlers for
String are resolved for the
So in order to use custom types as properties in case classes, the appropriate handlers are in scope.
For example if you have
case class Foo(bar: Bar) and want to create a handler for it is enough to put an implicit handler for
Bar in it’s companion object. That handler might itself be macro generated, or written by hand.
The macros are currently limited to case classes whose constructor doesn’t take more than 22 parameters (due to Scala not generating
unapplyin the other cases).
Sealed trait and union types:
Sealed traits are also supported as union types, with each of their subclasses considered as a disjoint case.
These ‘Opts’ suffixed macros can be used to explicitly define the
As for the
Foo \/ Bar \/ Baz is interpreted as type
Foo or type
Bar or type
Baz. The option
AutomaticMaterialization is used there to automatically try to materialize the handlers for the sub-types (disabled by default).
The other options available to configure the typeclasses generation at compile time are the following.
Verbose: Print out generated code during compilation.
SaveClassName: Indicate to the
BSONWriterto add a “className” field in the written document along with the other properties. The value for this meta field is the fully qualified name of the class. This is the default behaviour when the target type is a sealed trait (the “className” field is used as discriminator).
Some annotations are also available to configure the macros.
@Key annotation allows to specify the field name for a class property.
For example, it is convenient to use when you’d like to leverage the MongoDB
_id index but you don’t want to actually use
_id in your code.
@Ignore can be applied on the class properties to be ignored.
The mapped type can also be defined inside other classes, objects or traits but not inside functions (known macro limitation). In order to work you should have the case class in scope (where you call the macro), so you can refer to it by it’s short name - without package. This is necessary because the generated implementations refer to it by the short name to support nested declarations. You can work around this with local imports.
The following handlers are provided by ReactiveMongo, to read and write the BSON values.
|BSON type||Value type|
An optional value can be added to a document using the
Option type (e.g. for an optional string,
BSONBooleanLike, it is possible to read the following BSON values as boolean.
BSONNumberLike, it is possible to read the following BSON values as number.
BSONDateTime: the number of milliseconds since epoch.
BSONTimestamp: the number of milliseconds since epoch.
When using the compiler option
-Ywarn-unused and the BSON macro (e.g.
Macros.handler), you can get a warning as bellow. It can be safely ignore (there for compatibility).
private val in <$anon: reactivemongo.bson.BSONDocumentReader[foo.Bar] with reactivemongo.bson.BSONDocumentWriter[foo.Bar] with reactivemongo.bson.BSONHandler[reactivemongo.bson.BSONDocument,foo.Bar]> is never used