Skip to content

Commit

Permalink
work
Browse files Browse the repository at this point in the history
  • Loading branch information
ValdemarGr committed Sep 5, 2023
1 parent efe9741 commit a5f3fbf
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 94 deletions.
48 changes: 21 additions & 27 deletions modules/relational/src/main/scala/gql/relational/ExampleImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,33 +63,27 @@ class MySchema(pool: Resource[IO, Session[IO]]) {
"id" -> query(_.selId)
)

implicit lazy val entity2: Type[IO, QueryResult[EntityTable2]] = tpe[IO, QueryResult[EntityTable2]](
"Entity",
"name" -> query(_.selName),
"id" -> query(_.selId),
"age" -> query(_.selAge),
"height" -> query(_.selHeight),
"pets" -> cont { e =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} = ${e.id}".apply(Void))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield p
}
/*"pets" -> queryAndThen[IO, Lambda[X => X], EntityTable2, UUID, List[QueryResult[PetTable]]](_.selId)(
_.andThen(
resolveQuery(
EmptyableArg.Empty,
{ (is: NonEmptyList[UUID], _: Unit) =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} in ${uuid.list(is.size).values}".apply(is.toList))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield (pe.selEntityId, p)
},
SkunkRunQuery(pool)
)
)
)*/
)
implicit lazy val entity2: Type[IO, QueryResult[EntityTable2]] = relBuilder[IO, EntityTable2]{ b =>
b.tpe(
"Entity",
"name" -> b.query(_.selName),
"id" -> b.query(_.selId),
"age" -> b.query(_.selAge),
"height" -> b.query(_.selHeight),
"pets" -> b.cont { e =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} = ${e.id}".apply(Void))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield p
},
"pets" -> b.contBoundary(pool)(_.selId) { is =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} in ${uuid.list(is.size).values}".apply(is.toList))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield (pe.selEntityId, p)
}
)
}

