Last active
August 29, 2015 14:17
-
-
Save kryptt/b2f7ef6719a252dd316c to your computer and use it in GitHub Desktop.
Playing with the QueryFragment Idea
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package msw.util | |
import doobie.imports.{Composite, Query0} | |
import doobie.hi.connection.{ prepareStatement, prepareQueryAnalysis, prepareQueryAnalysis0, process => cprocess } | |
import doobie.hi.preparedstatement.{ set, executeQuery } | |
import doobie.hi.resultset.{ getUnique, getOption } | |
import scalaz.syntax.monad._ | |
import shapeless._ | |
import shapeless.ops.hlist.Prepend | |
trait QueryFragment { self => | |
type L <: HList | |
def query: String | |
def params: L | |
def ++[POut <: HList](other: QueryFragment)(implicit P: Prepend.Aux[self.L, other.L, POut]): QueryFragment = append(other) | |
def append[POut <: HList](other: QueryFragment)(implicit P: Prepend.Aux[self.L, other.L, POut]): QueryFragment = new QueryFragment { | |
type L = POut | |
val query = self.query + other.query | |
val params = self.params ++ other.params | |
} | |
def toQuery0[B](stackTrace: Option[StackTraceElement])(implicit CL: Composite[L], CB: Composite[B]): Query0[B] = new Query0[B] { | |
val sql = query | |
val stackFrame = stackTrace | |
val analysis = prepareQueryAnalysis[L, B](sql) | |
val process = cprocess[B](sql, set(params)) | |
def unique = prepareStatement(query)(set(params) >> executeQuery(getUnique[B])) | |
def option = prepareStatement(query)(set(params) >> executeQuery(getOption[B])) | |
} | |
} | |
object QueryFragment { | |
type Aux[Out <: HList] = QueryFragment { type L = Out } | |
def apply[P <: HList](q: String, p: P): Aux[P] = new QueryFragment { | |
type L = P | |
val query = q | |
val params = p | |
} | |
} |
revision 3 brought back my initial problem:
def listQuery(filter: Option[String], s: Option[SortInfo], p: PageInfo, withRoles: Option[Array[String]], withoutRoles: Option[Array[String]]): Query0[User.ListUserRoles] = {
implicit def orComposite: Composite[Option[Role]] = implicitly[Composite[Option[Role]]]
(QueryFragment("""SELECT u.id, u.email, u.firstName, u.lastName, u.verified, core.last_login(u.id), u.active,
r.id, r.code, COALESCE(ur.ig_id, r.ig_id) AS ig_id, ur.period_id
FROM core.users u LEFT JOIN core.resource_roles(u.id) ur ON true
LEFT JOIN core.roles r ON ur.role_id = r.id """, HNil) ++
filter.fold(QueryFragment("", HNil))(f => QueryFragment("WHERE u.email ILIKE ? OR u.firstName ILIKE ? OR u.lastName ILIKE ? ", s"%$f%" :: s"%$f%" :: s"%$f%" :: HNil)) ++
QueryFragment(s"ORDER BY u.${s.fold("id")(_.by)} ${s.fold("ASC")(_.order)} OFFSET ? LIMIT ?", p :: HNil))
.toQuery0[User.ListUserRoles](stackFrame)
}
fails to compile with:
[error] /home/rhansen/workspace/msw/app/src/main/scala/core/user-queries.scala:44: type mismatch;
[error] found : shapeless.::[String,shapeless.::[String,shapeless.::[String,shapeless.HNil]]]
[error] required: shapeless.HNil.type
[error] filter.fold(QueryFragment("", HNil))(f => QueryFragment("WHERE u.email ILIKE ? OR u.firstName ILIKE ? OR u.lastName ILIKE ? ", s"%$f%" :: s"%$f%" :: s"%$f%" :: HNil)) ++
[error] ^
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
revision 2 is a path dependent alternative which should help with external manipulations