Git Product home page Git Product logo

zio-mongodb's Introduction

zio-mongodb

One more ZIO wrapper around the official MongoDB Java driver but better ;)

The main goals of the project:

  • the first class ZIO support
  • encapsulate Java driver runtime codecs resolution by a compile time solution
  • Scala 3 support
  • codecs derivation support
  • split query building and results processing
  • type-safe query building DSL
  • provide zio-test integrated test-kit

Quick start

Import the library

libraryDependencies ++= Seq(
  "io.github.zeal18" %% "zio-mongodb-bson"    % "0.10.4",
  "io.github.zeal18" %% "zio-mongodb-driver"  % "0.10.4",
  "io.github.zeal18" %% "zio-mongodb-testkit" % "0.10.4" % Test
)

Create client and database layers

import io.github.zeal18.zio.mongodb.driver.MongoClient
import io.github.zeal18.zio.mongodb.driver.MongoDatabase
import zio.ZIO
import zio.ZLayer

val client: ZLayer[Any, Throwable, MongoClient] =
  MongoClient.live("mongodb://localhost:27017")

val database: ZLayer[Any, Throwable, MongoDatabase] =
  client >>> ZLayer.fromZIO(ZIO.serviceWith[MongoClient](_.getDatabase("database-name")))

Create DAL

import io.github.zeal18.zio.mongodb.bson.annotations.BsonId
import io.github.zeal18.zio.mongodb.driver.MongoDatabase
import io.github.zeal18.zio.mongodb.driver.filters
import zio.Task
import zio.ZLayer

case class User(@BsonId id: Int, name: String)

trait UsersDal {
  def insert(user: User): Task[Boolean]
  def get(id: Int): Task[Option[User]]
  def delete(id: Int): Task[Boolean]
}

object UsersDal {
  val live: ZLayer[MongoDatabase, Nothing, UsersDal] = ZLayer.fromZIO(for {
    mongo <- ZIO.service[MongoDatabase]

    coll =
      mongo
        .getCollection[User]("users")
        .withReadConcern(ReadConcern.MAJORITY)
        .withWriteConcern(WriteConcern.MAJORITY)
  } yield new UsersDal {
    override def insert(user: User): Task[Boolean] =
      coll.insertOne(user).map(_.wasAcknowledged())

    override def get(id: Int): Task[Option[User]] =
      coll.find(filters.eq(id)).runHead

    override def delete(id: Int): Task[Boolean] =
      coll.deleteOne(filters.eq(id)).map(_.getDeletedCount() > 0)
  })
}

Test the DAL with Embedded MongoDB

import io.github.zeal18.zio.mongodb.testkit.MongoClientTest
import io.github.zeal18.zio.mongodb.testkit.MongoDatabaseTest
import zio.ZIO
import zio.test.*

object UsersDalSpec extends ZIOSpecDefault {
  override def spec = suite("UsersDalSpec")(
    test("insert user") {
      for {
        dal <- ZIO.service[UsersDal]

        inserted <- dal.insert(User(3, "John Doe"))
      } yield assertTrue(inserted)
    },
    test("get user") {
      for {
        dal <- ZIO.service[UsersDal]
        user = User(5, "John Doe")

        _      <- dal.insert(user)
        result <- dal.get(5)
      } yield assertTrue(result.get == user)
    },
    test("delete user") {
      for {
        dal <- ZIO.service[UsersDal]
        user = User(5, "John Doe")

        _       <- dal.insert(user)
        deleted <- dal.delete(5)
        result  <- dal.get(5)
      } yield assertTrue(deleted, result.isEmpty)
    },
  ).provideSomeLayer(MongoDatabaseTest.random >>> UsersDal.live)
    .provideLayerShared(MongoClientTest.live())
}

NOTE: we provide MongoClientTest as a shared layer to prevent relaunching the MongoDB instance for every test case. At the same time we want to recreate databases with random names to isolate test cases from each other.

ZIO 1.x support

ZIO 1.x is not supported anymore. The latest version based on ZIO 1 is 0.5.1.

zio-mongodb's People

Contributors

executioner1939 avatar renovate[bot] avatar scala-steward avatar zeal18 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

zio-mongodb's Issues

Codec Derivation

Hi,

Firstly, thank you for the massive effort you have put into this, it solves almost all issues with the current Scala and Java Drivers.

I am currently trying to simply insert a case class, but am getting a NPE while doing so. I have tried to read through the test cases to see what I may need to do to setup the driver correctly and I believe I have done so.

I added a Codec[UUID] as below:

