{"id":21179537,"url":"https://github.com/jarlakxen/eremon","last_synced_at":"2025-07-09T23:31:39.830Z","repository":{"id":137205277,"uuid":"93876087","full_name":"Jarlakxen/eremon","owner":"Jarlakxen","description":"DAO pattern with ReactiveMongo","archived":false,"fork":false,"pushed_at":"2018-01-17T18:55:26.000Z","size":47,"stargazers_count":5,"open_issues_count":0,"forks_count":2,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-05T08:24:41.110Z","etag":null,"topics":["criteria","dao","mongo","reactive","reactivemongo","repository","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jarlakxen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-09T16:10:50.000Z","updated_at":"2019-11-26T14:01:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"c93004dc-3aea-42b9-b4fc-0456c73ec6b0","html_url":"https://github.com/Jarlakxen/eremon","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/Jarlakxen/eremon","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarlakxen%2Feremon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarlakxen%2Feremon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarlakxen%2Feremon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarlakxen%2Feremon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jarlakxen","download_url":"https://codeload.github.com/Jarlakxen/eremon/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarlakxen%2Feremon/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264505236,"owners_count":23618909,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["criteria","dao","mongo","reactive","reactivemongo","repository","scala"],"created_at":"2024-11-20T17:31:52.386Z","updated_at":"2025-07-09T23:31:39.818Z","avatar_url":"https://github.com/Jarlakxen.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"Easy REactive MONgo ( EREMON )\n======================\n\nThis project is build on top of [ReactiveMongo](https://github.com/ReactiveMongo/ReactiveMongo) to provide an easy \"DAO\"/\"Repository\" oriented solution. I use the Criteria DSL from [ReactiveMongo-Criteria](https://github.com/osxhacker/ReactiveMongo-Criteria)\n\n## Quick start\n\n### SBT\n\nIn your `build.sbt`, add the following entries:\n\n```scala\nresolvers += Resolver.bintrayRepo(\"jarlakxen\", \"maven\")\n\nlibraryDependencies += \"org.reactivemongo\" %% \"reactivemongo\" % \"0.12.6\"\nlibraryDependencies += \"io.eremon\" %% \"eremon-core\" % \"1.8.0\"\n```\n\n### Repository\n\n#### Define a repository\n\n```scala\n  import io.eremon._\n  import reactivemongo.api.indexes._\n  import reactivemongo.bson._\n  import reactivemongo.bson.Macros.Annotations._\n\n   case class User(name: String, age: Int, software: Set[String], @Key(\"_id\") id: ID = ID.generate())\n\n   implicit val userReader: BSONDocumentReader[User] = Macros.reader[User]\n   implicit val userWriter: BSONDocumentWriter[User] = Macros.writer[User]\n\n   class UserRepository(database: MongoDB)(implicit ec: ExecutionContext) extends ReactiveRepository[User](\n     database,\n     \"User\",\n     testReader,\n     testWriter,\n     ec) {\n\n       override def indexes: Seq[Index] = Seq(\n          Index(Seq(\"name\" -\u003e IndexType.Ascending), unique = true)\n        )\n      }\n\n   }\n```\n\n#### Create a new instance of a repository\n```scala\n  import io.eremon._\n  import scala.concurrent.ExecutionContext.Implicits.global\n  \n  val mongoUri = s\"mongodb://\u003chost\u003e:\u003cport\u003e/test-db\"\n  \n  val database: MongoDB = MongoConnector(mongoUri)\n  \n  val repository = new UserRepository(database)\n```\n\n#### Methods\n\n```scala\n  val id = ID.generate()\n  val entity = Test(\"Linus Torvalds\", 47, Set(\"Linux\"), id)\n  \n  repository.insert(entity)\n  repository.findById(id)\n  repository.findAll()\n  repository.removeById(id)\n  repository.findOne(criteria[User](_.name) === \"Linus Torvalds\")\n  repository.count\n  repository.updateById(id, entity.copy(age = 46))\n  repository.updateBy(criteria[User](_.name) === \"Linus Torvalds\", entity)\n```\n\nSee more example in the [tests](https://github.com/fravega/eremon/tree/master/core/src/test/scala/io/eremon).\n\n### Criteria DSL\n\n#### Untyped DSL Support\n\nWhat the DSL *does* provide is the ablity to formulate queries thusly:\n\n```scala\n  import io.eremon.criteria._\n  ...\n  // Using an Untyped.criteria\n  {\n  import Untyped._\n\n  // The MongoDB properties referenced are not enforced by the compiler\n  // to belong to any particular type.  This is what is meant by \"Untyped\".\n  val adhoc = criteria.firstName === \"Jack\" \u0026\u0026 criteria.age \u003e= 18;\n  val cursor = collection.find(adhoc).cursor[BSONDocument];\n  }\n```\n\nAnother form which achieves the same result is to use one of the `where` methods available:\n\n```scala\n  import io.eremon.criteria._\n  ...\n  // Using one of the Untyped.where overloads\n  {\n  import Untyped._\n\n  val cursor = collection.find(\n    where (_.firstName === \"Jack\" \u0026\u0026 _.age \u003e= 18)\n\t).cursor[BSONDocument];\n  }\n```\n\nThere 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.\n\n#### Typed DSL Support\n\nFor situations where the MongoDB document structure is well known and a developer wishes enforce property existence **during compilation**, the `Typed` Criteria can be used:\n\n```scala\n  import io.eremon.criteria._\n  ...\n  {\n  // Using a Typed criteria which restricts properties to those\n  // within a given type and/or those directly accessible\n  // through property selectors.\n  import Typed._\n\n  case class Nested (rating : Double)\n  case class ExampleDocument (aProperty : String, another : Int, nested : Nested)\n\n  val byKnownProperties = criteria[ExampleDocument] (_.aProperty) =~ \"^[A-Z]\\\\w+\" \u0026\u0026 (\n    criteria[ExampleDocument] (_.another) \u003e 0 ||\n    criteria[ExampleDocument] (_.nested.rating) \u003c 10.0\n\t);\n\n  val cursor = collection.find(byKnownProperties).cursor[BSONDocument];\n  }\n```\n\nWhen 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).\n\nAn 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`.\n\n\n### Usage Considerations\n\nNote 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.\n\nFeel 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.\n\n\n## Operators\n\nWhen 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.\n\nFor the purposes of the operator API reference, assume the following code is in scope:\n\n```scala\nimport io.eremon.criteria.Untyped._\n```\n\n### Comparison Operators\n\nWith 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.\n\n* **===**, **@==** Matches properties based on value equality.\n\n```scala\ncriteria.aProperty === \"value\"\n```\n\n```scala\ncriteria.aProperty @== \"value\"\n```\n\n* **\u003c\u003e**, **=/=**, **!==** Matches properties which do not have the given value.\n\n```scala\ncriteria.aProperty \u003c\u003e \"value\"\n```\n\n```scala\ncriteria.aProperty =/= \"value\"\n```\n\n```scala\ncriteria.aProperty !== \"value\"\n```\n\n* **\u003c** Matches properties which compare \"less than\" a given value.\n\n```scala\ncriteria.aNumber \u003c 99\n```\n\n* **\u003c=** Matches properties which compare \"less than or equal to\" a given value.\n\n```scala\ncriteria.aNumber \u003c= 99\n```\n\n* **\u003e** Matches properties which compare \"greater than\" a given value.\n\n```scala\ncriteria.aProperty \u003e \"Alice\"\n```\n\n* **\u003e=** Matches properties which compare \"greater than or equal to\" a given value.\n\n```scala\ncriteria.aNumber \u003e= 100\n```\n\n### Existence Operators\n\n* **exists** Matches any document which has the specified field.  Use the unary not operator to match based on the leaf property being absent entirely.\n\n```scala\ncriteria.aProperty.exists\t// Requires 'aProperty' to be in the document\n!criteria.aProperty.exists\t// Only matches documents without 'aProperty'\n```\n\n* **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.\"\n\n```scala\ncriteria.ranking.in (1, 2, 3, 4, 5)\n!criteria.ranking.in (1, 2, 3, 4, 5)\n```\n\n* **all** Matches array properties which contain all of the given values.\n\n```scala\ncriteria.strings.all (\"hello\", \"world\")\n```\n\n### String Operators\n\n* **=~** Matches a string property which satisfies the given regular expression `String`, optionally with [regex flags](https://docs.mongodb.com/manual/reference/operator/query/regex/).\n\n```scala\ncriteria.aProperty =~ \"\"\"^(value)|(someting\\s+else)\"\"\"\ncriteria.aProperty =~ \"\"\"^(value)|(someting\\s+else)\"\"\" -\u003e IgnoreCase\n```\n\n* **!~** Matches a string property which does _not_ satisfy the given regular expression `String`, optionally with [regex flags](https://docs.mongodb.com/manual/reference/operator/query/regex/).\n\n```scala\ncriteria.aProperty !~ \"\"\"\\d+\"\"\"\ncriteria.aProperty !~ \"\"\"foo.*bar\"\"\" -\u003e (IgnoreCase | MultilineMatching)\n```\n\n### Logical Operators\n\n* **!** The unary not operator provides logical negation of an `Expression`.\n\n```scala\n!(criteria.aProperty === \"value\")\n```\n\n* **\u0026\u0026** Defines logical conjunction (''AND'').\n\n```scala\ncriteria.aProperty === \"value\" \u0026\u0026 criteria.another \u003e 0\n```\n\n* **!\u0026\u0026** Defines negation of conjunction (''NOR'').\n\n```scala\ncriteria.aProperty === \"value\" !\u0026\u0026 criteria.aProperty @== \"other value\"\n```\n\n* **||** Defines logical disjunction (''OR'').\n\n```scala\ncriteria.aProperty === \"value\" || criteria.aProperty === \"other value\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarlakxen%2Feremon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjarlakxen%2Feremon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarlakxen%2Feremon/lists"}