如何使用在Scala中创建数据集的通用案例类实现特征

Spa*_*rky 2 generics scala traits case-class apache-spark-sql

我想创建一个应该与案例类T一起实现的Scala特征。该特征只是加载数据并将其转换为类型T的Spark数据集。我得到一个错误,即无法存储任何编码器,我认为这是因为Scala不知道T应该是案例类。我怎样才能告诉编译器呢?我见过某个地方应该提到Product,但是还没有定义此类。.随意建议其他方法!

我有以下代码,但未编译该错误:42:错误:找不到用于存储在数据集中的类型的编码器。通过导入sqlContext.implicits._ [INFO] .as [T],支持基本类型(Int,String等)和产品类型(案例类)。

我正在使用Spark 1.6.1

码:

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{Dataset, SQLContext}    

/**
      * A trait that moves data on Hadoop with Spark based on the location and the granularity of the data.
      */
    trait Agent[T] {
      /**
        * Load a Dataframe from the location and convert into a Dataset
        * @return Dataset[T]
        */
      protected def load(): Dataset[T] = {
        // Read in the data
        SparkContextKeeper.sqlContext.read
          .format("com.databricks.spark.csv")
          .load("/myfolder/" + location + "/2016/10/01/")
          .as[T]
      }
    }
Run Code Online (Sandbox Code Playgroud)

Tza*_*har 5

您的代码缺少3件事:

  • 确实,您必须让编译器知道T是Product(所有Scala case类和Tuples的超类)的子类。
  • 编译器也将需要TypeTagClassTag实际情况的类。Spark隐式使用它来克服类型擦除
  • 进口 sqlContext.implicits._

不幸的是,您不能在trait中添加带有上下文边界的类型参数,因此最简单的解决方法是改用:abstract class

import scala.reflect.runtime.universe.TypeTag
import scala.reflect.ClassTag

abstract class Agent[T <: Product : ClassTag : TypeTag] {
  protected def load(): Dataset[T] = { 
    val sqlContext: SQLContext = SparkContextKeeper.sqlContext
    import sqlContext.implicits._
    sqlContext.read.// same... 
  }
}
Run Code Online (Sandbox Code Playgroud)

显然,这并不等同于使用特征,并且可能表明该设计不是最适合这项工作的。另一种选择是放置load对象中并将类型参数移至方法:

object Agent {
  protected def load[T <: Product : ClassTag : TypeTag](): Dataset[T] = {
    // same...
  }
}
Run Code Online (Sandbox Code Playgroud)

哪一个更可取,主要取决于您将在何处拨打电话load,如何拨打电话以及打算如何处理结果。