如何使用在 Scala 中创建数据集的通用案例 class 实现特征
How to implement a trait with a generic case class that creates a dataset in Scala
我想创建一个 Scala trait,它应该用 case class T 来实现。trait 只是加载数据并将其转换为 T 类型的 Spark 数据集。我得到的错误是 no编码器可以存储,我认为这是因为Scala不知道T应该是一个caseclass。我怎么能告诉编译器呢?我在某个地方看到我应该提到产品,但是没有这样的 class 定义。请随意提出其他方法来做到这一点!
我有以下代码,但它没有编译错误:42:错误:无法找到存储在数据集中的类型的编码器。通过导入 sqlContext.implicits._ 支持原始类型(Int、String 等)和产品类型(case classes)
[信息].as[T]
我正在使用 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]
}
}
您需要执行两个操作:
- 在您的导入中添加
import sparkSession.implicits._
- 塑造你的特质
trait Agent[T <: Product]
您的代码缺少 3 个东西:
- 确实,您必须让编译器知道 T 是
Product
的子class(所有 Scala 案例 classes 和元组的超级class)
- 编译器还需要实际案例 class 的
TypeTag
和 ClassTag
。 Spark 隐式使用它来克服类型擦除
- 导入
sqlContext.implicits._
不幸的是,您不能在 trait 中添加具有 context bounds 的类型参数,因此最简单的解决方法是使用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...
}
}
显然,这不等同于使用特征,并且可能表明此设计不是最适合该工作的。另一种方法是将 load
放在 对象 中并将类型参数移动到方法中:
object Agent {
protected def load[T <: Product : ClassTag : TypeTag](): Dataset[T] = {
// same...
}
}
哪个更可取主要取决于您要调用的位置和方式 load
以及您打算如何处理结果。
我想创建一个 Scala trait,它应该用 case class T 来实现。trait 只是加载数据并将其转换为 T 类型的 Spark 数据集。我得到的错误是 no编码器可以存储,我认为这是因为Scala不知道T应该是一个caseclass。我怎么能告诉编译器呢?我在某个地方看到我应该提到产品,但是没有这样的 class 定义。请随意提出其他方法来做到这一点!
我有以下代码,但它没有编译错误:42:错误:无法找到存储在数据集中的类型的编码器。通过导入 sqlContext.implicits._ 支持原始类型(Int、String 等)和产品类型(case classes) [信息].as[T]
我正在使用 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]
}
}
您需要执行两个操作:
- 在您的导入中添加
import sparkSession.implicits._
- 塑造你的特质
trait Agent[T <: Product]
您的代码缺少 3 个东西:
- 确实,您必须让编译器知道 T 是
Product
的子class(所有 Scala 案例 classes 和元组的超级class) - 编译器还需要实际案例 class 的
TypeTag
和ClassTag
。 Spark 隐式使用它来克服类型擦除 - 导入
sqlContext.implicits._
不幸的是,您不能在 trait 中添加具有 context bounds 的类型参数,因此最简单的解决方法是使用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...
}
}
显然,这不等同于使用特征,并且可能表明此设计不是最适合该工作的。另一种方法是将 load
放在 对象 中并将类型参数移动到方法中:
object Agent {
protected def load[T <: Product : ClassTag : TypeTag](): Dataset[T] = {
// same...
}
}
哪个更可取主要取决于您要调用的位置和方式 load
以及您打算如何处理结果。