Working with Case classes and MongoDB in Scala

in Big DataScala

MongoDB is the most popular document database in the wild. It provides one of the main features that most of the developers want when they work with a DB, seamless write and read of objects from the DB, passing the driver the responsibility to marshal and unmarsnal the objects (using BSON representation). I had the pleasure to work with MongoDB in Scala using the Casbah library in the past, it has a nice API for encoding objects to BSON and decoding them back, but MongoDB deprecated this library. MongoDB realized that having Casbah as a wrapper for the Java library is not enough so they released an official Scala driver that is async with Scala idiomatic API.

However, till version 2.0 it lacked the main feature that developers want MongoDB for – writing an object to the DB. Version 2.0 introduced support for writing case class instances to the DB by using Scala macros.

This is how you do it:
We start from defining a simple case class – Person:

object Person {
  def apply(firstName: String, lastName: String): Person = Person(new ObjectId(), firstName, lastName);
}
case class Person(_id: ObjectId, firstName: String, lastName: String)

Then you need to specify the driver how to convert your case class to a Mongo Document:

import org.mongodb.scala.bson.codecs.Macros._
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries.{ fromRegistries, fromProviders }
val codecRegistry = fromRegistries(fromProviders(classOf[Person]), DEFAULT_CODEC_REGISTRY)

This code creates a Codec – the MongoDB terminology for a class that can encode and decode case class to BSON. The Codec class is generated during the compile time and then added to the default codec registry (using the fromRegistries method).
The codec registry holds the codecs that will be used to encode/decode case classes to and from BSON documents.

Now we are ready to read and write Person objects from MongoDB:

// Create the client
val mongoClient: MongoClient = if (args.isEmpty) MongoClient() else MongoClient(args.head)

// get handle to "mydb" database
val database: MongoDatabase = mongoClient.getDatabase("mydb").withCodecRegistry(codecRegistry)

// get a handle to the "test" collection
val collection: MongoCollection[Person] = database.getCollection("test")

val totalItemsInCollection = for {
  a1 <- collection.insertOne(Person("Charles", "Babbage"))
  a2 <-  collection.insertOne(Person("George", "Boole"))
  inCollection <- collection.count()
} yield inCollection

val res = Await.result(totalItemsInCollection.toFuture(), 10 seconds)
res.foreach(print)

// get the first person
collection.find.first().printResults()

First, we create a connection to the DB using the coded registry we defined earlier.
Then we define a collection of Person. This enables us to insert and query Person objects.

More detailed information can be found in MongoDB official documentation at https://docs.mongodb.com/ecosystem/drivers/scala/ .
The examples were based on MongoDB’s official example at https://github.com/mongodb/mongo-scala-driver

Contact us
You might also like
Share: