我正在尝试创建通用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,但结尾相同。
答案 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
你可能需要的(原始,String
和case 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)