TreasureDataをscalikejdbcで操作するときに、独自のカラム型に対応する方法を試す。
TreasureData(presto)には、Arrayのカラムが使えてvarcharやintを入れられる。
取得すると、[1,2,3]
のようなjson形式の配列で返ってくる。
独自の型とテーブル
型とテーブルの構造は以下とする。
case class TDArray[A](values: Seq[A])
case class Foo(
id: Int,
arrStr: TDArray[String],
arrInt: TDArray[Int],
time: Long)
object Foo extends SQLSyntaxSupport[Foo] {
override val tableName = "foo"
override val columns = Seq("id", "arr_str", "arr_int", "time")
def apply(rn: ResultName[Foo])(rs: WrappedResultSet): Foo = autoConstruct(rs, rn)
}
これをreadする。
object FooDao {
val f = Foo.syntax("f")
def read(id: Int)(implicit session: DBSession = AutoSession): Seq[Foo] = {
withSQL {
select
.from(Foo as f)
}
.map(Foo(f.resultName))
.list().apply()
}
}
このままだと、TDArrayをどうしたらいいか分からないのでエラーになる。
結果セットから、値を取得するときにTypeBinderを経由して取得しているようなので、TypeBinderに独自の定義を追加すればいい。
型クラスになっているので、implicitで型を定義する。
object TDArray {
val stringTypeBinder: TypeBinder[TDArray[String]] = {
TypeBinder(_ getString _)(_ getString _).map { s =>
if (s == null) TDArray(Seq[String]())
else TDArray(s.init.tail.replace("\"", "").split(',').map(_.trim))
}
}
implicit val tdString: TypeBinder[TDArray[String]] = stringTypeBinder
implicit val tdInt: TypeBinder[TDArray[Int]] = stringTypeBinder.map { tda => TDArray(tda.values.map(_.toInt)) }
}
jsonのパースの所はてきとう。
これでreadできるようになった。
もうちょっと説明
まずFooのapplyで使っているautoConstructのマクロで、WrappedResultSetのgetを呼ぶコードを作っている。
WrappedResultSetのgetは、TypeBinderを取得してapplyしてる。構文が分からない場合は"scala 型クラス"でググルとよろし。
なので、TypeBinderのimplicitを定義すればいいというわけだ。
おわり
追記
ちなみにWrappedResultSetのarrayは、TreasureDataのJDBCドライバがgetArrayをサポートしていないのでUnsopported..の例外になる。╭(°ㅂ°`)╮
(´-`).。oO(TreasureDataよく分からん...)