greenleafoss / green-leaf-mongo   0.1.15

Apache License 2.0 Website GitHub

🌱 This extension created on top of official MongoDB Scala Driver and allows to fully utilize Spray JSON or Play JSON to represent bidirectional serialization for case classes in BSON, as well as flexible DSL for MongoDB query operators, documents and collections.

Scala versions: 3.x 2.13 2.12

green-leaf-mongo

GitHub Build Status Scala CI green-leaf-mongo-core green-leaf-mongo-spray green-leaf-mongo-play

Short description

This extension created on top of official MongoDB Scala Driver and allows to fully utilize Spray JSON or Play JSON to represent bidirectional serialization for case classes in BSON, as well as flexible DSL for MongoDB query operators, documents and collections.

It was introduced in 2019 - Andrii Lashchenko at #ScalaUA - Spray JSON and MongoDB Queries: Insights and Simple Tricks Related slides available at https://www.slideshare.net/lashchenko/spray-json-and-mongodb-queries-insights-and-simple-tricks

Usage

// build.sbt

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-core
// `green-leaf-mongo-core` can be used if you want to create your own extension

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-spray
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo-spray" % "3.0"

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-play
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo-play" % "3.0"

JSON and BSON protocols

GreenLeafMongoJsonBasicFormats based on DefaultJsonProtocol from Spray JSON and allows to override predefined JsonFormats to make possible use custom serialization in BSON format. This trait also includes a few additional JsonFormats for LocalDate, LocalDateTime, ZonedDateTime, ObjectId, scala Enumeration and UUID.

PlayJsonProtocol is a related extension for the Play JSON library and SprayJsonProtocol for the Spray JSON library.

SprayBsonProtocol/PlayBsonProtocol extends related JsonProtocols and overrides Int, Long, BigDecimal, LocalDate, LocalDateTime, ZonedDateTime, ObjectId, scala Enumeration, UUID and Regex JSON formats to represent them in related BSON (MongoDB Extended JSON V2) formats https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/#mongodb-extended-json-v2-usage.

These base protocols allow to simply (de)serialize this instance to and from both JSON and BSON the same way as in Spray JSON:

// MODEL
case class Test(_id: ObjectId, i: Int, l: Long, b: Boolean, zdt: ZonedDateTime)

// JSON
trait TestJsonProtocol extends SprayJsonProtocol:
  given testJsonFormat = jsonFormat5(Test)

object TestJsonProtocol extends TestJsonProtocol

// BSON
object TestBsonProtocol extends TestJsonProtocol with SprayBsonProtocol

Once protocols defined, we can make instance of Test case class and use TestJsonProtocol to print related JSON:

val obj = Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01")

import TestJsonProtocol.given
println(obj.toJson.prettyPrint)

Output in this case will be:

{
  "_id": "5c72b799306e355b83ef3c86",
  "i": 1,
  "l": 4886718345,
  "b": true,
  "zdt": "1970-01-01T00:00:00Z"
}

Changing single line of import TestJsonProtocol to TestBsonProtocol allows us to (de)serialize this instance to and from BSON:

val obj = Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01")

import TestBsonProtocol.given
println(obj.toJson.prettyPrint)

Output in this case will be:

{
  "_id": {
    "$oid": "5c72b799306e355b83ef3c86"
  },
  "i": {
    "$numberInt": "1"
  },
  "l": {
    "$numberLong": "4886718345"
  },
  "b": true,
  "zdt": {
    "$date": {
      "$numberLong": "0"
    }
  }
}

More examples available in implementation of JsonProtocolSpec/BsonProtocolSpec in Spray and Play project modules.

GreenLeafMongoDsl

GreenLeafMongoFilterOps makes it possible to write queries with a syntax that is more close to real queries in MongoDB, as was implemented in Casbah Query DSL.

"size" $all ("S", "M", "L")
"price" $eq 10
"price" $gt 10
"price" $gte 10
"size" $in ("S", "M", "L")
"price" $lt 100
"price" $lte 100
"price" $ne 1000
"size" $nin ("S", "XXL")
$or( "price" $lt 5, "price" $gt 1, "promotion" $eq true )
$and( "price" $lt 5, "price" $gt 1, "stock" $gte 1 )
"price" $not { $gte (5.1)  }
$nor( "price" $eq 1.99 , "qty" $lt 20, "sale" $eq true )
"qty" $exists true
"results" $elemMatch $and("product" $eq "xyz", "score" $gte 8)
// ...

More examples of queries available in GreenLeafMongoFilterOpsSpec.

GreenLeafMongoDao

GreenLeafMongoDao extends GreenLeafMongoFilterOps with GreenLeafMongoObservableToFutureOps to provide a simple DSL to transform Mongo's Observable[Document] instances to Future[Seq[T]], Future[Option[T]] and Future[T]. In addition, this trait provides many useful generic methods such as insert, getById, findById, updateById, replaceById and others. SprayMongoDao/PlayMongoDao are related implementations for Spray and Play JSON libraries. You can find more details and examples in the dao tests.