Skip to content
/ MongoLess Public

Shapeless based BSON serialization for Mongo Java and Scala Drivers

License

Notifications You must be signed in to change notification settings

a14e/MongoLess

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MongoLess

Build Status codecov.io

Shapeless based BSON serialization for Mongo Java and Scala Drivers

MongoLess is a simple lib for encoding scala case classes for Mongo Java Driver. You can also use it with Scala mongo driver

#Installation

MonadLess is currently available for Scala 2.12 and Java 8. To install MongoLess just add following line to your sbt file

libraryDependencies += "com.github.a14e" %% "mongoless" % "0.3.01"

Case class to/from bson encoding

Encoding to Bson is quiet simple: just import a14e.bson.auto._ and call asBson. For decoding import a14e.bson.auto._ and call .as[...]. If you want to replace field name with _id use ID wrapper.

Simple example

import a14e.bson._
import a14e.bson.auto._

case class User(id: ID[Int],
                name: String,
                age: Int)
val exampleUser = User(
  id = 1,
  name = "some name",
  age = 25
)

val bson = exampleUser.asBson
// { "age" : 25, "name" : "some name", "_id" : 1 }
bson.as[User] == exampleUser
// true

Nested and recursive case classes are also supported

Bigger example

import a14e.bson._
import a14e.bson.auto._

case class SampleUser(id: ID[Int],
                      name: String,
                      job: Option[Job],
                      children: Seq[SampleUser])

case class Job(company: String,
               salary: Long)

val user = SampleUser(
  id = 213,
  name = "name",
  job = Some(
    Job(
      company = "some company",
      salary = 123
    )
  ),
  children = Seq(
    SampleUser(
      id = 456,
      name = "name1",
      job = None,
      children = Seq.empty
    )
  )
)

val bson = user.asBson

// { "children" : [{ "children" : [], "name" : "name1", "_id" : 456 }], "job" : { "salary" : { "$numberLong" : "123" }, "company" : "some company" }, "name" : "name", "_id" : 213 }

bson.as[SampleUser] == user
// true

Bson builder

MongoLess also offers PlayJson like builder for bson:

import a14e.bson.Bson
Bson.obj(
  "_id" -> 213,
  "name" ->  "name",
  "children" -> Bson.arr(
    Bson.obj(
      "_id" -> 456,
      "name" ->  "name1",
      "children" -> Bson.arr()
    )
  )
)
// { "_id" : 213, "name" : "name", "children" : [{ "_id" : 456, "name" : "name1", "children" : [] }] }

Search and Recursive search

MongoLess also supports helpers for search and recursive search. Use \ to search in root and \\ for recursive search of nearest node with expected key

import a14e.bson._
val bson = Bson.obj(
  "_id" -> 213,
  "name" ->  "name",
  "children" -> Bson.arr(
    Bson.obj(
      "_id" -> 456,
      "name" ->  "name1",
      "children" -> Bson.arr(),
      "someKey" -> 123
    )
  )
)
bson \\ "_id"
// Success(BsonInt32{value=213})

(bson \\ "_id").as[Int]
// 213

(bson \ "children" \\ "name").as[String]
// name1

(bson \\ "someKey").as[Int]
// 123

Enum Support

MongoLess also offers limited scala enums support. But enum should be an object and it should not be nested

import a14e.bson._
import a14e.bson.auto._
import a14e.bson.auto.enumUnsafe._


object SizeType extends Enumeration {
    type SizeType = Value
    val Big = Value("BIG")
    val Small = Value("SMALL")
}

import SizeType._

case class Hat(price: Int,
               sizeType: SizeType)
val hat = Hat(123, Big)

val bsonHat = hat.asBson
//{ "sizeType" : "BIG", "price" : 123 }
bsonHat.as[Hat] == hat
// true

ADT support

One can build ADT encoders throw BsonEncoder.switch (warning: in example lazy val can produce stack overflow)

trait Shape
case class Circle(r: Double) extends Shape
case class Rectangle(a: Double) extends Shape

import a14e.bson.encoder.BsonEncoder
import a14e.bson._

implicit val shapeEncoder: BsonEncoder[Shape] = {
    import a14e.bson.auto._
    BsonEncoder.switch[String, Shape]("type")(
      "circle" -> BsonEncoder.derived[Circle](),
      "rectangle" -> BsonEncoder.derived[Rectangle]()
    )
  }

 Circle(1).asBson // { "r" : 1.0, "type" : "circle" }
 Rectangle(1).asBson // { "a" : 1.0, "type" : "rectangle" }

and decoders in same way:

trait Shape
case class Circle(r: Double) extends Shape
case class Rectangle(a: Double) extends Shape

import a14e.bson.decoder.BsonDecoder
import a14e.bson._

implicit val decoder: BsonDecoder[Shape] = {
    import a14e.bson.auto._
    BsonDecoder.switch[String, Shape]("type")(
      "circle" -> BsonDecoder[Circle],
      "rectangle" -> BsonDecoder[Rectangle]
    )
  }


val circleBson = Bson.obj(
  "type" -> "circle",
  "r" -> 1.0
)

circleBson.as[Shape] // == Circle(1.0)

Known Issues

  • import of a14e.bson.auto._ can break encoding of BsonNull
import org.bson.BsonNull
import a14e.bson._
import a14e.bson.auto._
Bson.obj("key" -> new BsonNull()).asBson // == {"key" : {}}

workaround: move move or hide import a14e.bson.auto._

import org.bson.BsonNull
import a14e.bson._
Bson.obj("key" -> new BsonNull()).asBson // == {"key" : null}

About

Shapeless based BSON serialization for Mongo Java and Scala Drivers

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages