GithubHelp home page GithubHelp logo

jarlakxen / eremon Goto Github PK

View Code? Open in Web Editor NEW
5.0 14.0 1.0 47 KB

DAO pattern with ReactiveMongo

License: Apache License 2.0

Scala 100.00%
scala mongo reactivemongo dao repository criteria reactive

eremon's Introduction

Easy REactive MONgo ( EREMON )

This project is build on top of ReactiveMongo to provide an easy "DAO"/"Repository" oriented solution. I use the Criteria DSL from ReactiveMongo-Criteria

Quick start

SBT

In your build.sbt, add the following entries:

resolvers += Resolver.bintrayRepo("jarlakxen", "maven")

libraryDependencies += "org.reactivemongo" %% "reactivemongo" % "0.12.6"
libraryDependencies += "io.eremon" %% "eremon-core" % "1.8.0"

Repository

Define a repository

  import io.eremon._
  import reactivemongo.api.indexes._
  import reactivemongo.bson._
  import reactivemongo.bson.Macros.Annotations._

   case class User(name: String, age: Int, software: Set[String], @Key("_id") id: ID = ID.generate())

   implicit val userReader: BSONDocumentReader[User] = Macros.reader[User]
   implicit val userWriter: BSONDocumentWriter[User] = Macros.writer[User]

   class UserRepository(database: MongoDB)(implicit ec: ExecutionContext) extends ReactiveRepository[User](
     database,
     "User",
     testReader,
     testWriter,
     ec) {

       override def indexes: Seq[Index] = Seq(
          Index(Seq("name" -> IndexType.Ascending), unique = true)
        )
      }

   }

Create a new instance of a repository

  import io.eremon._
  import scala.concurrent.ExecutionContext.Implicits.global
  
  val mongoUri = s"mongodb://<host>:<port>/test-db"
  
  val database: MongoDB = MongoConnector(mongoUri)
  
  val repository = new UserRepository(database)

Methods

  val id = ID.generate()
  val entity = Test("Linus Torvalds", 47, Set("Linux"), id)
  
  repository.insert(entity)
  repository.findById(id)
  repository.findAll()
  repository.removeById(id)
  repository.findOne(criteria[User](_.name) === "Linus Torvalds")
  repository.count
  repository.updateById(id, entity.copy(age = 46))
  repository.updateBy(criteria[User](_.name) === "Linus Torvalds", entity)

See more example in the tests.

Criteria DSL

Untyped DSL Support

What the DSL does provide is the ablity to formulate queries thusly:

  import io.eremon.criteria._
  ...
  // Using an Untyped.criteria
  {
  import Untyped._

  // The MongoDB properties referenced are not enforced by the compiler
  // to belong to any particular type.  This is what is meant by "Untyped".
  val adhoc = criteria.firstName === "Jack" && criteria.age >= 18;
  val cursor = collection.find(adhoc).cursor[BSONDocument];
  }

Another form which achieves the same result is to use one of the where methods available:

  import io.eremon.criteria._
  ...
  // Using one of the Untyped.where overloads
  {
  import Untyped._

  val cursor = collection.find(
    where (_.firstName === "Jack" && _.age >= 18)
	).cursor[BSONDocument];
  }

There are overloads for between 1 and 22 place holders using the where method. Should more than 22 be needed, then the 1 argument version should be used with a named parameter. This allows an infinite number of property constraints to be specified.

Typed DSL Support

For situations where the MongoDB document structure is well known and a developer wishes enforce property existence during compilation, the Typed Criteria can be used:

  import io.eremon.criteria._
  ...
  {
  // Using a Typed criteria which restricts properties to those
  // within a given type and/or those directly accessible
  // through property selectors.
  import Typed._

  case class Nested (rating : Double)
  case class ExampleDocument (aProperty : String, another : Int, nested : Nested)

  val byKnownProperties = criteria[ExampleDocument] (_.aProperty) =~ "^[A-Z]\\w+" && (
    criteria[ExampleDocument] (_.another) > 0 ||
    criteria[ExampleDocument] (_.nested.rating) < 10.0
	);

  val cursor = collection.find(byKnownProperties).cursor[BSONDocument];
  }

When the Typed version is employed, compilation will fail if the provided property navigation does not exist from the root type (specified as the type parameter to criteria above) or the leaf type is not type-compatible with the value(s) provided (if any).

An easy way to think of this is that if it doesn't compile in "regular usage", then it definitely will not when used in a Typed.criteria.

Usage Considerations

Note that Typed and Untyped serve different needs. When the structure of a document collection is both known and identified as static, Typed makes sense to employ. However, Untyped is compelling when document structure can vary within a collection. These are considerations which can easily vary between projects and even within different modules of one project.

Feel free to use either or both Typed and Untyped as they make sense for the problem at hand. One thing to keep in mind is that the examples shown above assumes only one is in scope.

Operators

When using the Criteria DSL, the fact that the operators adhere to the expectations of both programmers and Scala precedences, most uses will "just work." For example, explicitly defining grouping is done with parentheses, just as you would do with any other bit of Scala code.

For the purposes of the operator API reference, assume the following code is in scope:

import io.eremon.criteria.Untyped._

Comparison Operators

With the majority of comparison operators, keep in mind that the definition of their ordering is dependent on the type involved. For example, strings will use lexigraphical ordering whereas numbers use natural ordering.

  • ===, @== Matches properties based on value equality.
criteria.aProperty === "value"
criteria.aProperty @== "value"
  • <>, =/=, !== Matches properties which do not have the given value.
criteria.aProperty <> "value"
criteria.aProperty =/= "value"
criteria.aProperty !== "value"
  • < Matches properties which compare "less than" a given value.
criteria.aNumber < 99
  • <= Matches properties which compare "less than or equal to" a given value.
criteria.aNumber <= 99
  • > Matches properties which compare "greater than" a given value.
criteria.aProperty > "Alice"
  • >= Matches properties which compare "greater than or equal to" a given value.
criteria.aNumber >= 100

Existence Operators

  • exists Matches any document which has the specified field. Use the unary not operator to match based on the leaf property being absent entirely.
criteria.aProperty.exists	// Requires 'aProperty' to be in the document
!criteria.aProperty.exists	// Only matches documents without 'aProperty'
  • in Matches properties which equal one of the given values or array properties having one element which equals any of the given values. Combine with the unary not operator to specify "not in."
criteria.ranking.in (1, 2, 3, 4, 5)
!criteria.ranking.in (1, 2, 3, 4, 5)
  • all Matches array properties which contain all of the given values.
criteria.strings.all ("hello", "world")

String Operators

  • =~ Matches a string property which satisfies the given regular expression String, optionally with regex flags.
criteria.aProperty =~ """^(value)|(someting\s+else)"""
criteria.aProperty =~ """^(value)|(someting\s+else)""" -> IgnoreCase
  • !~ Matches a string property which does not satisfy the given regular expression String, optionally with regex flags.
criteria.aProperty !~ """\d+"""
criteria.aProperty !~ """foo.*bar""" -> (IgnoreCase | MultilineMatching)

Logical Operators

  • ! The unary not operator provides logical negation of an Expression.
!(criteria.aProperty === "value")
  • && Defines logical conjunction (''AND'').
criteria.aProperty === "value" && criteria.another > 0
  • !&& Defines negation of conjunction (''NOR'').
criteria.aProperty === "value" !&& criteria.aProperty @== "other value"
  • || Defines logical disjunction (''OR'').
criteria.aProperty === "value" || criteria.aProperty === "other value"

eremon's People

Contributors

jarlakxen avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

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

Forkers

fravega

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.