diff --git a/core/src/main/scala/io/github/rafafrdz/criteria4s/core/Conjuction.scala b/core/src/main/scala/io/github/rafafrdz/criteria4s/core/Conjuction.scala index b7766f7..dc70ae7 100644 --- a/core/src/main/scala/io/github/rafafrdz/criteria4s/core/Conjuction.scala +++ b/core/src/main/scala/io/github/rafafrdz/criteria4s/core/Conjuction.scala @@ -1,22 +1,27 @@ package io.github.rafafrdz.criteria4s.core +import io.github.rafafrdz.criteria4s.core import io.github.rafafrdz.criteria4s.core.Criteria._ -import io.github.rafafrdz.criteria4s.instances.builder.BuilderBinary +import io.github.rafafrdz.criteria4s.instances.builder._ -trait Conjuction[T <: CriteriaTag] { +trait ConjuctionBinary[T <: CriteriaTag] { def eval(cr1: Criteria[T], cr2: Criteria[T]): Criteria[T] } -object Conjuction { +trait ConjuctionUnary[T <: CriteriaTag] { + def eval(cr: Criteria[T]): Criteria[T] +} + +object ConjuctionBinary { def eval[T <: CriteriaTag](cr1: Criteria[T], cr2: Criteria[T])(implicit - ev: Conjuction[T] + ev: ConjuctionBinary[T] ): Criteria[T] = ev.eval(cr1, cr2) - trait OR[T <: CriteriaTag] extends Conjuction[T] + trait OR[T <: CriteriaTag] extends ConjuctionBinary[T] - trait AND[T <: CriteriaTag] extends Conjuction[T] + trait AND[T <: CriteriaTag] extends ConjuctionBinary[T] implicit val orBuilder: BuilderBinary[OR] = new BuilderBinary[OR] { override def build[T <: CriteriaTag](F: (String, String) => String): OR[T] = @@ -29,3 +34,14 @@ object Conjuction { } } + +object ConjuctionUnary { + + trait NOT[T <: CriteriaTag] extends ConjuctionUnary[T] + + implicit val notBuilder: BuilderUnary[NOT] = new BuilderUnary[NOT] { + override def build[T <: core.CriteriaTag](F: String => String): NOT[T] = + (cr: Criteria[T]) => pure(F(cr.value)) + } + +} diff --git a/core/src/main/scala/io/github/rafafrdz/criteria4s/core/package.scala b/core/src/main/scala/io/github/rafafrdz/criteria4s/core/package.scala index 3133b91..5534041 100644 --- a/core/src/main/scala/io/github/rafafrdz/criteria4s/core/package.scala +++ b/core/src/main/scala/io/github/rafafrdz/criteria4s/core/package.scala @@ -4,9 +4,11 @@ package object core { type CriteriaTag = Criteria.CriteriaTag - type AND[T <: CriteriaTag] = Conjuction.AND[T] + type AND[T <: CriteriaTag] = ConjuctionBinary.AND[T] - type OR[T <: CriteriaTag] = Conjuction.OR[T] + type OR[T <: CriteriaTag] = ConjuctionBinary.OR[T] + + type NOT[T <: CriteriaTag] = ConjuctionUnary.NOT[T] type GT[T <: CriteriaTag] = PredicateBinary.GT[T] diff --git a/core/src/main/scala/io/github/rafafrdz/criteria4s/extensions/conjunctions.scala b/core/src/main/scala/io/github/rafafrdz/criteria4s/extensions/conjunctions.scala index 9b4f123..fefb08b 100644 --- a/core/src/main/scala/io/github/rafafrdz/criteria4s/extensions/conjunctions.scala +++ b/core/src/main/scala/io/github/rafafrdz/criteria4s/extensions/conjunctions.scala @@ -1,14 +1,15 @@ package io.github.rafafrdz.criteria4s.extensions import io.github.rafafrdz.criteria4s.{functions => F} -import io.github.rafafrdz.criteria4s.core.Conjuction._ -import io.github.rafafrdz.criteria4s.core.{Criteria, CriteriaTag} +import io.github.rafafrdz.criteria4s.core._ trait conjunctions { implicit class CriteriaOpsImplicit[T <: CriteriaTag](c: Criteria[T]) { def and(other: Criteria[T])(implicit H: AND[T]): Criteria[T] = F.and(c, other) + def &&(other: Criteria[T])(implicit H: AND[T]): Criteria[T] = F.and(c, other) def or(other: Criteria[T])(implicit H: OR[T]): Criteria[T] = F.or(c, other) + def ||(other: Criteria[T])(implicit H: OR[T]): Criteria[T] = F.or(c, other) } } diff --git a/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/conjunctions.scala b/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/conjunctions.scala index 626ed10..3ae3057 100644 --- a/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/conjunctions.scala +++ b/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/conjunctions.scala @@ -1,15 +1,25 @@ package io.github.rafafrdz.criteria4s.functions -import io.github.rafafrdz.criteria4s.core.{Criteria, CriteriaTag} -import io.github.rafafrdz.criteria4s.core.Conjuction._ +import io.github.rafafrdz.criteria4s.core._ private[functions] trait conjunctions { def and[T <: CriteriaTag: AND](cr1: Criteria[T], cr2: Criteria[T]): Criteria[T] = cond[T, AND[T]](cr1, cr2) + def &&[T <: CriteriaTag: AND](cr1: Criteria[T], cr2: Criteria[T]): Criteria[T] = + and(cr1, cr2) + def or[T <: CriteriaTag: OR](cr1: Criteria[T], cr2: Criteria[T]): Criteria[T] = cond[T, OR[T]](cr1, cr2) + def ||[T <: CriteriaTag: OR](cr1: Criteria[T], cr2: Criteria[T]): Criteria[T] = + or(cr1, cr2) + + def not[T <: CriteriaTag: NOT](cr: Criteria[T]): Criteria[T] = + cond[T, NOT[T]](cr) + + def !![T <: CriteriaTag: NOT](cr: Criteria[T]): Criteria[T] = not(cr) + } object conjunctions extends conjunctions diff --git a/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/package.scala b/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/package.scala index 4288ee2..2d43ae4 100644 --- a/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/package.scala +++ b/core/src/main/scala/io/github/rafafrdz/criteria4s/functions/package.scala @@ -17,9 +17,14 @@ package object functions extends predicates with conjunctions { ): Criteria[T] = H.eval(ref) - def cond[T <: CriteriaTag, H <: Conjuction[T]](cr1: Criteria[T], cr2: Criteria[T])(implicit + def cond[T <: CriteriaTag, H <: ConjuctionBinary[T]](cr1: Criteria[T], cr2: Criteria[T])(implicit H: H ): Criteria[T] = H.eval(cr1, cr2) + def cond[T <: CriteriaTag, H <: ConjuctionUnary[T]](cr: Criteria[T])(implicit + H: H + ): Criteria[T] = + H.eval(cr) + } diff --git a/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/NotExample.scala b/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/NotExample.scala new file mode 100644 index 0000000..8432b45 --- /dev/null +++ b/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/NotExample.scala @@ -0,0 +1,43 @@ +package io.github.rafafrdz.criteria4s.examples + +import io.github.rafafrdz.criteria4s.core._ +import io.github.rafafrdz.criteria4s.dialect.mongodb.MongoDB +import io.github.rafafrdz.criteria4s.examples.datastores._ +import io.github.rafafrdz.criteria4s.extensions._ +import io.github.rafafrdz.criteria4s.functions._ + +object NotExample extends App { + + val simpleNot: Criteria[Postgres] = not(col[Postgres]("a").between(range(1, 10))) + + val symbolNot: Criteria[Postgres] = !!(col[Postgres]("a").between(range(1, 10))) + + def taglessFinalNotExample[ + T <: CriteriaTag: GT: NOT: Show[Column, *]: Show[(Int, Int), *] + ]: Criteria[T] = + not(col[T]("column") gt lit(100)) + + def taglessFinalNotSymbolExample[ + T <: CriteriaTag: BETWEEN: NOT: Show[Column, *]: Show[(Int, Int), *] + ]: Criteria[T] = + !!(col[T]("column") between range(100, 150)) + + def weirdExampleWithNot[ + T <: CriteriaTag: NOT: GT: LEQ: AND: Show[Column, *]: Show[(Int, Int), *] + ]: Criteria[T] = + not(col[T]("column") gt lit(2)) && (col[T]("column") leq lit(10)) + + println(simpleNot) + println(symbolNot) + println(taglessFinalNotExample[Postgres]) + println(taglessFinalNotExample[WeirdDatastore]) + println(taglessFinalNotExample[MongoDB]) + + println(taglessFinalNotSymbolExample[Postgres]) + println(taglessFinalNotSymbolExample[WeirdDatastore]) + println(taglessFinalNotSymbolExample[MongoDB]) + + println(weirdExampleWithNot[Postgres]) + println(weirdExampleWithNot[WeirdDatastore]) + println(weirdExampleWithNot[MongoDB]) +} diff --git a/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/datastores/WeirdDatastore.scala b/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/datastores/WeirdDatastore.scala index 96a156b..590590f 100644 --- a/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/datastores/WeirdDatastore.scala +++ b/examples/src/main/scala/io/github/rafafrdz/criteria4s/examples/datastores/WeirdDatastore.scala @@ -1,9 +1,6 @@ package io.github.rafafrdz.criteria4s.examples.datastores -import io.github.rafafrdz.criteria4s.core.Conjuction._ -import io.github.rafafrdz.criteria4s.core.{Column, CriteriaTag, Show} -import io.github.rafafrdz.criteria4s.core.PredicateBinary._ -import io.github.rafafrdz.criteria4s.core.PredicateUnary._ +import io.github.rafafrdz.criteria4s.core._ import io.github.rafafrdz.criteria4s.instances._ trait WeirdDatastore extends CriteriaTag @@ -24,36 +21,23 @@ object WeirdDatastore { implicit val showColumn: Show[Column, WeirdDatastore] = Show.create(_.colName) - implicit val ltPred: LT[WeirdDatastore] = build[WeirdDatastore, LT](wope("<")) - - implicit val gtPred: GT[WeirdDatastore] = build[WeirdDatastore, GT](wope(">")) - - implicit val orOp: OR[WeirdDatastore] = build[WeirdDatastore, OR](wope("OR")) - - implicit val andOp: AND[WeirdDatastore] = build[WeirdDatastore, AND](wope("AND")) - - implicit val eqPred: EQ[WeirdDatastore] = build[WeirdDatastore, EQ](wope("=")) - - implicit val neqPred: NEQ[WeirdDatastore] = build[WeirdDatastore, NEQ](wope("!=")) - - implicit val leqPred: LEQ[WeirdDatastore] = build[WeirdDatastore, LEQ](wope("<=")) - - implicit val geqPred: GEQ[WeirdDatastore] = build[WeirdDatastore, GEQ](wope(">=")) - - implicit val likePred: LIKE[WeirdDatastore] = build[WeirdDatastore, LIKE](wope("LIKE")) - - implicit val inPred: IN[WeirdDatastore] = build[WeirdDatastore, IN](wope("IN")) - - implicit val notinPred: NOTIN[WeirdDatastore] = build[WeirdDatastore, NOTIN](wope("NOT IN")) - + implicit val andConj: AND[WeirdDatastore] = build[WeirdDatastore, AND](wope("AND")) + implicit val orConj: OR[WeirdDatastore] = build[WeirdDatastore, OR](wope("OR")) + implicit val notConj: NOT[WeirdDatastore] = build[WeirdDatastore, NOT](wope1("NOT")) + implicit val eqPred: EQ[WeirdDatastore] = build[WeirdDatastore, EQ](wope("=")) + implicit val neqPred: NEQ[WeirdDatastore] = build[WeirdDatastore, NEQ](wope("!=")) + implicit val gtPred: GT[WeirdDatastore] = build[WeirdDatastore, GT](wope(">")) + implicit val geqPred: GEQ[WeirdDatastore] = build[WeirdDatastore, GEQ](wope(">=")) + implicit val ltPred: LT[WeirdDatastore] = build[WeirdDatastore, LT](wope("<")) + implicit val leqPred: LEQ[WeirdDatastore] = build[WeirdDatastore, LEQ](wope("<=")) + implicit val inPred: IN[WeirdDatastore] = build[WeirdDatastore, IN](wope("IN")) + implicit val notinPred: NOTIN[WeirdDatastore] = build[WeirdDatastore, NOTIN](wope("NOT IN")) + implicit val likePred: LIKE[WeirdDatastore] = build[WeirdDatastore, LIKE](wope("LIKE")) implicit val isnullPred: ISNULL[WeirdDatastore] = build[WeirdDatastore, ISNULL](wope1("IS NULL")) - implicit val isnotnullPred: ISNOTNULL[WeirdDatastore] = build[WeirdDatastore, ISNOTNULL](wope1("IS NOT NULL")) - implicit val betweenPred: BETWEEN[WeirdDatastore] = build[WeirdDatastore, BETWEEN](wope("BETWEEN")) - implicit val notbetweenPred: NOTBETWEEN[WeirdDatastore] = build[WeirdDatastore, NOTBETWEEN](wope("NOT BETWEEN")) diff --git a/mongodb/src/main/scala/io/github/rafafrdz/criteria4s/dialect/mongodb/MongoDB.scala b/mongodb/src/main/scala/io/github/rafafrdz/criteria4s/dialect/mongodb/MongoDB.scala index 02cece6..378318c 100644 --- a/mongodb/src/main/scala/io/github/rafafrdz/criteria4s/dialect/mongodb/MongoDB.scala +++ b/mongodb/src/main/scala/io/github/rafafrdz/criteria4s/dialect/mongodb/MongoDB.scala @@ -1,22 +1,29 @@ package io.github.rafafrdz.criteria4s.dialect.mongodb -import io.github.rafafrdz.criteria4s.core.Conjuction.{AND, OR} -import io.github.rafafrdz.criteria4s.core.{Column, CriteriaTag, Show} -import io.github.rafafrdz.criteria4s.core.PredicateBinary._ -import io.github.rafafrdz.criteria4s.core.PredicateUnary._ +import io.github.rafafrdz.criteria4s.core._ import io.github.rafafrdz.criteria4s.instances._ trait MongoDB extends CriteriaTag object MongoDB { - private def binaryPredExpr(symbol: String): (String, String) => String = + private def predExpr(symbol: String): (String, String) => String = (left, right) => s"{$left: {$$$symbol: $right}}" - private def opExpr(symbol: String): (String, String) => String = - (left, right) => s"{$$$symbol: [{$left}, {$right}]}" + private def conjExpr(symbol: String): (String, String) => String = + (left, right) => s"{$$$symbol: [$left, $right]}" + + private def conjExpr1(symbol: String): String => String = + right => s"{$$$symbol: $right}" + + private def notExpr: String => String = + ref => { + val (left, right): (String, String) = + (ref.takeWhile(c => c != ':'), ref.dropWhile(c => c != ':')) + s"$left: {$$not$right}" + } private val likeExpr: (String, String) => String = - (value, regex) => binaryPredExpr("regex")(value, s"\\$regex\\") + (value, regex) => predExpr("regex")(value, s"\\$regex\\") private val isNullExpr: String => String = ref => s"{$ref: null}" @@ -27,24 +34,28 @@ object MongoDB { private val betweenExample: (String, String) => String = (ref, range) => s"{$ref: $range}" - implicit val showColumn: Show[Column, MongoDB] = Show.create(col => s"\"${col.colName}\"") + implicit val showColumn: Show[Column, MongoDB] = + Show.create(col => s"\"${col.colName}\"") + implicit def showSeq[T](implicit show: Show[T, MongoDB]): Show[Seq[T], MongoDB] = Show.create(_.map(show.show).mkString("[", ", ", "]")) + implicit def betweenShow[T](implicit show: Show[T, MongoDB]): Show[(T, T), MongoDB] = Show.create { case (l, r) => s"{ $$gte: ${show.show(l)}, $$lt: ${show.show(r)} }" } trait MongoDBExpr[T <: MongoDB] { - implicit val ltPred: LT[T] = build[T, LT](binaryPredExpr("lt")) - implicit val gtPred: GT[T] = build[T, GT](binaryPredExpr("gt")) - implicit val orOp: OR[T] = build[T, OR](opExpr("or")) - implicit val andOp: AND[T] = build[T, AND](opExpr("and")) - implicit val eqPred: EQ[T] = build[T, EQ](binaryPredExpr("eq")) - implicit val neqPred: NEQ[T] = build[T, NEQ](binaryPredExpr("ne")) - implicit val leqPred: LEQ[T] = build[T, LEQ](binaryPredExpr("lte")) - implicit val geqPred: GEQ[T] = build[T, GEQ](binaryPredExpr("gte")) + implicit val andConj: AND[T] = build[T, AND](conjExpr("and")) + implicit val orConj: OR[T] = build[T, OR](conjExpr("or")) + implicit val notConj: NOT[T] = build[T, NOT](notExpr) + implicit val eqPred: EQ[T] = build[T, EQ](predExpr("eq")) + implicit val neqPred: NEQ[T] = build[T, NEQ](predExpr("ne")) + implicit val gtPred: GT[T] = build[T, GT](predExpr("gt")) + implicit val geqPred: GEQ[T] = build[T, GEQ](predExpr("gte")) + implicit val ltPred: LT[T] = build[T, LT](predExpr("lt")) + implicit val leqPred: LEQ[T] = build[T, LEQ](predExpr("lte")) + implicit val inPred: IN[T] = build[T, IN](predExpr("in")) + implicit val notInPred: NOTIN[T] = build[T, NOTIN](predExpr("nin")) implicit val likePred: LIKE[T] = build[T, LIKE](likeExpr) - implicit val inPred: IN[T] = build[T, IN](binaryPredExpr("in")) - implicit val notInPred: NOTIN[T] = build[T, NOTIN](binaryPredExpr("nin")) implicit val isNullPred: ISNULL[T] = build[T, ISNULL](isNullExpr) implicit val isNotNullPred: ISNOTNULL[T] = build[T, ISNOTNULL](isNotNullExpr) implicit val betweenPred: BETWEEN[T] = build[T, BETWEEN](betweenExample) diff --git a/sql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/sql/SQL.scala b/sql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/sql/SQL.scala index 0598964..3a55e2c 100644 --- a/sql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/sql/SQL.scala +++ b/sql/src/main/scala/io/github/rafafrdz/criteria4s/dialect/sql/SQL.scala @@ -1,29 +1,34 @@ package io.github.rafafrdz.criteria4s.dialect.sql -import io.github.rafafrdz.criteria4s.core.Conjuction._ -import io.github.rafafrdz.criteria4s.core.PredicateBinary._ -import io.github.rafafrdz.criteria4s.core.PredicateUnary._ -import io.github.rafafrdz.criteria4s.core.{Column, CriteriaTag, Show} +import io.github.rafafrdz.criteria4s.core._ import io.github.rafafrdz.criteria4s.instances._ trait SQL extends CriteriaTag object SQL { - private def opExpr(symbol: String)(left: String, right: String): String = + private def conjExpr(symbol: String)(left: String, right: String): String = s"($left) $symbol ($right)" + private def conjExpr1(symbol: String)(right: String): String = + s"$symbol ($right)" + private def predExpr(symbol: String)(left: String, right: String): String = s"$left $symbol $right" - private def predExpr1(symbol: String)(value: String): String = s"$value $symbol" + private def predExpr1(symbol: String)(value: String): String = + s"$value $symbol" - implicit val showColumn: Show[Column, SQL] = Show.create(col => s"'${col.colName}'") + implicit val showColumn: Show[Column, SQL] = + Show.create(col => s"'${col.colName}'") trait SQLExpr[T <: SQL] { - protected def opExpr(symbol: String): (String, String) => String = - (left: String, right: String) => SQL.opExpr(symbol)(left, right) + protected def conjExpr(symbol: String): (String, String) => String = + (left: String, right: String) => SQL.conjExpr(symbol)(left, right) + + protected def conjExpr1(symbol: String): String => String = + (right: String) => SQL.conjExpr1(symbol)(right) protected def predExpr(symbol: String): (String, String) => String = (left: String, right: String) => SQL.predExpr(symbol)(left, right) @@ -31,37 +36,22 @@ object SQL { protected def predExpr1(symbol: String): String => String = (value: String) => SQL.predExpr1(symbol)(value) - implicit val ltPred: LT[T] = build[T, LT](predExpr("<")) - - implicit val gtPred: GT[T] = build[T, GT](predExpr(">")) - - implicit val orOp: OR[T] = build[T, OR](opExpr("OR")) - - implicit val andOp: AND[T] = build[T, AND](opExpr("AND")) - - implicit val eqPred: EQ[T] = build[T, EQ](predExpr("=")) - - implicit val neqPred: NEQ[T] = build[T, NEQ](predExpr("!=")) - - implicit val leqPred: LEQ[T] = build[T, LEQ](predExpr("<=")) - - implicit val geqPred: GEQ[T] = build[T, GEQ](predExpr(">=")) - - implicit val likePred: LIKE[T] = build[T, LIKE](predExpr("LIKE")) - - implicit val inPred: IN[T] = build[T, IN](predExpr("IN")) - - implicit val notinPred: NOTIN[T] = build[T, NOTIN](predExpr("NOT IN")) - - implicit val isnullPred: ISNULL[T] = build[T, ISNULL](predExpr1("IS NULL")) - - implicit val isnotnullPred: ISNOTNULL[T] = - build[T, ISNOTNULL](predExpr1("IS NOT NULL")) - - implicit val betweenPred: BETWEEN[T] = build[T, BETWEEN](predExpr("BETWEEN")) - - implicit val notbetweenPred: NOTBETWEEN[T] = - build[T, NOTBETWEEN](predExpr("NOT BETWEEN")) + implicit val andConj: AND[T] = build[T, AND](conjExpr("AND")) + implicit val orConj: OR[T] = build[T, OR](conjExpr("OR")) + implicit val notConj: NOT[T] = build[T, NOT](conjExpr1("NOT")) + implicit val eqPred: EQ[T] = build[T, EQ](predExpr("=")) + implicit val neqPred: NEQ[T] = build[T, NEQ](predExpr("!=")) + implicit val gtPred: GT[T] = build[T, GT](predExpr(">")) + implicit val geqPred: GEQ[T] = build[T, GEQ](predExpr(">=")) + implicit val ltPred: LT[T] = build[T, LT](predExpr("<")) + implicit val leqPred: LEQ[T] = build[T, LEQ](predExpr("<=")) + implicit val inPred: IN[T] = build[T, IN](predExpr("IN")) + implicit val notinPred: NOTIN[T] = build[T, NOTIN](predExpr("NOT IN")) + implicit val likePred: LIKE[T] = build[T, LIKE](predExpr("LIKE")) + implicit val isnullPred: ISNULL[T] = build[T, ISNULL](predExpr1("IS NULL")) + implicit val isnotnullPred: ISNOTNULL[T] = build[T, ISNOTNULL](predExpr1("IS NOT NULL")) + implicit val betweenPred: BETWEEN[T] = build[T, BETWEEN](predExpr("BETWEEN")) + implicit val notbetweenPred: NOTBETWEEN[T] = build[T, NOTBETWEEN](predExpr("NOT BETWEEN")) } }