Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom aggregations in correlated subqueries #2677

Open
shagoon opened this issue Jan 7, 2023 · 0 comments
Open

custom aggregations in correlated subqueries #2677

shagoon opened this issue Jan 7, 2023 · 0 comments

Comments

@shagoon
Copy link

shagoon commented Jan 7, 2023

My issue initiated when trying to perform some json-aggregation through correlated subqueries. I wrote a couple of messages about that to the discord channel.

To track down the problem, I decided to try to write an infix-query, that does the same as the built-in size/count aggregation. The latest scastie that shows that and the initial problem I had, is available at https://scastie.scala-lang.org/tKSoFbB6QBqYqdaHVz7BSg.

However, I further investigated the problem. In the scastie above, I used two nested infix-queries to get the parentheses around correlated subqueries. I think, this should not have been required at all. I.e., instead of having the nested version

implicit class MyQueryOps[T](q: Query[T]) {
  def mySize = quote(infix"(${q.map(t => infix"count($t)".pure.as[Long])})".pure.as[Long])
}

the plain version

implicit class MyQueryOps[T](q: Query[T]) {
  def mySize = quote(q.map(x => infix"COUNT($x.*)".pure.as[Long]).value.getOrNull)
}

should behave the same as the built-in size/count.

However, it doesn't. https://scastie.scala-lang.org/ERJrGZlZRZCPHmumPARr5Q shows that. While size automagically becomes a correlated subquery, mySize doesn't. I think, it's not obvious, that the two queries behave that different, especially given, that they share the same signature.

I think there are two possible solutions to that.

  1. make the infix behave as the aggregation
  2. provide a way to lift an infix into an aggregation

As it was easier for me to implement 2.), I added def aggregate[R](f: T => R): R in trait Query[+T] in phenetic/zio-quill@7b8765d. This allows to rewrite the correlated subquery like this:

object Showcase2 extends App {

  val ctx = new SqlMirrorContext(PostgresDialect, SnakeCase)

  import ctx._

  case class Person(id: Long, name: String)
  case class Follower(fellow: Long, follower: Long)

  val q = ctx.run {
    for {
      p <- query[Person]
    } yield (p, query[Follower].aggregate(p => unquote(p.mySize)))
  }
  println(q.string)

  implicit class MyQueryOps[T](t: T) {
    def mySize = quote(infix"COUNT($t.*)".pure.as[Long])
  }

}

This also solves my initial problems (io.getquill.quotation.QuatException: The post-rename field 'xxx' does not exist in an SQL-level type when using joins in more than one correlated subquery).

However, I'm not quite happy with it. Signature probably should be def aggregate[R](f: Query[T] => R): R instead. AggregationOperator.`custom` feels clunky, especially case AggregationOperator.`custom` => stmt"". And I'm wondering, whether the new method is required at all, or the existing .value could do just that?

What do you think?

@getquill/maintainers

@shagoon shagoon changed the title custom aggregations custom aggregations in correlated subqueries Jan 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant