Spark SQL - 通用数据集读取器

时间:2017-06-27 11:21:33

标签: scala apache-spark apache-spark-sql apache-spark-dataset

我正在尝试创建通用DataSet [T]阅读器,以避免每个阅读器调用的dataframe.as [..]。 支持原始类型和案例类,所以我在想类似的东西:

def read[T <: Product](sql : String): Dataset[T] = {
  import sparkSession.implicits._
  val sqlContext = sparkSession.sqlContext
  val df: DataFrame = sqlContext.read.option("query", sql).load()
  df.as[T]
}

但我得到'无法找到存储在数据集中的类型的编码器'错误。 有可能做那样的事吗?

第二周期:

def read[T <: Product](sql : String) : Dataset[T] = {
  import sparkSession.implicits._
  innerRead(sql)
}

private def innerRead[T <: Product : Encoder](sql : String): Dataset[T] = {
  val sqlContext = sparkSession.sqlContext
  val df: DataFrame = sqlContext.read.option("query", sql).load()
  df.as[T]
}  

以类型不匹配结束(foudn Encoder [Nothing],需要编码器[T])。

我只是尝试导入newProductEncoder,但结尾相同。

2 个答案:

答案 0 :(得分:5)

要将DataFrame转换为Dataset,您需要拥有Encoder。您只需在Encoder上添加上下文绑定T即可完成此操作:

def read[T <: Product : Encoder](sql : String): Dataset[T] = {
  import sparkSession.implicits._
  val sqlContext = sparkSession.sqlContext
  val df: DataFrame = sqlContext.read.option("query", sql).load()
  df.as[T]
}

上下文绑定是以下语法糖:

def read[T <: Product](sql : String)(implicit $ev: Encoder[T]): Dataset[T]

这意味着您需要在隐式上下文中拥有Encoder[T]的一个(且仅一个)实例。

这是必需的,因为as方法本身需要此上下文绑定。

Spark本身可以为您提供大部分Encoder你可能需要的(原始,Stringcase class es到目前为止)导入(如你所做)的暗示SparkSession。但是,这些必须在调用站点的隐式作用域中可用,这意味着您希望拥有的内容可能更像以下内容:

def read[T <: Product : Encoder](spark: SparkSession, sql: String): Dataset[T] = {
  import spark.implicits._
  val df: DataFrame = spark.sqlContext.read.option("query", sql).load()
  df.as[T]
}

val spark: SparkSession = ??? // your SparkSession object
import spark.implicits._
val ds: Dataset[YourType] = read[YourType](spark, "select something from a_table")

答案 1 :(得分:1)

在第二个周期中,您可能需要为innerRead调用提供type参数:

innerRead[T](sql)