问题
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