Условно UPDATE поля с интерполяцией Slick String

У меня есть два Options:

val name: Option[String] = ...
val shared: Option[Boolean] = ...

Я хотел бы построить запрос UPDATE, который SETs эти поля, если вышеуказанные значения Some, но оставить их без изменений, если они None.

Мне удалось добиться этого так, но я не очень заинтересован в проверке запятой, которая не будет масштабироваться, если мне нужно добавить дополнительные столбцы. Я также не очень хочу использовать var.

 var query = Q.u + "UPDATE wishlist SET"
 if(name.isDefined)  query = query + " name = " +? name + (if (share.isDefined) ", " else "")
 if(shared.isDefined) query = query + " shared = " +? share

Если бы меня не беспокоила SQL-инъекция, я мог бы сделать что-то вроде:

 val fields = List(
   name.map(n => s"name = '$n'"),
   shared.map(e => s"shared = $e")
 ).flatten

 s"UPDATE wishlist SET ${fields.mkString(", ")}"

Вопрос: есть ли более удобный способ сделать это с помощью простой интерполяции SQL/String от Slick, или это мой единственный вариант перейти на встроенный с поднятым номером?


person theon    schedule 09.08.2013    source источник


Ответы (1)


Это не супер элегантно, но я думаю, что это, по крайней мере, гибко в том смысле, что он будет расширяться для поддержки многих входных данных без изменения базовой логики:

 val name:Option[String] = Some("foo")
 val share:Option[String] = Some("bar")

 val sets = List(
   name.map(n => (" name = ", n)),
   share.map(s => (" shared = ", s))
 ).flatten

 val query2 = sets.zipWithIndex.foldLeft(Q.u + "UPDATE wishlist SET"){
   case (q,(p,0)) => q + p._1 +? p._2
   case (q,(p,i)) => q + "," + p._1 +? p._2
 }

Я помещаю пары имени поля и значения в список как параметры для Tuples, а затем сглаживаю, чтобы удалить Nones. Затем я сворачиваю, чтобы получить окончательный оператор, принимая во внимание, что любая часть запроса после первой части потребует запятой.

person cmbaxter    schedule 09.08.2013
comment
Спасибо! Это определенно лучше. - person theon; 10.08.2013
comment
Оказывается, это не работает при использовании смешанных типов. Это связано с тем, что метод +? использует классы типов. Он использует тип аргумента для поиска связанного экземпляра SetParameter: def +? [T](v: T)(implicit p: SetParameter[T]). В этом коде, если name является String, а share является Boolean, то sets становится List[(String,Any)], и, как вы понимаете, вызов +? с Any не будет работать. В качестве обходного пути я закончил тем, что написал несколько строк шаблона для разрешения класса типа до создания sets. Мой код теперь выглядит так: этот - person theon; 13.08.2013