implicit val uid: Codec[UUID] = Codec[String].bimap(str => UUID.fromString(str), (id: UUID) => id.toString)

as that was preventing compilation, however I am still getting an error, I know that my case class does have a custom type as is below:

object DocumentTypes {
  type DocumentType = String
}

It is also quite nested along with a List.

Here is the error:

timestamp=2022-10-12T10:21:15.7951366+02:0 level=ERROR thread=zio-fiber-31 message="Error Inserting" cause=Exception in thread "zio-fiber-" java.lang.NullPointerException: null
	at io.github.zeal18.zio.mongodb.bson.codecs.MagnoliaCodec$InlinedCaseClassCodec.$anonfun$encode$3(MagnoliaCodec.scala:115)

Any help with this would be appreciated

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Repository problems

These problems occurred while renovating this repository. View logs.

  • WARN: Found renovate config warnings

This repository currently has no open or pending branches.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • coursier/cache-action v6
  • actions/setup-java v4.3.0
  • actions/checkout v4
  • coursier/cache-action v6
  • actions/setup-java v4.3.0
  • actions/checkout v4
  • coursier/cache-action v6
  • actions/setup-java v4.3.0
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
.github/workflows/release-drafter.yml
  • release-drafter/release-drafter v6
  • ubuntu 22.04
sbt
build.sbt
  • scala 2.13.14
  • com.olegpy:better-monadic-for 0.3.1
  • org.mongodb:bson 5.1.4
  • dev.zio:zio-test 2.1.9
  • dev.zio:zio-test-sbt 2.1.9
  • com.softwaremill.magnolia1_2:magnolia 1.1.10
  • dev.zio:zio 2.1.9
  • dev.zio:zio-interop-reactivestreams 2.0.2
  • org.mongodb:mongodb-driver-reactivestreams 5.1.4
  • org.reactivestreams:reactive-streams-tck 1.0.4
  • dev.zio:zio-test 2.1.9
  • dev.zio:zio-test-sbt 2.1.9
  • dev.zio:zio 2.1.9
  • dev.zio:zio-test 2.1.9
  • de.flapdoodle.embed:de.flapdoodle.embed.mongo 4.17.0
  • org.immutables:builder 2.10.1
  • org.immutables:value 2.10.1
  • dev.zio:zio-test-sbt 2.1.9
project/build.properties
  • sbt/sbt 1.10.1
project/plugins.sbt
  • com.timushev.sbt:sbt-updates 0.6.4
  • com.timushev.sbt:sbt-rewarn 0.1.3
  • com.github.sbt:sbt-native-packager 1.10.4
  • org.typelevel:sbt-tpolecat 0.5.2
  • org.scalameta:sbt-scalafmt 2.5.2
  • com.github.sbt:sbt-ci-release 1.6.1
scalafmt
.scalafmt.conf
  • scalafmt 3.8.3

  • Check this box to trigger a request for Renovate to run again on this repository

filters.eq

Hello,

I am trying to use zio-mongodb.
I am facing with an issue.

when I try to used 'filters.eq' it seems that scala eq() method is used instead of eq() from filters object.

where I am wrong?

Case Classes that differ for Aggregation Pipelines

Hi,

I would like to use a different class that the top-level type supplied to the MongoCollection[A] when working with aggregation pipelines.

I am trying to create a PR for this but don't quite understand how the Codec[A] would be used, for example:

    override def aggregate[B](pipeline: Seq[Aggregation])(implicit codec: Codec[B]): AggregateQuery[B] =
      AggregateQuery(wrapped.aggregate[B](pipeline.asJava, classOf[B]))

Given the type is passed to the underlying Java MongoCollection, I don't quite understand the flow :-). Not sure if you could give some pointers :-).

How to perform transactions

After reading the official mongo docs and looking around in the zio-mongodb code I came up with the following sample code. Is it correct?

case class Sample(...)

case class Sample2(...)

val db = mongoDB
      .withReadPreference(ReadPreference.primary())
      .withReadConcern(ReadConcern.SNAPSHOT)
      .withWriteConcern(WriteConcern.MAJORITY)

    val sampleCollection = db.getCollection[Sample]("sample_collection")
    val sample2Collection = db.getCollection[Sample2]("sample2_collection")

    ZIO.scoped {
      for {
        session               <- db.startSession()
        _                         <- ZIO.succeed(session.startTransaction())
        _                         <- sampleCollection.deleteOne(session, filters.eq("someid"))
        _                         <- sample2Collection.deleteOne(session, filters.eq("someid"))
        _                         <- ZIO.succeed(session.commitTransaction())
      } yield ()
    }
  }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.