Search code examples
kotlinkotlin-exposed

How to add multiple or filter conditions based on incoming parameters using exposed?


I have to add or conditions using the parameter values to the query.

Example: select * from users where email = "abc@xyz.com" or phone="1234123412";

The user might send both fields or only one. I want to do this in a loop for each field and add each one of them in or where condition.

val query = Users.selectAll()
**var predicates = Op.build { Users.id inList listOf<Int>()}**
for((k, v) in params) {
    val value = URLDecoder.decode(v.first(), "UTF-8")
    predicates = when(k) {
        "email" -> predicates.or(Users.email eq value)
        "phone" -> predicates.or(Users.phone eq value)
    }
}
query.andWhere { predicates }

The above DSL produces the below SQL.

SELECT * from users where (((false = true) OR (users.email = "abc@xyz.com")) OR (users.phone = "1234567890"))

See that false = true? That is because, to use .or method, I have to initialize with a condition. The below given snippet is an unncessary line of code added to initialize the predicate.

var predicates = Op.build { Users.id inList listOf<Int>()}

What is the right way to initialize it so that I can seamlessly add multiple or and and predicates to the query?


Solution

  • First of all, I would advice to optimize your code like:

        val params = mapOf<String, List<String>>()
        val emails = params.filterKeys { it == "email" }.map { URLDecoder.decode(it.value.first(), "UTF-8") }
        val phones = params.filterKeys { it == "phone" }.map { URLDecoder.decode(it.value.first(), "UTF-8") }
    
        if (emails.isEmpty() && phones.isEmpty()) {
            error("No suitable params were provided")
        } else {
            Users.select {
                Users.email inList emails or (Users.phone inList phones)
            }
        }
    

    UPD: orWhere function is available in Exposed since 0.16.1 version.

    If you want to use pattern-matching (when) please define your local orWhere function the same way andWhere works:

    fun Query.orWhere(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWhere {
        val expr = Op.build { andPart() }
        if(this == null) expr
        else this or expr
    }
    

    And use it like:

    val query = Users.selectAll()
    for((k, v) in params) {
        val value = URLDecoder.decode(v.first(), "UTF-8")
        predicates = when(k) {
            "email" -> query.orWhere { Users.email eq value }
            "phone" -> query.orWhere{ Users.phone eq value }
        }
    }