diff --git a/README.md b/README.md index f9e77df..93fdf69 100644 --- a/README.md +++ b/README.md @@ -37,30 +37,43 @@ To use dsl of Criteria4s, you need to add the following dependency to your proje - Core library: [criteria4s-core](https://central.sonatype.com/artifact/io.github.rafafrdz/criteria4s-core_2.13) - SQL implementation: [criteria4s-sql](https://central.sonatype.com/artifact/io.github.rafafrdz/criteria4s-sql_2.13) +- MongoDB dialect + implementation: [criteria4s-mongodb](https://central.sonatype.com/artifact/io.github.rafafrdz/criteria4s-mongodb_2.13) **SBT** ```scala libraryDependencies += "io.github.rafafrdz" %% "criteria4s-core" % "" // Core library libraryDependencies += "io.github.rafafrdz" %% "criteria4s-sql" % "" // SQL implementation +libraryDependencies += "io.github.rafafrdz" %% "criteria4s-mongodb" % "" // MongoDB implementation + ``` **Maven** ```xml - - io.github.rafafrdz - criteria4s-core_2.13 - 0.8.0 - - - - - io.github.rafafrdz - criteria4s-core_2.13 - 0.8.0 - + + + io.github.rafafrdz + criteria4s-core_2.13 + 0.8.2 + + + + + io.github.rafafrdz + criteria4s-core_2.13 + 0.8.2 + + + + + io.github.rafafrdz + criteria4s-mongodb_2.13 + 0.8.2 + + ``` > [!IMPORTANT] @@ -70,52 +83,75 @@ libraryDependencies += "io.github.rafafrdz" %% "criteria4s-sql" % "" // Criteria4s is extensible to support any kind of data stores. Currently, it supports the following **dialects**: -| Dialect | Package | Example | -|-------------|:-------------------------------------------------------------------------------------------|:--------| -| SQL | [sql](./sql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/sql) | - | -| MongoDB | [mongodb](./mongodb/src/main/scala/io/github/rafafrdz/criteria4s/dialect/mongodb) | - | -| PostgresSQL | [postgresql](./postgresql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/postgresql) | - | +| Dialect | Package | +|-------------|:-------------------------------------------------------------------------------------------| +| SQL | [sql](./sql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/sql) | +| MongoDB | [mongodb](./mongodb/src/main/scala/io/github/rafafrdz/criteria4s/dialect/mongodb) | +| PostgresSQL | [postgresql](./postgresql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/postgresql) | ## Examples -Here, we will show some examples of how to use the Criteria DSL. +Here, we will show some examples of how to use the Criteria DSL. You can find more examples in +the [`criteria4s-examples`](./examples/src/main/scala/io/github/rafafrdz/criteria4s/examples) module. + +### Imports + +First, we need to import the Criteria4s DSL and the SQL dialect: ```scala import io.github.rafafrdz.criteria4s.core._ import io.github.rafafrdz.criteria4s.examples.datastores._ import io.github.rafafrdz.criteria4s.extensions._ import io.github.rafafrdz.criteria4s.functions._ +``` -def ageCriteria[T <: CriteriaTag : GT : LT : AND : Sym]: Criteria[T] = - (col[T]("age") gt lit(18)) and (col[T]("age") lt lit(65)) +### Defining Criteria Expressions -def expr[T <: CriteriaTag : LEQ : EQ : AND : OR : Sym]: Criteria[T] = - (col[T]("a") leq lit(3)) and (col[T]("b") leq lit(4)) or (col[T]("c") === lit("c")) +We can define criteria expressions in a polymorphic way by using the Criteria DSL. You can find more definitions +examples +in the [`Defining Criteria Expressions`](./doc/defining-criteria-expressions.md) document. -def expr2[T <: CriteriaTag : EQ : Sym](fieldName: String, id: UUID): Criteria[T] = - col[T](fieldName) === lit(id.toString) +```scala +def expr[T <: CriteriaTag : LEQ : EQ : AND : OR : Show[Column, *]]: Criteria[T] = + (col[T]("field1") leq lit(3)) and (col[T]("field2") leq lit(4)) or (col[T]("field3") === lit("c")) ``` -And then we can use the `ageCriteria`, `expr` and `expr2` functions to generate criteria expressions for different -datastores: +#### Case of use ```scala -ageCriteria[WeirdDatastore] -// res: {left: {left: age, opt: >, right: 18 }, opt: AND, right: {left: age, opt: <, right: 65 } } - -expr[MySQL] -// res: ((a <<< '3') AND (b <<< '4')) OR (c = 'c') +def ageCriteria[T <: CriteriaTag : GT : LT : AND : Show[Column, *]]: Criteria[T] = + (col[T]("age") gt lit(18)) and (col[T]("age") lt lit(65)) -expr2[Postgres]("USER_ID", UUID.randomUUID()) -// res: `USER_ID` = '07715cee-5d87-427d-99a7-cc03f2b5ef4a' +def refCriteria[T <: CriteriaTag : EQ : Show[Column, *]](fieldName: String, id: UUID): Criteria[T] = + col[T](fieldName) === lit(id.toString) ``` +### Evaluating Criteria Expressions + +And then we can use those expressions defined belows in order to generate criteria expressions for different +datastores by importing the corresponding dialects. You can find evaluation examples for some different dialects in the +following documents: + +- [PostgreSQL Dialect](./doc/postgresql-dialect-evaluating.md) +- [MongoDB Dialect](./doc/mongodb-dialect-evaluating.md) +- [MySQL Dialect](./doc/mysql-dialect-evaluating.md) +- [Custom Dialect](./doc/custom-dialect-evaluating.md) + +### Inline Criteria DSL + Or maybe we can use the Criteria DSL inline: ```scala -(col[WeirdDatastore]("a") leq lit(3)) and (col[WeirdDatastore]("b") leq lit(4)) or (col[WeirdDatastore]("c") === lit("c")) -// res: {left: {left: {left: a, opt: <=, right: 3 }, opt: AND, right: {left: b, opt: <=, right: 4 } }, opt: OR, right: {left: c, opt: =, right: c } } +(col[PostgreSQL]("field1") leq lit(3)) and (col[PostgreSQL]("field2") leq lit(4)) or (col[PostgreSQL]("field3") === lit("c")) +// res: (('field1' <= 3) AND ('field2' <= 4)) OR ('field3' = c) ``` -You can find more examples in -the [`criteria4s-examples`](./examples/src/main/scala/io/github/rafafrdz/criteria4s/examples) module. +```scala +(col[MongoDB]("field1") leq lit(3)) and (col[MongoDB]("field2") leq lit(4)) or (col[MongoDB]("field3") === lit("c")) +// res: {$or: [{$and: [{"field1": {$lte: 3}}, {"field2": {$lte: 4}}]}, {"field3": {$eq: c}}]} +``` + +```scala +(col[WeirdDatastore]("field1") leq lit(3)) and (col[WeirdDatastore]("field2") leq lit(4)) or (col[WeirdDatastore]("field3") === lit("c")) +// res: {left: {left: {left: field1, opt: <=, right: 3 }, opt: AND, right: {left: field2, opt: <=, right: 4 } }, opt: OR, right: {left: field3, opt: =, right: c } } +``` diff --git a/doc/custom-dialect-evaluating.md b/doc/custom-dialect-evaluating.md new file mode 100644 index 0000000..53c1f08 --- /dev/null +++ b/doc/custom-dialect-evaluating.md @@ -0,0 +1,63 @@ +# Custom Dialect + +The `WeirdDatastore` dialect using in this document is an own-implemented dialect for a custom or "weird" datastore. +The examples provided illustrate what the output would look like if a `WeirdDatastore` dialect needs be implemented. +The results are intentionally generated using weird expressions to highlight the differences that occur when applying +the same criteria expression across different dialects. + +The `WeirdDatastore` dialect implementation is located in the [WeirdDatastore.scala](../examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/datastores/WeirdDatastore.scala) file. + +For these examples we will use the defined expressions in +the [Defining Criteria Expressions](defining-criteria-expressions.md) document. + +```scala +expr[WeirdDatastore] +// res: {left: {left: {left: field1, opt: <=, right: 3 }, opt: AND, right: {left: field2, opt: <=, right: 4 } }, opt: OR, right: {left: field3, opt: =, right: c } } +``` + +## Case of use + +```scala +ageCriteria[WeirdDatastore] +// res: {left: {left: age, opt: >, right: 18 }, opt: AND, right: {left: age, opt: <, right: 65 } } +``` + +```scala +refCriteria[WeirdDatastore]("id", UUID.randomUUID()) +// res: {left: id, opt: =, right: dfacb848-6d39-404b-9e6b-f8cbc1f52918 } +``` + +### Using `IS NULL` expression + +```scala +isNullExpr[WeirdDatastore]("field") +// res: {left: field, opt: IS NULL } +``` + +### Using `NOT` expression + +```scala +notExpr[WeirdDatastore]("field") +// res: {left: {left: {left: field, opt: >, right: 2 }, opt: NOT }, opt: AND, right: {left: field, opt: <=, right: 10 } } +``` + +### Using `BETWEEN` expression + +```scala +betweenExpr[WeirdDatastore]("field") +// res: {left: field, opt: BETWEEN, right: [100, 150] } +``` + +### Using `ARRAY` expression + +```scala +arrayExpr[WeirdDatastore]("field") +// res: {left: field, opt: IN, right: [1, 2, 3] } +``` + +### Using `LIKE` expression + +```scala +likeExpr[WeirdDatastore]("field") +// res: {left: field, opt: LIKE, right: a% } +``` \ No newline at end of file diff --git a/doc/defining-criteria-expressions.md b/doc/defining-criteria-expressions.md new file mode 100644 index 0000000..c598da4 --- /dev/null +++ b/doc/defining-criteria-expressions.md @@ -0,0 +1,53 @@ +# Defining Criteria Expressions + +We can define criteria expressions in a polymorphic way by using the Criteria DSL: + +```scala +def expr[T <: CriteriaTag : LEQ : EQ : AND : OR : Show[Column, *]]: Criteria[T] = + (col[T]("field1") leq lit(3)) and (col[T]("field2") leq lit(4)) or (col[T]("field3") === lit("c")) +``` + +## Case of use + +```scala +def ageCriteria[T <: CriteriaTag : GT : LT : AND : Show[Column, *]]: Criteria[T] = + (col[T]("age") gt lit(18)) and (col[T]("age") lt lit(65)) + +def refCriteria[T <: CriteriaTag : EQ : Show[Column, *]](fieldName: String, id: UUID): Criteria[T] = + col[T](fieldName) === lit(id.toString) +``` + +### Using `IS NULL` expression + +```scala +def isNullExpr[T <: CriteriaTag : ISNULL : Show[Column, *]](fieldName: String): Criteria[T] = + col[T](fieldName).isNull +``` + +### Using `NOT` expression + +```scala +def notExpr[T <: CriteriaTag : NOT : GT : LEQ : AND : Show[Column, *] : Show[(Int, Int), *]](fieldName: String): Criteria[T] = + not(col[T](fieldName) gt lit(2)) && (col[T](fieldName) leq lit(10)) +``` + +### Using `BETWEEN` expression + +```scala +def betweenExpr[T <: CriteriaTag : BETWEEN : Show[Column, *] : Show[(Int, Int), *]](fieldName: String): Criteria[T] = + col[T](fieldName) between range(100, 150) +``` + +### Using `ARRAY` expression + +```scala +def arrayExpr[T <: CriteriaTag : IN : Show[Column, *] : Show[Seq[Int], *]](fieldName: String): Criteria[T] = + col[T](fieldName) in array(1, 2, 3) +``` + +### Using `LIKE` expression + +```scala +def likeExpr[T <: CriteriaTag : LIKE : Show[Column, *]](fieldName: String): Criteria[T] = + col[T](fieldName) like lit("a%") +``` diff --git a/doc/mongodb-dialect-evaluating.md b/doc/mongodb-dialect-evaluating.md new file mode 100644 index 0000000..c018859 --- /dev/null +++ b/doc/mongodb-dialect-evaluating.md @@ -0,0 +1,59 @@ +# MongoDB Dialect + +The `MongoDB` dialect is available. The examples provided illustrate what the output look like using the MongoDB +dialect. + +For these examples we will use the defined expressions in +the [Defining Criteria Expressions](defining-criteria-expressions.md) document. + +```scala +expr[MongoDB] +// res: {$or: [{$and: [{"field1": {$lte: 3}}, {"field2": {$lte: 4}}]}, {"field3": {$eq: c}}]} +``` + +## Case of use + +```scala +ageCriteria[MongoDB] +// res: {$and: [{"age": {$gt: 18}}, {"age": {$lt: 65}}]} +``` + +```scala +refCriteria[MongoDB]("id", UUID.randomUUID()) +// res: {"id": {$eq: d0ba6ea7-4cff-44e4-b612-ebe12242bd18}} +``` + +### Using `IS NULL` expression + +```scala +isNullExpr[MongoDB]("field") +// res: {"field": null} +``` + +### Using `NOT` expression + +```scala +notExpr[MongoDB]("field") +// res: {$and: [{"field": {$not: {$gt: 2}}}, {"field": {$lte: 10}}]} +``` + +### Using `BETWEEN` expression + +```scala +betweenExpr[MongoDB]("field") +// res: {"field": { $gte: 100, $lt: 150 }} +``` + +### Using `ARRAY` expression + +```scala +arrayExpr[MongoDB]("field") +// res: {"field": {$in: [1, 2, 3]}} +``` + +### Using `LIKE` expression + +```scala +likeExpr[MongoDB]("field") +// res: {"field": {$regex: \a%\}} +``` \ No newline at end of file diff --git a/doc/mysql-dialect-evaluating.md b/doc/mysql-dialect-evaluating.md new file mode 100644 index 0000000..c815a1f --- /dev/null +++ b/doc/mysql-dialect-evaluating.md @@ -0,0 +1,63 @@ +# MySQL Dialect + +The `MySQL` dialect is not yet available. The examples provided illustrate what the output would look like if a MySQL +dialect were implemented. The results are intentionally generated using non-official expressions to highlight the +differences that occur when applying the same criteria expression across different dialects. + +We are currently working on the official MySQL dialect. In the meantime, you can use the SQL dialect to implement your +own MySQL dialect. + +For these examples we will use the defined expressions in +the [Defining Criteria Expressions](defining-criteria-expressions.md) document. + +```scala +expr[MySQL] +// res: ((field1 <<< '3') AND (field2 <<< '4')) OR (field3 = 'c') +``` + +## Case of use + +```scala +ageCriteria[MySQL] +// res: ((age > '18') AND (age < '65')) +``` + +```scala +refCriteria[MySQL]("id", UUID.randomUUID()) +// res: id = '07715cee-5d87-427d-99a7-cc03f2b5ef4a' +``` + +### Using `IS NULL` expression + +```scala +isNullExpr[MySQL]("field") +// res: field IS NULL +``` + +### Using `NOT` expression + +```scala +notExpr[Mysql]("field") +// res: (NOT (field > '2')) AND (field <= '10') +``` + +### Using `BETWEEN` expression + +```scala +betweenExpr[MySQL]("field") +// res: field BETWEEN 100 AND 150 +``` + +### Using `ARRAY` expression + +```scala +arrayExpr[MySQL]("field") +// res: field IN (1, 2, 3) +``` + +### Using `LIKE` expression + +```scala +likeExpr[MySQL]("field") +// res: field LIKE 'a%' +``` \ No newline at end of file diff --git a/doc/postgresql-dialect-evaluating.md b/doc/postgresql-dialect-evaluating.md new file mode 100644 index 0000000..8c851e1 --- /dev/null +++ b/doc/postgresql-dialect-evaluating.md @@ -0,0 +1,63 @@ +# PostgreSQL Dialect + +The `PostgreSQL` dialect is not yet available. The examples provided illustrate what the output would look like if a PostgreSQL +dialect were implemented. The results are intentionally generated using non-official expressions to highlight the +differences that occur when applying the same criteria expression across different dialects. + +We are currently working on the official PostgreSQL dialect. In the meantime, you can use the SQL dialect to implement your +own PostgreSQL dialect. + +For these examples we will use the defined expressions in +the [Defining Criteria Expressions](defining-criteria-expressions.md) document. + +```scala +expr[PostgreSQL] +// res: (('field1' <= 3) AND ('field2' <= 4)) OR ('field3' = c) +``` + +## Case of use + +```scala +ageCriteria[PostgreSQL] +// res: ('age' > 18) AND ('age' < 65) +``` + +```scala +refCriteria[PostgreSQL]("id", UUID.randomUUID()) +// res: 'id' = 473a1484-afd8-4ebd-8eb4-312fd51510b3 +``` + +### Using `IS NULL` expression + +```scala +isNullExpr[PostgreSQL]("field") +// res: 'field' IS NULL +``` + +### Using `NOT` expression + +```scala +notExpr[PostgreSQL]("field") +// res: (NOT ('field' > 2)) AND ('field' <= 10) +``` + +### Using `BETWEEN` expression + +```scala +betweenExpr[PostgreSQL]("field") +// res: 'field' BETWEEN 100 TO 150 +``` + +### Using `ARRAY` expression + +```scala +arrayExpr[PostgreSQL]("field") +// res: 'field' IN (1, 2, 3) +``` + +### Using `LIKE` expression + +```scala +likeExpr[PostgreSQL]("field") +// res: 'field' LIKE a% +``` \ No newline at end of file