我有大量的日志数据,这些数据是半结构化为CSV数据。但是,每行的列取决于行的类型,由特定列指示。
示例数据:
8/01/2018, person, 1, Bob, Loblaw 32
8/01/2018, person, 2, Roger, McRoger, 55
8/03/2018, dog, Bella, 9, 1
8/05/2018, person, 3, Charlie, McCharles, 23
8/07/2018, dog, Scout, 5, 3
此特定示例显示了一个半结构文件,其中散布了两个模式,大致等于以下案例类:
case class Person(id: Int, firstName: String, lastName: String, age: Int)
case class Dog(name: String, age: Int, ownerId: Int)
我正在尝试找出通过Spark有效解析这些散布数据的最佳方法,以便我可以查询数据集,并可能在各种行类型之间进行连接。
当所有行都相同时,我可以将CSV数据加载到结构化架构中,但是这些文件中的行的异质性使我感到困惑。我认为我可以使用Spark读取以文本开头的数据,然后对“类型”列执行某种groupBy操作,这时我可以分别解析每个组,但是我已经之所以无法编写代码,是因为DataFrame的语义似乎与标准Scala集合完全不同,例如:据我所知,Spark中的groupBy不等同于Scala集合groupBy。
我意识到我可以在使用Spark规范化数据之前使用某种ETL处理这些文件,但似乎应该可以跳过这一步,让Spark按原样查询数据。我是从根本上走错了路吗?
答案 0 :(得分:1)
在大规模情况下,数据通常会被规范化(嵌套结构,类型不匹配,多余/缺失的字段等)。强制将半结构化数据强制标准化为DataFrame可能很麻烦,尤其是对于数十亿条记录而言。
我建议研究开源Rumble引擎,它是Spark之上的额外一层。它是专门为这种异构数据场景而设计的(免责声明:我是团队的成员)。
例如,可以浏览文档,并将其转换为可以随意查询的JSON对象(不必遵循模式)的混合序列。语言是JSONiq:
let $my-heterogeneous-list :=
(: first we convert each CSV line to a JSON object, switching on person/dog :)
for $i in text-file("/path/to/heterogeneous-csv.txt")
let $j := tokenize($i, ", ")
let $person := $j[2] eq "person"
return if($person)
then {
"kind" : "person",
"date" : $j[1],
"id" : $j[3],
"first" : $j[4],
"last" : $j[5],
"age" : $j[6]
} else {
"kind" : "dog",
"date" : $j[1],
"name" : $j[3],
"age" : $j[4],
"owner" : $j[5]
}
(: now we can query and re-arrange :)
return
for $dog in $my-heterogeneous-list[$$.kind eq "dog"]
return {|
project($dog, ("date", "name")),
{ "owner" : $my-heterogeneous-list[$$.id eq $dog.owner] }
|}
哪个返回其嵌套所有者(已规范化)的狗的列表:
{
"date" : "8\/03\/2018",
"name" : "Bella",
"owner" : {
"kind" : "person",
"date" : "8\/01\/2018",
"id" : "1",
"first" : "Bob",
"last" : "Loblaw",
"age" : "32"
}
}
{
"date" : "8\/07\/2018",
"name" : "Scout",
"owner" : {
"kind" : "person",
"date" : "8\/05\/2018",
"id" : "3",
"first" : "Charlie",
"last" : "McCharles",
"age" : "23"
}
}