Created
December 11, 2016 22:59
-
-
Save teigen/6d0951c72702a1d7d81164e5fcc51047 to your computer and use it in GitHub Desktop.
influx.db data types and decoders for json query results
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package influxdb | |
import argonaut.Argonaut._ | |
import shapeless._ | |
object Main { | |
def main(args: Array[String]): Unit = { | |
val one = | |
""" | |
|{ | |
| "results": [ | |
| { | |
| "series": [ | |
| { | |
| "name": "cpu_load_short", | |
| "columns": [ | |
| "time", | |
| "value" | |
| ], | |
| "values": [ | |
| [ | |
| "2015-01-29T21:55:43.702900257Z", | |
| 0.55 | |
| ], | |
| [ | |
| "2015-01-29T21:55:43.702900257Z", | |
| 23422 | |
| ], | |
| [ | |
| "2015-06-11T20:46:02Z", | |
| 0.64 | |
| ] | |
| ] | |
| } | |
| ] | |
| } | |
| ] | |
|} | |
""".stripMargin.decodeEither[Results].toEither.right.get | |
val two = | |
""" | |
|{ | |
| "results": [ | |
| { | |
| "series": [ | |
| { | |
| "name": "cpu_load_short", | |
| "columns": [ | |
| "time", | |
| "value" | |
| ], | |
| "values": [ | |
| [ | |
| "2015-01-29T21:55:43.702900257Z", | |
| 0.55 | |
| ], | |
| [ | |
| "2015-01-29T21:55:43.702900257Z", | |
| 23422 | |
| ], | |
| [ | |
| "2015-06-11T20:46:02Z", | |
| 0.64 | |
| ] | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "series": [ | |
| { | |
| "name": "cpu_load_count", | |
| "columns": [ | |
| "time", | |
| "count" | |
| ], | |
| "values": [ | |
| [ | |
| "1970-01-01T00:00:00Z", | |
| 3 | |
| ] | |
| ] | |
| } | |
| ] | |
| } | |
| ] | |
|} | |
""".stripMargin.decodeEither[Results].toEither.right.get | |
val three = | |
""" | |
|{ | |
| "results": [ | |
| { | |
| "series": [ | |
| { | |
| "name": "cpu_load_short", | |
| "columns": [ | |
| "time", | |
| "value" | |
| ], | |
| "values": [ | |
| [ | |
| "2015-01-29T21:55:43.702900257Z", | |
| 0.55 | |
| ], | |
| [ | |
| "2015-01-29T21:55:43.702900257Z", | |
| 23422 | |
| ], | |
| [ | |
| "2015-06-11T20:46:02Z", | |
| 0.64 | |
| ] | |
| ] | |
| }, | |
| { | |
| "name": "cpu_load_count", | |
| "columns": [ | |
| "time", | |
| "count" | |
| ], | |
| "values": [ | |
| [ | |
| "1970-01-01T00:00:00Z", | |
| 3 | |
| ] | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "series": [ | |
| { | |
| "name": "cpu_load_count", | |
| "columns": [ | |
| "time", | |
| "count" | |
| ], | |
| "values": [ | |
| [ | |
| "1970-01-01T00:00:00Z", | |
| 3 | |
| ] | |
| ] | |
| } | |
| ] | |
| } | |
| ] | |
|} | |
""".stripMargin.decodeEither[Results].toEither.right.get | |
case class CpuLoadShort(time:String, value:Double) | |
case class CpuLoadCount(time:String, count:Int) | |
val oneAs:Either[String, List[CpuLoadShort]] = | |
one.as[CpuLoadShort] | |
val twoAs:Either[String, List[CpuLoadShort] :: List[CpuLoadCount] :: HNil] = | |
two.as[CpuLoadShort :: CpuLoadCount :: HNil] | |
val threeAs:Either[String, (List[CpuLoadShort] :: List[CpuLoadCount] :: HNil) :: List[CpuLoadCount] :: HNil] = | |
three.as[(CpuLoadShort :: CpuLoadCount :: HNil) :: CpuLoadCount :: HNil] | |
println(oneAs) | |
println(twoAs) | |
println(threeAs) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package influxdb | |
import argonaut._, Argonaut._ | |
import shapeless._ | |
object Result { | |
object Decoder { | |
type Aux[A, B] = Decoder[A]{ type Out = B } | |
import HList.ListCompat._ | |
implicit def SingleSerieDecoder[A](implicit d:Serie.Decoder[A]):Decoder.Aux[A, List[A]] = | |
new Decoder[A]{ | |
type Out = List[A] | |
override def decode(series: List[Serie]): Either[String, List[A]] = | |
series match { | |
case Serie(_, columns, values) :: Nil => d.decode(columns, values) | |
case xs => Left("expected a single series, but got " + xs) | |
} | |
} | |
implicit val HNilDecoder:Decoder.Aux[HNil, HNil] = | |
new Decoder[HNil]{ | |
type Out = HNil | |
override def decode(series: List[Serie]): Either[String, HNil] = | |
if(series.isEmpty) Right(HNil) else Left("expected Nil, but got " + series) | |
} | |
implicit def HConsDecoder[H, T <: HList, TOut <: HList](implicit dh:Serie.Decoder[H], | |
dt:Decoder.Aux[T, TOut]):Decoder.Aux[H :: T, List[H] :: TOut] = | |
new Decoder[H :: T] { | |
type Out = List[H] :: TOut | |
override def decode(series: List[Serie]): Either[String, ::[List[H], TOut]] = | |
series match { | |
case Serie(_, columns, values) :: xs => | |
for { | |
h <- dh.decode(columns, values).right | |
t <- dt.decode(xs).right | |
} yield h :: t | |
case Nil => Left("expected at least one series, but got Nil") | |
} | |
} | |
} | |
trait Decoder[A]{ | |
type Out | |
def decode(series:List[Serie]):Either[String, Out] | |
} | |
implicit val codec = CodecJson.derive[Result] | |
} | |
case class Result(series:List[Serie]){ | |
def as[A](implicit d:Result.Decoder[A]):Either[String, d.Out] = | |
d.decode(series) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package influxdb | |
import argonaut._, Argonaut._ | |
import shapeless._ | |
object Results { | |
object Decoder { | |
type Aux[A, B] = Decoder[A]{ type Out = B } | |
import HList.ListCompat._ | |
implicit def SingleResultDecoder[A, B](implicit d:Result.Decoder.Aux[A, B]):Decoder.Aux[A, B] = | |
new Decoder[A]{ | |
type Out = B | |
override def decode(results: List[Result]): Either[String, B] = | |
results match { | |
case Result(series) :: Nil => d.decode(series) | |
case xs => Left("expected a single Result, but got " + xs) | |
} | |
} | |
implicit val HNilDecoder:Decoder.Aux[HNil, HNil] = | |
new Decoder[HNil]{ | |
type Out = HNil | |
override def decode(results: List[Result]): Either[String, HNil] = | |
if(results.isEmpty) Right(HNil) else Left("expected Nil, but got " + results) | |
} | |
implicit def HConsDecoder[H, T <: HList, TOut <: HList](implicit dh:Result.Decoder[H], | |
dt:Decoder.Aux[T, TOut]):Decoder.Aux[H :: T, dh.Out :: TOut] = | |
new Decoder[H :: T] { | |
type Out = dh.Out :: TOut | |
override def decode(results: List[Result]): Either[String, dh.Out :: TOut] = | |
results match { | |
case Result(series) :: xs => | |
for { | |
h <- dh.decode(series).right | |
t <- dt.decode(xs).right | |
} yield h :: t | |
case Nil => Left("expected at least one Result, but got Nil") | |
} | |
} | |
} | |
trait Decoder[A]{ | |
type Out | |
def decode(results:List[Result]):Either[String, Out] | |
} | |
implicit val codec = CodecJson.derive[Results] | |
} | |
case class Results(results:List[Result]){ | |
def as[A](implicit d:Results.Decoder[A]):Either[String, d.Out] = | |
d.decode(results) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package influxdb | |
import argonaut._, Argonaut._ | |
import shapeless._, labelled._ | |
object Serie { | |
object IndexedDecoder { | |
def index[A : DecodeJson](i:Int):IndexedDecoder[A] = | |
IndexedDecoder(_.apply(i).as[A].toEither.left.map(_._1)) | |
} | |
case class IndexedDecoder[A](decode:List[Json] => Either[String, A]){ | |
def map[B](f:A => B):IndexedDecoder[B] = | |
IndexedDecoder(decode(_).right.map(f)) | |
def merge[B, C](db:IndexedDecoder[B])(f:(A, B) => C):IndexedDecoder[C] = | |
IndexedDecoder{ row => | |
for { | |
a <- decode(row).right | |
b <- db.decode(row).right | |
} yield f(a, b) | |
} | |
} | |
object Decoder { | |
def column[A : DecodeJson](name:String):Decoder[A] = | |
Decoder(columns => | |
columns | |
.get(name) | |
.map(IndexedDecoder.index[A]) | |
.toRight("could not find column '"+name + "' in " + columns.keySet.mkString("[", ", ", "]"))) | |
implicit def HNilDecoder:Decoder[HNil] = | |
Decoder(_ => Right(IndexedDecoder(_ => Right(HNil)))) | |
implicit def HConsDecoder[K <: Symbol, H, T <: HList](implicit key:Witness.Aux[K], | |
hd:DecodeJson[H], | |
td:Decoder[T]):Decoder[FieldType[K, H] :: T] = | |
(column[H](key.value.name).map(h => field[K](h)) merge td)(_ :: _) | |
implicit def LabelledGenericDecoder[A, B](implicit gen:LabelledGeneric.Aux[A, B], db:Decoder[B]):Decoder[A] = | |
db.map(gen.from) | |
} | |
case class Decoder[A](indexed:Map[String, Int] => Either[String, IndexedDecoder[A]]) { | |
def decode(columns:List[String], rows:List[List[Json]]):Either[String, List[A]] = { | |
indexed(columns.zipWithIndex.toMap).right.flatMap{ | |
d => rows.foldRight[Either[String, List[A]]](Right(Nil))((v, acc) => | |
for { | |
values <- acc.right | |
a <- d.decode(v).right | |
} yield a :: values | |
) | |
} | |
} | |
def map[B](f:A => B):Decoder[B] = | |
Decoder(indexed(_).right.map(_.map(f))) | |
def merge[B, C](sb:Decoder[B])(f:(A, B) => C):Decoder[C] = | |
Decoder{ columns => | |
for { | |
da <- indexed(columns).right | |
db <- sb.indexed(columns).right | |
} yield (da merge db) (f) | |
} | |
} | |
implicit val codec = CodecJson.derive[Serie] | |
} | |
case class Serie(name:String, columns:List[String], values:List[List[Json]]){ | |
def as[A](implicit ds:Serie.Decoder[A]):Either[String, List[A]] = | |
ds.decode(columns, values) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment