Skip to content

Commit

Permalink
Add support for unary NOT conjuction (#32)
Browse files Browse the repository at this point in the history
close #22
  • Loading branch information
rafafrdz authored Jul 21, 2024
1 parent cd7ba6d commit 9301c8f
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 101 deletions.
Original file line number Diff line number Diff line change
@@ -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] =
Expand All @@ -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))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}
Original file line number Diff line number Diff line change
@@ -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])
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"))

Expand Down
Original file line number Diff line number Diff line change
@@ -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}"
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 9301c8f

Please sign in to comment.