Exposed: Add set operations

Created on 9 Oct 2018  路  6Comments  路  Source: JetBrains/Exposed

It would be useful to have the set operations added:

union, union all, minus, intersect

I realize this can be handled with multiple select statements and then process the resulting lists, but they are more efficient in the database

enhancement

Most helpful comment

Here is my attempt at UnionAll. turned out to be quite hairy. It can be improved slightly if Query.arguments() specifies it return type explicitly and matches the interface signature:

class UnionAll(vararg val queries: Query) : Query(queries[0].set, null) {
    override fun arguments(): List<ArrayList<Pair<IColumnType, Any?>>> {
        val allArgs = queries.map { it.arguments() }
        var answer: List<ArrayList<Pair<IColumnType, Any?>>> = listOf()
        allArgs.forEach {
            answer = answer.zipLongest(it) { a, b ->
                a + b
            }
        }
        return answer
    }

    override fun prepareSQL(transaction: Transaction): String =
        queries.joinToString( " UNION ALL ") { it.prepareSQL(transaction) }
}

private operator fun <T> Collection<T>?.plus(other: Collection<T>?) : ArrayList<T> {
    val result = ArrayList<T>(this?.size + other?.size)
    if (this != null) {
        result.addAll(this)
    }
    if (other != null) {
        result.addAll(other)
    }
    return result
}

private operator fun Int?.plus(other: Int?) = when {
    this == null && other == null -> 10
    this == null -> other!!
    other == null -> this
    else -> this + other
}

private inline fun <T, R, V> Iterable<T>.zipLongest(other: Iterable<R>, transform: (a: T?, b: R?) -> V): List<V> {
    val first = iterator()
    val second = other.iterator()
    val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))
    while (first.hasNext() || second.hasNext()) {
        if (first.hasNext() && second.hasNext()) {
            list.add(transform(first.next(), second.next()))
        } else if (first.hasNext()) {
            list.add(transform(first.next(), null))
        } else {
            list.add(transform(null, second.next()))
        }
    }
    return list
}

private fun <T> Iterable<T>.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) this.size else default

All 6 comments

Agree, I surprisingly found there's no union operation in Exposed.

Is this being worked on? No movement on this issue for a long time. At the moment I drop into SQL to write UNION queries.

Here is my attempt at UnionAll. turned out to be quite hairy. It can be improved slightly if Query.arguments() specifies it return type explicitly and matches the interface signature:

class UnionAll(vararg val queries: Query) : Query(queries[0].set, null) {
    override fun arguments(): List<ArrayList<Pair<IColumnType, Any?>>> {
        val allArgs = queries.map { it.arguments() }
        var answer: List<ArrayList<Pair<IColumnType, Any?>>> = listOf()
        allArgs.forEach {
            answer = answer.zipLongest(it) { a, b ->
                a + b
            }
        }
        return answer
    }

    override fun prepareSQL(transaction: Transaction): String =
        queries.joinToString( " UNION ALL ") { it.prepareSQL(transaction) }
}

private operator fun <T> Collection<T>?.plus(other: Collection<T>?) : ArrayList<T> {
    val result = ArrayList<T>(this?.size + other?.size)
    if (this != null) {
        result.addAll(this)
    }
    if (other != null) {
        result.addAll(other)
    }
    return result
}

private operator fun Int?.plus(other: Int?) = when {
    this == null && other == null -> 10
    this == null -> other!!
    other == null -> this
    else -> this + other
}

private inline fun <T, R, V> Iterable<T>.zipLongest(other: Iterable<R>, transform: (a: T?, b: R?) -> V): List<V> {
    val first = iterator()
    val second = other.iterator()
    val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))
    while (first.hasNext() || second.hasNext()) {
        if (first.hasNext() && second.hasNext()) {
            list.add(transform(first.next(), second.next()))
        } else if (first.hasNext()) {
            list.add(transform(first.next(), null))
        } else {
            list.add(transform(null, second.next()))
        }
    }
    return list
}

private fun <T> Iterable<T>.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) this.size else default

any update on this?.. mysql doesn't support full outer join and I will have to rely on union operation to implement the full outer join.. lack of this operation basically means no full outer join in mysql as well ..

@Tapac Do you guys have any updates on this?

UNION and UNION ALL supported

Was this page helpful?
0 / 5 - 0 ratings

Related issues

brabo-hi picture brabo-hi  路  4Comments

BugsBunnyBR picture BugsBunnyBR  路  3Comments

junhwong picture junhwong  路  3Comments

power721 picture power721  路  3Comments

fmgonsalves picture fmgonsalves  路  3Comments