Postgresql Slick:类似于以列为参数的查询

Postgresql Slick:类似于以列为参数的查询,postgresql,scala,slick,Postgresql,Scala,Slick,我有以下SQL查询,我想映射到Slick SELECT * FROM rates WHERE '3113212512' LIKE (prefix || '%') and present = true ORDER BY prefix DESC LIMIT 1; 但是,没有为字符串定义like符号: case class Rate(grid: String, prefix: String, present: Boolean) class Rates(tag: Tag) extends

我有以下SQL查询,我想映射到Slick

SELECT * FROM   rates
WHERE  '3113212512' LIKE (prefix || '%') and  present = true
ORDER  BY prefix DESC
LIMIT  1;
但是,没有为字符串定义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
    }
  }
}
以及:

将呈现不正确的SQL,我现在甚至没有考虑“%”(仅考虑与前缀有关的WHERE子句:

(x2."prefix" like '3113212512')
而不是:

'3113212512' LIKE (prefix || '%')
当然,我可以使用静态查询来解决这个问题,但我想知道这是否可行

为清楚起见,以下是数据库中的一些前缀

31
3113
312532623
31113212

因此,预计会有31113212个

实际上不需要concat,因为它也可以写为:

filter(s => LiteralColumn(prefix) like (s.prefix ++ "%"))
更新:

根据cvogt的提示,这是不使用内部光滑API的最终结果:

        val concat = SimpleBinaryOperator[String]("||")

        Rates.filter(_.ratePlanId === ratePlan)
          .filter(_.present === true)
          .filter(s => LiteralColumn(prefix) like concat(s.prefix, "%"))
          .sortBy(_.prefix.desc).firstOption
这完美地给出了:

and ('3113212512' like (x2."prefix" || '%'))
备选方案:

这似乎是可能的。但是请注意,以下方法使用了Slick的内部API,并且由于API更改(请参阅cvogt的评论)我使用了Slick 2.1.0,因此在将来可能不兼容

基本上,我创建了一个对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)
    }
  }
}
现在可以按如下方式使用:

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 ('3113212512' like (x2."prefix" || '%'))
注:

  • 我可能有更好的名字
  • 必须链接| |并使用om列
  • 我引入了新的SqlOperator,因为Or操作符呈现Or,我需要在Postgres中进行连接||

  • 您可以简单地用受支持的样式重写查询。我认为这应该是等效的:

    .filter(s => (s.prefix === "") || s.prefix.isEmpty || LiteralColumn(prefix) like s.prefix)
    
    如果条件更复杂,并且需要大小写表达式,请参见

    如果您仍然需要| |运算符,可以通过以下方式定义它:

    /** SQL || for String (currently collides with Column[Boolean] || so other name) */
    val thisOrThat = SimpleBinaryOperator[String]("||") 
    

    但我没有尝试过,我发现我们没有对SimpleBinaryOperator进行测试。如果它不起作用,请在github.com/slick/slick上打开一个票证。可能我们还应该将其更改为返回一个类型化函数。我为此添加了一个票证

    也看到


    幸运的是,这里甚至不需要SimpleBinaryOperator。

    您可以添加生成的SQL吗?我已经更新了问题,也许我没有得到它,但是这两个问题有什么不同(除了第二部分中的
    %
    部分)?将字符串与列进行匹配或以其他方式进行匹配,结果都是一样的,还是我遗漏了什么?或者可能您的
    前缀
    列值中已经有了通配符,在这种情况下,这一切都是有意义的。这项工作很有效,但需要深入研究不太稳定的内部API的巧妙使用。因此,如果您可以避免t、 让我们尝试使用slick的公共api。嗯,字符串仍然没有'like',因此'prefix like s.prefix'或'(s=>prefix like'仍然不起作用。您可能必须强制转换并写入LiteralColumn(prefix)。like(…)。如果这不起作用,我现在可能会误解我们的like运算符的适用性。我确实需要| |运算符,因为在postgres中这意味着连接,所以正如您提到的thisOrThat,这是有效的:。过滤器(s=>LiteralColumn(prefix)like thisOrThat(s.prefix,“%”)产生和('311312512'like(x2.“prefix”|‘%))我也是。是的,在某些情况下,隐式转换不会自动启动,您必须手动触发它。实际上,我可以使用++运算符进行连接:。筛选器(s=>LiteralColumn(prefix),比如(s.prefix++“%”)
    
    .filter(s => (s.prefix === "") || s.prefix.isEmpty || LiteralColumn(prefix) like s.prefix)
    
    /** SQL || for String (currently collides with Column[Boolean] || so other name) */
    val thisOrThat = SimpleBinaryOperator[String]("||") 
    
    .filter(s => LiteralColumn(prefix) like thisOrThat(s.prefix,"%"))