我正在将数据从ElasticSearch移动到HDFS,使用数据库库将它们保存为avro。 我需要将数据展平作为输出,所以我正在应用以下功能:
def flattenSchema(schema: StructType, prefix: String = null) : Array[Column] = {
schema.fields.flatMap(f => {
val colName = if (prefix == null) f.name else (prefix + "." + f.name)
f.dataType match { case st: StructType => flattenSchema(st, colName) case _ => Array(col(colName)) } }
)
}
到我的数据:
var df = sql.read.format("es").load("my-index/log").withColumnRenamed("@timestamp", "ts")
val flattened = flattenSchema(df.schema)
val renamed = flattened.map(name => col(name.toString()).as(name.toString().replace(".","_")))
df = df.select(renamed:_*)
直到现在,这都是一种魅力。
不幸的是,现在我必须从ES返回的_id
添加_metadata
。首先,我启用了元数据设置--conf spark.es.read.metadata="true"
。显然,我的函数不会平铺地图中的数据,只是结构。我的数据帧的架构现在看起来像:
root
|-- ts: timestamp (nullable = true)
|-- field_1: string (nullable = true)
|-- field_2: string (nullable = true)
|-- field_n: string (nullable = true)
|-- _metadata: map (nullable = true)
| |-- key: string
| |-- value: string (valueContainsNull = true)
如何仅从_id
中提取_metadata
密钥和相关值,并将其添加到df
? (同时删除_metadata
本身,因为我只需要_id
字段)
答案 0 :(得分:0)
对于我的用例,我发现了一个几乎无法接受的解决方法。我没有从ES获取_id
,而是在Spark中生成一个UUID,创建消息的MD5。通过这种方式,您可以放弃ES和Hadoop之间的匹配,但您可以根据UUID本身的假设进行一些分析。我把这个"部分"解决方案,因为它可能会帮助一些未来的googlers:
import java.security.MessageDigest
val md5 = udf((string: String) => {MessageDigest.getInstance("MD5").digest(string.getBytes).map("%02X".format(_)).mkString + scala.util.Random.alphanumeric.take(10).mkString })
df = df.withColumn("uuid",md5(col("message")))
我还会在生成字符串的同时添加一些盐(10个字符的随机字符串),只是为了确保减少名称标记,如果我不幸的话,使用相同的时间戳将相同的日志记录两次。