implicit lazy val entity: Type[IO, QueryResult[EntityTable]] = tpe[IO, QueryResult[EntityTable]](
"Entity",
Expand Down
120 changes: 82 additions & 38 deletions modules/relational/src/main/scala/gql/relational/QueryAlgebra.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import cats.arrow.FunctionK
import gql.ast._
import gql.EmptyableArg
import gql.resolver.FieldMeta
import gql._

trait QueryAlgebra {
import QueryAlgebra.{QueryState => _, QueryStateImpl => _, _}
Expand All @@ -30,21 +29,30 @@ trait QueryAlgebra {
def apply[A](query: Frag, decoder: Decoder[A], connection: Connection[F]): F[List[A]]
}

def resolveQuery[F[_]: Queryable, G[_], I, B, ArgType](
def resolveQueryFull[F[_]: Queryable, G[_], H[_], I, B, ArgType](
toplevelArg: EmptyableArg[ArgType],
q: (NonEmptyList[I], ArgType) => Query[G, (Query.Select[I], B)],
connection: Connection[F]
)(implicit F: Applicative[F]): Resolver[F, I, G[QueryResult[B]]] =
compileToResolver[F, G, I, ArgType, Either[String, G[QueryResult[B]]]](toplevelArg) { (xs, at, fm) =>
)(implicit F: Applicative[F], H: Reassociateable[H]): Resolver[F, H[I], H[G[QueryResult[B]]]] = {
implicit val T: Traverse[H] = H.traverse
compileToResolver[F, G, H, I, ArgType, Either[String, G[QueryResult[B]]]](toplevelArg) { (xs, at, fm) =>
evalQuery(xs, fm, q(xs, at), connection)
}.emap(_.toIor)
}.emap(_.traverse(_.toIor))
}

def resolveQuery[F[_]: Queryable: Applicative, G[_], I, B, ArgType](
toplevelArg: EmptyableArg[ArgType],
q: (NonEmptyList[I], ArgType) => Query[G, (Query.Select[I], B)],
connection: Connection[F]
): Resolver[F, I, G[QueryResult[B]]] =
resolveQueryFull[F, G, Id, I, B, ArgType](toplevelArg, q, connection)

def resolveQuerySingle[F[_]: Queryable, G[_], I, B, ArgType](
toplevelArg: EmptyableArg[ArgType],
q: (I, ArgType) => Query[G, B],
connection: Connection[F]
)(implicit F: Applicative[F]): Resolver[F, I, G[QueryResult[B]]] =
compileToResolver[F, G, I, ArgType, Either[String, G[QueryResult[B]]]](toplevelArg) { (xs, at, fm) =>
compileToResolver[F, G, Id, I, ArgType, Either[String, G[QueryResult[B]]]](toplevelArg) { (xs, at, fm) =>
xs.toList
.traverse { x =>
val baseQuery = q(x, at)
Expand Down Expand Up @@ -76,7 +84,7 @@ trait QueryAlgebra {
println(s"running:\n${frag.asInstanceOf[skunk.AppliedFragment].fragment.sql}\n")
val result = queryable(frag, decoder, connection)
result
.map{ xs => println(s"got ${xs.size} results"); xs }
.map { xs => println(s"got ${xs.size} results"); xs }
.map(_.groupMap { case (k, _) => k } { case (_, v) => v })
.map(_.fmap(done.reassoc))
}
Expand Down Expand Up @@ -125,7 +133,7 @@ trait QueryAlgebra {
case class Select[A](cols: Chain[Frag], decoder: Decoder[A]) extends Query[Lambda[X => X], Select[A]]
object Select {
implicit lazy val applicativeForSelect: Applicative[Select] = new Applicative[Select] {
override def pure[A](x: A): Select[A] =
override def pure[A](x: A): Select[A] =
Select(Chain.empty, Applicative[Decoder].pure(x))

override def ap[A, B](ff: Select[A => B])(fa: Select[A]): Select[B] =
Expand Down Expand Up @@ -163,30 +171,36 @@ trait QueryAlgebra {
done: Interpreter.Done[G, A, B],
rootQueryValue: C
)
def compileToResolver[F[_], G[_], I, ArgType, O](toplevelArg: EmptyableArg[ArgType])(
def compileToResolver[F[_], G[_], H[_]: Traverse, I, ArgType, O](toplevelArg: EmptyableArg[ArgType])(
compiler: (NonEmptyList[I], ArgType, FieldMeta[F]) => F[Map[I, O]]
)(implicit F: Applicative[F]): Resolver[F, I, O] = {
type K = ((ArgType, FieldMeta[F]), I)
)(implicit F: Applicative[F]): Resolver[F, H[I], H[O]] = {
type K[V[_]] = ((ArgType, FieldMeta[F]), V[I])
Resolver
.meta[F, I]
.meta[F, H[I]]
.andThen(toplevelArg.addArg)
.tupleIn
.map(Set(_))
.andThen(Resolver.inlineBatch[F, K, O] { xs =>
val lst = xs.toList
lst.toNel
.traverse { nel =>
val rev = nel.map { case (v, k) => k -> v }.toList.toMap
val ((a, fm), _) = nel.head
val inputs = nel.map { case (_, i) => i }
compiler(inputs, a, fm)
.map(_.toList.mapFilter { case (i, o) => rev.get(i).tupleRight(i).tupleRight(o) })
.map(_.toMap)
.andThen(
Resolver
.id[F, K[H]]
.map { case (k, h) => (h.toList tupleLeft k).toSet }
.andThen(Resolver.inlineBatch[F, K[Id], O] { xs =>
val lst = xs.toList
lst.toNel
.traverse { nel =>
val rev = nel.map { case (v, k) => k -> v }.toList.toMap
val ((a, fm), _) = nel.head
val inputs = nel.map { case (_, i) => i }
compiler(inputs, a, fm)
.map(_.toList.mapFilter { case (i, o) => rev.get(i).tupleRight(i).tupleRight(o) })
.map(_.toMap)
}
.map(_.getOrElse(Map.empty))
})
.tupleIn
.emap { case (o, (k, h)) =>
h.traverse(i => o.get((k, i)).toRightIor("Could not find query result"))
}
.map(_.getOrElse(Map.empty))
})
.tupleIn
.emap { case (o, i) => o.collectFirst { case ((_, k), v) if k == i => v }.toRightIor("Could not find query result") }
)
}

def resolvePreparedQuery[F[_], G[_], I, B, ArgType](
Expand Down Expand Up @@ -405,15 +419,10 @@ trait QueryAlgebra {
): QueryState[Lambda[X => G[H[X]]], (AK, BK), B] = {
type N[A] = qsa.T[qsb.T[A]]
val reassoc: Reassoc[N, (AK, BK)] = new Reassoc[N, (AK, BK)] {
def nestedTraverse = Nested.catsDataTraverseForNested[qsa.T, qsb.T](qsa.reassoc.traverse, qsb.reassoc.traverse)
def traverse = new Traverse[N] {
override def foldLeft[A, B](fa: N[A], b: B)(f: (B, A) => B): B =
nestedTraverse.foldLeft(Nested(fa), b)(f)
override def foldRight[A, B](fa: N[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
nestedTraverse.foldRight(Nested(fa), lb)(f)
override def traverse[G[_]: Applicative, A, B](fa: N[A])(f: A => G[B]): G[N[B]] =
nestedTraverse.traverse(Nested(fa))(f).map(_.value)
}
def traverse = Reassociateable.reassociateStep(
Reassociateable.reassocaiteForAnyTraverse(qsa.reassoc.traverse),
Reassociateable.reassocaiteForAnyTraverse(qsb.reassoc.traverse)
).traverse

override def apply[A](fa: List[((AK, BK), A)]): Either[String, N[List[A]]] = {
val ys = fa.map { case ((ak, bk), a) => (ak, (bk, a)) }
Expand Down Expand Up @@ -449,8 +458,8 @@ trait QueryAlgebra {
case p: Query.Pure[a] => Effect.pure(QueryStateImpl(JoinType.One.reassoc[Unit], ().pure[Decoder], p.a, FunctionK.id[G]))
case s: Query.Select[a] => Effect.pure(QueryStateImpl(JoinType.One.reassoc[Unit], ().pure[Decoder], s, FunctionK.id[G]))
case fm: Query.FlatMap[g, h, a, b] => handleFlatMap(fm)
case toList: Query.ToList[g, a] =>
collapseQuery(toList.fa).map{ qs =>
case toList: Query.ToList[g, a] =>
collapseQuery(toList.fa).map { qs =>
QueryStateImpl(
JoinType.joinTypeList.reassoc[Unit],
().pure[Decoder],
Expand Down Expand Up @@ -513,6 +522,7 @@ object QueryAlgebra {
type T[A] = I[A]
}

// Structure that aids re-construction of hierarchical data from flat data
trait Reassoc[G[_], Key] {
def traverse: Traverse[G]

Expand Down Expand Up @@ -589,3 +599,37 @@ object QueryAlgebra {
}
}
}

// A typeclass that exists for any traversable which is subject to derivation for nested effects
trait Reassociateable[F[_]] {
def traverse: Traverse[F]
}

object Reassociateable extends ReassociateableLowPrio1 {
implicit def reassociateStep[F[_], G[_]](implicit F: Reassociateable[F], G: Reassociateable[G]): Reassociateable[Lambda[X => F[G[X]]]] = {
type H[A] = F[G[A]]
new Reassociateable[H] {
val instance = Nested.catsDataTraverseForNested(F.traverse, G.traverse)
def traverse = new Traverse[H] {
override def foldLeft[A, B](fa: H[A], b: B)(f: (B, A) => B): B =
instance.foldLeft(Nested(fa), b)(f)
override def foldRight[A, B](fa: H[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
instance.foldRight(Nested(fa), lb)(f)
override def traverse[G[_]: Applicative, A, B](fa: H[A])(f: A => G[B]): G[H[B]] =
instance.traverse(Nested(fa))(f).map(_.value)
}
}
}
}

trait ReassociateableLowPrio1 extends ReassociateableLowPrio2 {
implicit lazy val reassociateForId: Reassociateable[Id] = new Reassociateable[Id] {
def traverse = Traverse[Id]
}
}

trait ReassociateableLowPrio2 {
implicit def reassocaiteForAnyTraverse[F[_]](implicit F: Traverse[F]): Reassociateable[F] = new Reassociateable[F] {
def traverse = F
}
}
94 changes: 65 additions & 29 deletions modules/relational/src/main/scala/gql/relational/QueryDsl.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
package gql.relational

import cats.effect._
import skunk.implicits._
import gql.ast._
import gql.dsl._
import cats.implicits._
import fs2.Pure
import skunk.codec._
import skunk._
import cats._
import gql.resolver.Resolver
import cats.data._
import scala.reflect.ClassTag
import java.util.UUID
import gql.Arg
import gql.EmptyableArg
import gql.resolver.FieldMeta
import cats.mtl.Stateful
import cats.mtl.Tell
import gql.{preparation => prep}
import cats.mtl.Raise
import gql.Schema
import gql.SchemaShape
import gql.Application
import natchez.TraceValue
import natchez.Kernel
import natchez.Span
import cats.arrow.FunctionK

trait QueryDsl extends QueryAlgebra { self =>
def query[F[_], G[_], A, B](f: A => Query[G, Query.Select[B]])(implicit
Expand Down Expand Up @@ -59,6 +41,12 @@ trait QueryDsl extends QueryAlgebra { self =>
)(implicit tpe: => Out[F, G[QueryResult[B]]]) =
Field(resolveQuery[F, G, I, B, Unit](EmptyableArg.Empty, (i, _) => q(i), connection), Eval.later(tpe))

final class BuildWithBuilder[F[_], A] {
def apply[B](f: RelationalFieldBuilder[F, A] => B): B = f(new RelationalFieldBuilder[F, A]())
}

def relBuilder[F[_], A] = new BuildWithBuilder[F, A]

def table[T <: Table[?]](f: String => T): TableAlg[T] = new TableAlg[T] {
def make: String => T = f
}
Expand Down Expand Up @@ -111,8 +99,8 @@ trait QueryDsl extends QueryAlgebra { self =>
gql.dsl.tpe[F, QueryResult[A]](name, hd, tl: _*)

def queryAndThen[G[_], B, C](f: A => Query[G, Query.Select[B]])(g: Resolver[F, G[B], G[B]] => Resolver[F, G[B], C])(
tpe: => Out[F, C]
): Field[F, QueryResult[A], C] =
tpe: => Out[F, C]
): Field[F, QueryResult[A], C] =
queryFull(EmptyableArg.Empty)((a, _) => f(a), g(Resolver.id[F, G[B]]))(tpe)

def queryAndThen[G[_], B, C, D](
Expand All @@ -122,17 +110,65 @@ trait QueryDsl extends QueryAlgebra { self =>
): Field[F, QueryResult[A], D] =
queryFull(EmptyableArg.Lift(a))((a, c) => f(a, c), g(Resolver.id[F, G[B]]))(tpe)

def contBoundary[G[_], H[_], B, C, D](connection: Connection[F])(f: A => Query[G, Query.Select[B]])(
continue: NonEmptyList[List[B]] => Query[H, (Query.Select[List[B]], C)]
)(implicit F: Applicative[F], Q: Queryable[F], tpe: => Out[F, H[QueryResult[C]]]) =
queryFull[F, List, A, B, Unit, H[QueryResult[C]]](EmptyableArg.Empty)((i, _) => Query.ToList(f(i)), Resolver.id[F, List[B]].andThen(
resolveQuery[F, H, List[B], C, Unit](
EmptyableArg.Empty,
(i, _) => continue(i),
connection
def contBoundaryFull[G[_]: Reassociateable, H[_], B, C, D, Arg1, Arg2](ea1: EmptyableArg[Arg1], connection: Connection[F])(
f: (A, Arg1) => Query[G, Query.Select[B]]
)(ea2: EmptyableArg[Arg2])(continue: (NonEmptyList[B], Arg2) => Query[H, (Query.Select[B], C)])(implicit
F: Applicative[F],
Q: Queryable[F],
tpe: => Out[F, G[H[QueryResult[C]]]]
) =
queryFull[F, G, A, B, Arg1, G[H[QueryResult[C]]]](ea1)(
f,
Resolver
.id[F, G[B]]
.andThen(
resolveQueryFull[F, H, G, B, C, Arg2](ea2, continue, connection)
)
)
)(tpe)

def contBoundary11[G[_]: Reassociateable, H[_], B, C, D, Arg1, Arg2](a1: Arg[Arg1], connection: Connection[F])(
f: (A, Arg1) => Query[G, Query.Select[B]]
)(a2: Arg[Arg2])(continue: (NonEmptyList[B], Arg2) => Query[H, (Query.Select[B], C)])(implicit
F: Applicative[F],
Q: Queryable[F],
tpe: => Out[F, G[H[QueryResult[C]]]]
) = {
implicit def tpe0: Out[F, G[H[QueryResult[C]]]] = tpe
contBoundaryFull[G, H, B, C, D, Arg1, Arg2](EmptyableArg.Lift(a1), connection)(f)(EmptyableArg.Lift(a2))(continue)
}

def contBoundary10[G[_]: Reassociateable, H[_], B, C, D, Arg1](a1: Arg[Arg1], connection: Connection[F])(
f: (A, Arg1) => Query[G, Query.Select[B]]
)(continue: NonEmptyList[B] => Query[H, (Query.Select[B], C)])(implicit
F: Applicative[F],
Q: Queryable[F],
tpe: => Out[F, G[H[QueryResult[C]]]]
) = {
implicit def tpe0: Out[F, G[H[QueryResult[C]]]] = tpe
contBoundaryFull[G, H, B, C, D, Arg1, Unit](EmptyableArg.Lift(a1), connection)(f)(EmptyableArg.Empty)((i, _) => continue(i))
}

def contBoundary01[G[_]: Reassociateable, H[_], B, C, D, Arg2](connection: Connection[F])(
f: A => Query[G, Query.Select[B]]
)(a2: Arg[Arg2])(continue: (NonEmptyList[B], Arg2) => Query[H, (Query.Select[B], C)])(implicit
F: Applicative[F],
Q: Queryable[F],
tpe: => Out[F, G[H[QueryResult[C]]]]
) = {
implicit def tpe0: Out[F, G[H[QueryResult[C]]]] = tpe
contBoundaryFull[G, H, B, C, D, Unit, Arg2](EmptyableArg.Empty, connection)((i, _) => f(i))(EmptyableArg.Lift(a2))(continue)
}

def contBoundary[G[_]: Reassociateable, H[_], B, C, D](connection: Connection[F])(
f: A => Query[G, Query.Select[B]]
)(continue: NonEmptyList[B] => Query[H, (Query.Select[B], C)])(implicit
F: Applicative[F],
Q: Queryable[F],
tpe: => Out[F, G[H[QueryResult[C]]]]
) = {
implicit def tpe0: Out[F, G[H[QueryResult[C]]]] = tpe
contBoundaryFull[G, H, B, C, D, Unit, Unit](EmptyableArg.Empty, connection)((i, _) => f(i))(EmptyableArg.Empty)((i, _) => continue(i))
}

def query[G[_], B](f: A => Query[G, Query.Select[B]])(implicit
tpe: => Out[F, G[B]]
Expand Down

0 comments on commit a5f3fbf

Please sign in to comment.