Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
lashchenko committed Jul 21, 2024
1 parent 02c628b commit b904f9b
Showing 1 changed file with 96 additions and 16 deletions.
112 changes: 96 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
[![green-leaf-mongo-core](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-core/latest-by-scala-version.svg)](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-core)
[![green-leaf-mongo-spray](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-spray/latest-by-scala-version.svg)](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-spray)
[![green-leaf-mongo-play](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-play/latest-by-scala-version.svg)](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-play)
[![green-leaf-mongo-circe](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-circe/latest-by-scala-version.svg)](https://index.scala-lang.org/greenleafoss/green-leaf-mongo/green-leaf-mongo-circe)


## Short description
This extension created on top of official [MongoDB Scala Driver](https://mongodb.github.io/mongo-scala-driver) and allows to fully utilize [Spray JSON](https://github.com/spray/spray-json) or [Play JSON](https://github.com/playframework/play-json) to represent bidirectional serialization for case classes in BSON, as well as flexible DSL for [MongoDB query operators](https://www.mongodb.com/docs/manual/reference/operator/query/), documents and collections.
This extension created on top of official [MongoDB Scala Driver](https://mongodb.github.io/mongo-scala-driver) and allows to fully utilize [Spray JSON](https://github.com/spray/spray-json), [Play JSON](https://github.com/playframework/play-json) or [Circe JSON](https://circe.github.io/circe/) to represent bidirectional serialization for case classes in BSON, as well as flexible DSL for [MongoDB query operators](https://www.mongodb.com/docs/manual/reference/operator/query/), documents and collections.

It was introduced in 2019 - [Andrii Lashchenko at #ScalaUA - Spray JSON and MongoDB Queries: Insights and Simple Tricks
](https://www.youtube.com/watch?v=NBgKkQtydAo)
Expand All @@ -25,45 +26,85 @@ Related slides available at https://www.slideshare.net/lashchenko/spray-json-and
// `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"
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo-spray" % "3.1"

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

// https://mvnrepository.com/artifact/io.github.greenleafoss/green-leaf-mongo-circe
libraryDependencies += "io.github.greenleafoss" %% "green-leaf-mongo-circe" % "3.1"
```

## 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.
`CirceJsonProtocol` is a related extension for the Circe JSON library, `PlayJsonProtocol` is 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.
`SprayBsonProtocol`/`PlayBsonProtocol`/`CirceBsonProtocol` 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:
```scala 3
// MODEL
case class Test(_id: ObjectId, i: Int, l: Long, b: Boolean, zdt: ZonedDateTime)
final case class Test(_id: ObjectId, i: Int, l: Long, b: Boolean, zdt: ZonedDateTime)

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

object TestJsonProtocol extends TestJsonProtocol

// BSON
object TestBsonProtocol extends TestJsonProtocol with SprayBsonProtocol
```
or Play JSON
```scala 3
// MODEL
final case class Test(_id: ObjectId, i: Int, l: Long, b: Boolean, zdt: ZonedDateTime)

// JSON
trait TestJsonProtocol extends PlayJsonProtocol:
given testJsonFormat: Format[Test] = Json.format[Test]

object TestJsonProtocol extends TestJsonProtocol

// BSON
object TestBsonProtocol extends TestJsonProtocol with PlayBsonProtocol
```
or Circe
```scala 3
// MODEL
final case class Test(_id: ObjectId, i: Int, l: Long, b: Boolean, zdt: ZonedDateTime)

// JSON
trait TestJsonProtocol extends CirceJsonProtocol:
given testJsonFormat: Codec[Test] = deriveCodec

object TestJsonProtocol extends TestJsonProtocol

// BSON
object TestBsonProtocol extends TestJsonProtocol with CirceBsonProtocol
```

Once protocols defined, we can make instance of Test case class and use TestJsonProtocol to print related JSON:
```scala
val obj = Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01")
Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01T00:00:00Z".parseZonedDateTime)

// Spray JSON
import TestJsonProtocol.given
println(obj.toJson.prettyPrint)

// Play JSON
import TestJsonProtocol.given
println(Json.prettyPrint(Json.toJson(obj)))

// Circe JSON
import TestJsonProtocol.given
println(obj.asJson)
```
Output in this case will be:
```js
```json
{
"_id": "5c72b799306e355b83ef3c86",
"i": 1,
Expand All @@ -76,14 +117,23 @@ Output in this case will be:
Changing single line of import `TestJsonProtocol` to `TestBsonProtocol` allows us to (de)serialize this instance to and from BSON:

```scala
val obj = Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01")
Test(new ObjectId("5c72b799306e355b83ef3c86"), 1, 0x123456789L, true, "1970-01-01T00:00:00Z".parseZonedDateTime)

// Spray JSON
import TestBsonProtocol.given
println(obj.toJson.prettyPrint)

// Play JSON
import TestBsonProtocol.given
println(Json.prettyPrint(Json.toJson(obj)))

// Circe JSON
import TestBsonProtocol.given
println(obj.asJson)
```

Output in this case will be:
```js
```json
{
"_id": {
"$oid": "5c72b799306e355b83ef3c86"
Expand All @@ -103,27 +153,57 @@ Output in this case will be:
}
```

More examples available in implementation of **JsonProtocolSpec**/**BsonProtocolSpec** in Spray and Play project modules.
More examples available in implementation of **JsonProtocolSpec**/**BsonProtocolSpec** in Spray, Play and Circe 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](http://mongodb.github.io/casbah/3.1/reference/query_dsl/).

```scala
"size" $all ("S", "M", "L")
"size" $all Seq("S", "M", "L")
// {"size": {"$all": ["S", "M", "L"]}}

"price" $eq 10
// {"price": {"$eq": 10}}

"price" $gt 10
// {"price": {"$gt": 10}}

"price" $gte 10
"size" $in ("S", "M", "L")
// {"price": {"$gte": 10}}

"size" $in Seq("S", "M", "L")
// {"size": {"$in": ["S", "M", "L"]}}

"price" $lt 100
// {"price": {"$lt": 100}}

"price" $lte 100
// {"price": {"$lte": 100}}

"price" $ne 1000
"size" $nin ("S", "XXL")
// {"price": {"$ne": 1000}}

"size" $nin Seq("S", "XXL")
// {"size": {"$nin": ["S", "XXL"]}}

$or( "price" $lt 5, "price" $gt 1, "promotion" $eq true )
// {"$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) }
// {"$and": [{"price": {"$lt": 5}}, {"price": {"$gt": 1}}, {"stock": {"$gte": 1}}]}

"price" $not { $gte (5.1) }
// {"price": {"$not": {"$gte": 5.1}}}

$nor( "price" $eq 1.99 , "qty" $lt 20, "sale" $eq true )
// {"$nor": [{"price": {"$eq": 1.99}}, {"qty": {"$lt": 20}}, {"sale": {"$eq": true}}]}

"qty" $exists true
// {"qty": {"$exists": true}}

"results" $elemMatch $and("product" $eq "xyz", "score" $gte 8)
// {"results": {"$elemMatch": {"$and": [{"product": {"$eq": "xyz"}}, {"score": {"$gte": 8}}]}}}

// ...
```

Expand Down

0 comments on commit b904f9b

Please sign in to comment.