如何让DataFrame
给出一个引用自己的case class
?请采取以下措施:
case class TestCase(id: Long, parent: Option[TestCase])
如果我这样做:
val testCases = Seq(TestCase(1L, None), TestCase(2L, Some(TestCase(1L, None)))).toDF
它引发了一个很大的'olde ScalaReflection
错误。当然,我可以这样做:
case class TestCase(id: Long, parentId: Option[Long])
但那不是我想要的。
顺便说一句,Avro
没有问题编码和解码递归模式。我不认为我在问这个不可能的事。这似乎是处理父子关系的一个非常正常的用例。
更新
我可以手动创建一个模式,但据我所知,我必须通过重复嵌套StructType
来硬编码你可以走多远的链。像这样:
val schema = StructType(Array(
StructField("id", LongType, false),
StructField("parent", StructType(Array(
StructField("id", LongType, false),
StructField("parent", StructType(Array(
StructField("id", LongType, false),
StructField("parent", NullType)
)))
)))
))
请注意,对于链的最后parent
,其类型为NullType
。使用上面的模式,以下所有工作:
df.select($"parent")
df.select($"parent.parent")
df.select($"parent.parent.parent")
根据上面的架构,前两个可能会返回null
或父级。第三个总是返回null
。
有趣的是,要为数据框创建Row
个对象,我只需要这样做:
val testCaseSeq = Seq[TestCase](...)
val df = sqlContext.createDataFrame(
sc.parallelize(testCaseSeq.map(tc => Row(tc.id, tc.parent))),
schema
)
我猜这或多或少有效。我只需要提前弄清楚要支持多少级的父子层次结构。哪种糟透了。但是,任何人都可以比这更好吗?