Last active
April 9, 2020 11:21
-
-
Save missingfaktor/aa6c264c5b7411fa48a6a5b654dd0917 to your computer and use it in GitHub Desktop.
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
implicit class RichQuery[E, U, C[_]](val underlying: Query[E, U, C]) { | |
/** | |
* To group by elements by some columns, say "column1" and "column2", and then pick latest in each group, you | |
* would normally "ORDER BY column1, column2, created_at DESC" and then "DISTINCT ON (column1, column2)". | |
* However Slick currently does not support DISTINCT ON. There are a number of alternatives that can be used to | |
* circumvent this limitation. The following function uses "sub-select method" described here: | |
* https://robots.thoughtbot.com/ordering-within-a-sql-group-by-clause#sub-select-method | |
* | |
* This function only deals with arity 2. You will need to define variations of it for different arities. We | |
* attempted abstracting over arity but could not get it working. | |
*/ | |
def latestPer[A, B]( | |
column1: E => Rep[A], | |
column2: E => Rep[B] | |
)( | |
implicit | |
groupByEvidence1: Shape[_ <: FlatShapeLevel, (Rep[A], Rep[B]), (A, B), (Rep[A], Rep[B])], | |
groupByEvidence2: Shape[_ <: FlatShapeLevel, E, _, E], | |
mapEvidence1: Shape[_ <: FlatShapeLevel, (Rep[A], Rep[B], Rep[Option[DateTime]]), (A, B, Option[DateTime]), (Rep[A], Rep[B], Rep[Option[DateTime]])], | |
mapEvidence2: Shape[_ <: FlatShapeLevel, E, U, E], | |
liskov: Liskov[E, HasCreatedAt], | |
optionMapperA: OptionMapper2[A, A, Boolean, A, A, Boolean], | |
optionMapperB: OptionMapper2[B, B, Boolean, B, B, Boolean] | |
): Query[E, U, C] = { | |
underlying | |
.join { | |
underlying | |
.groupBy(record => (column1(record), column2(record))) | |
.map { case ((key1, key2), values) => (key1, key2, values.map(value => liskov.coerce(value).createdAt).max) } | |
} | |
.on { case (record, (key1, key2, createdAt)) => | |
new BaseColumnExtensionMethods(column1(record)) === key1 && | |
new BaseColumnExtensionMethods(column2(record)) === key2 && | |
liskov.coerce(record).createdAt === createdAt | |
} | |
.map { case (record, _) => record } | |
} | |
} | |
// Usage: Just write `.latestPer(_.col1, _.col2)` where you would have written | |
// `.sortBy(r => (r.col1, r.col2, r.map(_.createdAt).max)).distinctOn(r => (r.col1, r.col2))`. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey, thanks for the gist. I could get it to work by adapting it for a concrete instantiation but I fail to provide the necessary imports to get your generic implementation working.
Could you maybe add the required imports?