如何使用exposed根据传入参数添加多个或过滤条件?
How to add multiple or filter conditions based on incoming parameters using exposed?
我必须使用参数值向查询添加 or
条件。
示例:select * from users where email = "abc@xyz.com" or phone="1234123412";
用户可能会发送两个字段或只发送一个。我想在每个字段的循环中执行此操作,并将每个字段添加到 or where
条件中。
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 }
上面的 DSL 生成下面的 SQL。
SELECT * from users where (((false = true) OR (users.email = "abc@xyz.com")) OR (users.phone = "1234567890"))
看到false = true
了吗?那是因为,要使用 .or
方法,我必须使用条件进行初始化。下面给出的代码片段是为初始化谓词而添加的一行不必要的代码。
var predicates = Op.build { Users.id inList listOf<Int>()}
正确的初始化方法是什么,以便我可以无缝地向查询添加多个 or
和 and
谓词?
一个技巧是将 predicates
初始化为 null
并在 Op<Boolean>?
上定义您自己的 or
和 and
:
// not sure x's type is correct here, the wiki doesn't give exact signatures
// may also need @JvmName to allow overloading
fun Op<Boolean>?.or(x: Op<Boolean>) = if (this != null) this.or(x) else x
...
var predicates: Op<Boolean>? = null
...
predicates = when(k) {
"email" -> predicates.or(Users.email eq value)
"phone" -> predicates.or(Users.phone eq value)
}
是否值得复杂化,我怀疑:正如 Giacomo 的评论所提到的,数据库肯定会优化您原来的 false = true
。
首先,我建议您像这样优化您的代码:
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:自 0.16.1 版本以来,orWhere 函数在 Exposed 中可用。
如果您想使用模式匹配 (when
),请按照 andWhere
的工作方式定义您的本地 orWhere
函数:
fun Query.orWhere(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWhere {
val expr = Op.build { andPart() }
if(this == null) expr
else this or expr
}
并像这样使用它:
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 }
}
}
我必须使用参数值向查询添加 or
条件。
示例:select * from users where email = "abc@xyz.com" or phone="1234123412";
用户可能会发送两个字段或只发送一个。我想在每个字段的循环中执行此操作,并将每个字段添加到 or where
条件中。
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 }
上面的 DSL 生成下面的 SQL。
SELECT * from users where (((false = true) OR (users.email = "abc@xyz.com")) OR (users.phone = "1234567890"))
看到false = true
了吗?那是因为,要使用 .or
方法,我必须使用条件进行初始化。下面给出的代码片段是为初始化谓词而添加的一行不必要的代码。
var predicates = Op.build { Users.id inList listOf<Int>()}
正确的初始化方法是什么,以便我可以无缝地向查询添加多个 or
和 and
谓词?
一个技巧是将 predicates
初始化为 null
并在 Op<Boolean>?
上定义您自己的 or
和 and
:
// not sure x's type is correct here, the wiki doesn't give exact signatures
// may also need @JvmName to allow overloading
fun Op<Boolean>?.or(x: Op<Boolean>) = if (this != null) this.or(x) else x
...
var predicates: Op<Boolean>? = null
...
predicates = when(k) {
"email" -> predicates.or(Users.email eq value)
"phone" -> predicates.or(Users.phone eq value)
}
是否值得复杂化,我怀疑:正如 Giacomo 的评论所提到的,数据库肯定会优化您原来的 false = true
。
首先,我建议您像这样优化您的代码:
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:自 0.16.1 版本以来,orWhere 函数在 Exposed 中可用。
如果您想使用模式匹配 (when
),请按照 andWhere
的工作方式定义您的本地 orWhere
函数:
fun Query.orWhere(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWhere {
val expr = Op.build { andPart() }
if(this == null) expr
else this or expr
}
并像这样使用它:
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 }
}
}