在ES

时间:2017-06-30 07:24:18

标签: scala apache-spark elasticsearch

我正在将数据从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字段)

1 个答案:

答案 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个字符的随机字符串),只是为了确保减少名称标记,如果我不幸的话,使用相同的时间戳将相同的日志记录两次。