[英]Slick: LIKE query with column as parameter
I have the following SQL query, which I'd like to map to Slick 我有以下SQL查询,我想将其映射到Slick
SELECT * FROM rates
WHERE '3113212512' LIKE (prefix || '%') and present = true
ORDER BY prefix DESC
LIMIT 1;
However the like symbol is not defined for String: 但是,没有为String定义like符号:
case class Rate(grid: String, prefix: String, present: Boolean)
class Rates(tag: Tag) extends Table[Rate](tag, "rates") {
def grid = column[String]("grid", O.PrimaryKey, O.NotNull)
def prefix = column[String]("prefix", O.NotNull)
def present = column[Boolean]("present", O.NotNull)
// Foreign keys
def ratePlan = foreignKey("rate_plan_fk", ratePlanId, RatePlans)(_.grid)
def ratePlanId = column[String]("rate_plan_id", O.NotNull)
def * = (grid, prefix, present) <>(Rate.tupled, Rate.unapply)
}
object Rates extends TableQuery(new Rates(_)) {
def findActiveRateByRatePlanAndPartialPrefix(ratePlan: String, prefix: String) = {
DB withSession {
implicit session: Session =>
Rates.filter(_.ratePlanId === ratePlan)
.filter(_.present === true)
.filter(prefix like _.prefix)
.sortBy(_.prefix.desc).firstOption
}
}
}
Obviously it's logical that something like this won't work: 显然,这样的事情不起作用是合乎逻辑的:
.filter(prefix like _.prefix)
And: 和:
.filter(_.prefix like prefix)
Will render incorrect SQL, and I'm not even considering the '%' right now (only WHERE clause concerning the prefix: 将呈现不正确的SQL,我现在甚至没有考虑'%'(只有关于前缀的WHERE子句:
(x2."prefix" like '3113212512')
Instead of: 代替:
'3113212512' LIKE (prefix || '%')
Of course I can solve this using a static query, but I'd like to know whether this would be possible at all? 当然我可以使用静态查询解决这个问题,但我想知道这是否可行?
For clarity, here are some prefixes in the database 为清楚起见,这里是数据库中的一些前缀
31
3113
312532623
31113212
And 31113212 is expected as a result 结果预计会有31113212
You could simply rewrite your query in a style that is supported. 您可以简单地以支持的样式重写查询。 I think this should be equivalent: 我认为这应该是等价的:
.filter(s => (s.prefix === "") || s.prefix.isEmpty || LiteralColumn(prefix) like s.prefix)
If the conditions are more complex and you need a Case expression, see http://slick.typesafe.com/doc/2.1.0/sql-to-slick.html#case 如果条件更复杂且需要Case表达式,请参阅http://slick.typesafe.com/doc/2.1.0/sql-to-slick.html#case
If you still want the || 如果你还想要|| operator you may be able to define it this way: 运算符您可以这样定义它:
/** SQL || for String (currently collides with Column[Boolean] || so other name) */
val thisOrThat = SimpleBinaryOperator[String]("||")
... ...
.filter(s => LiteralColumn(prefix) like thisOrThat(s.prefix,"%"))
I didn't try this though and I saw we don't have a test for SimpleBinaryOperator. 我没有试过这个,但我看到我们没有测试SimpleBinaryOperator。 Open a ticket on github.com/slick/slick if it doesn't work. 如果不起作用,请在github.com/slick/slick上打开一张票。 Probably we should also change it to return a typed function. 可能我们也应该改变它以返回一个类型化的函数。 I added a ticket for that https://github.com/slick/slick/issues/1073 . 我为https://github.com/slick/slick/issues/1073添加了一张票。
Also see http://slick.typesafe.com/doc/2.1.0/userdefined.html#scalar-database-functions 另请参阅http://slick.typesafe.com/doc/2.1.0/userdefined.html#scalar-database-functions
Luckily SimpleBinaryOperator is not even needed here. 幸运的是,这里甚至不需要SimpleBinaryOperator。
UPDATE 2 更新2
One actually does not require the concat, as it can be written as follows as well: 实际上一个不需要concat,因为它也可以写成如下:
filter(s => LiteralColumn(prefix) like (s.prefix ++ "%"))
UPDATE: 更新:
After hints by cvogt this is the final result without using internal Slick APIs: 在cvogt提示后,这是不使用内部Slick API的最终结果:
val concat = SimpleBinaryOperator[String]("||")
Rates.filter(_.ratePlanId === ratePlan)
.filter(_.present === true)
.filter(s => LiteralColumn(prefix) like concat(s.prefix, "%"))
.sortBy(_.prefix.desc).firstOption
Which perfectly gives: 完美地给出了:
and ('3113212512' like (x2."prefix" || '%'))
Alternative: 替代方案:
This seems to possible. 这似乎是可能的。 Note however that the following method uses the Internal API of Slick and may not be compatible in the future due to API changes (see comment by cvogt) I used Slick 2.1.0. 但请注意,以下方法使用Slick的内部API,并且由于API更改,将来可能不兼容(请参阅cvogt的评论)我使用了Slick 2.1.0。
Basically I created an extension to Slick in which I define a custom scheme to implement the required result: 基本上我创建了一个Slick的扩展,我在其中定义了一个自定义方案来实现所需的结果:
package utils
import scala.language.{higherKinds, implicitConversions}
import scala.slick.ast.ScalaBaseType._
import scala.slick.ast._
import scala.slick.lifted.{Column, ExtensionMethods}
object Helpers {
implicit class PrefixExtensionMethods[P1](val c: scala.slick.lifted.Column[P1]) extends AnyVal with ExtensionMethods[String, P1] {
def subPrefixFor[P2, R](prefix: Column[P2])(implicit om: o#arg[String, P2]#to[Boolean, R]) = {
om.column(Library.Like, prefix.toNode, om.column(new Library.SqlOperator("||"), n, LiteralNode("%")).toNode)
}
}
}
This can now be used as follows: 现在可以按如下方式使用:
import utils.Helpers._
[...]
def findActiveRateByRatePlanAndPartialPrefix(ratePlan: String, prefix: String) = {
DB withSession {
implicit session: Session =>
Rates.filter(_.ratePlanId === ratePlan)
.filter(_.present === true)
.filter(_.prefix subPrefixFor prefix)
.sortBy(_.prefix.desc).firstOption
}
}
And will render correct result: 并将呈现正确的结果:
and ('3113212512' like (x2."prefix" || '%'))
Notes: 笔记:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.