Slick: Is there a way to create a WHERE clause with a regex?

自闭症网瘾萝莉.ら 提交于 2019-12-10 18:04:20

问题


I look for a slick equivalient to

select * from users where last_name ~* '[\w]*son';

So for example when having the following names in the database:

first_name | last_name
----------------------
Tore       | Isakson
John       | Smith
Solveig    | Larsson
Marc       | Finnigan

The result would be

first_name | last_name
----------------------
Tore       | Isakson
Solveig    | Larsson

My current solution is to interpolate this with an SQLActionBuilder like

val pattern = "[\\w]*son"
val action = sql""" SELECT * FROM users WHERE last_name ~* ${pattern}; """.as[User]

But this is not the way I would like to have it. I would prefer something like

users.filter(_.last_name matchRegex "[\\w]*son")   // <- This does not exist

In case it is relevant: I use Postgres.


回答1:


(This answer is following the question in Slick: How can I combine a SQL LIKE statement with a SQL IN statement)

Although Slick doesn't support the ~* operator out of the box, you can add it yourself. That would give you a way to execute the query using the lifted embedded style of Slick query.

To do that, you can use the SimpleExpression builder. There's not much documentation on it, but the jumping off point would be the Scalar Database Functions page of the reference manual.

What we want to do is write a method along these lines:

def find(names: Seq[String]): DBIO[Seq[String]] = {
  val pattern = names.mkString("|")
  users.filter(_.lastName regexLike pattern).map(_.lastName).result
}

To get regexLike we can use a enrich (enhance, "pimp") a string column to have the regexLike method:

implicit class RegexLikeOps(s: Rep[String]) {
  def regexLike(p: Rep[String]): Rep[Boolean] = {
    val expr = SimpleExpression.binary[String,String,Boolean] { (s, p, qb) =>
      qb.expr(s)
      qb.sqlBuilder += " ~* "
      qb.expr(p)
    }
    expr.apply(s,p)
  }
}

The implicit class part is allow the compiler to construct the RegexLikeOps class anytime it has a Rep[String] that calls a method that Rep[String] doesn't already have (i.e., when regexLike is asked for).

Our regexLike method takes another Rep[String] argument as the pattern, and then uses SimpleExpression builder to safely construct the SQL we want to use.

Putting it all together we can write:

val program = for {
  _ <- users.schema.create
  _ <- users ++= User("foo") :: User("baz") :: User("bar") :: Nil
  result <- find( Seq("baz","bar") )
} yield result

println( Await.result(db.run(program), 2.seconds) )

The SQL generated (in my test with H2) is:

select "last_name" from "app_user" where "last_name" ~* 'baz|bar'

The full code is: https://github.com/d6y/so46199828



来源:https://stackoverflow.com/questions/46218122/slick-is-there-a-way-to-create-a-where-clause-with-a-regex

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!