Spark——Spark读Elasticsearch Index索引报错存在重复列名

文章目录

  • 问题背景
  • 解决方法
  • 参考

问题背景

在通过Spark读取Elasticsearch的索引并映射成结构化的表,写入到Hive的时候,报错存在同名的列名/字段名。查看Elasticsearch对应index发现,index存在字段名相同但其中某些字母大小写不同的字段(例如,同一个index存在gmtCreate和gmtcreate)。

因为Elasticsearch index中字段默认是大小写敏感的,也就是说,可以存在我们刚才描述的那种情况。而Hive中表列名是大小写不敏感的,Spark读Elasticsearch index转成Dataframe之后,会将index中的字段名全都转成小写,那么在写入Hive的时候,就会报错说存在重名的列。

此处也给出,Spark整合Elasticsearch的Maven依赖:

<dependency>
    <groupId>org.elasticsearchgroupId>
    <artifactId>elasticsearch-spark_2.11artifactId>
    <version>2.4.1version>
dependency>

解决方法

那么,如果想要把ES index索引存入Hive,我们需要对重名列进行重命名。

  1. 当通过Spark将Elasticsearch index转成DataFrame的时候,我们需要保持列名的大小写敏感,而Spark SQL默认情况下对列名是大小写不敏感的,所以我们需要配置参数spark.sql.caseSensitive来开启列名的大小写敏感;
    val spark = SparkSession
                   .builder()
                   .appName("es index")
                   .config("es.nodes", "node-1,node-2,node-3,node-4,node-5")
                   .config("es.port", "9200")
                   .config("pushdown", "true")
                   .config("spark.sql.caseSensitive", "true") //列名大小写敏感
                   .enableHiveSupport()
                   .getOrCreate()
    
  2. 找出重名的列,并进行相应的处理
    import org.elasticsearch.spark._ 
    
    def process(spark: SparkSession): Unit = {
        import spark.implicits._
        //读取Elasticsearch索引加载为Spark RDD
        val rdd: RDD[(String, String)] = spark.sparkContext.esJsonRDD(index)
                    .map(m => (m._1.toString, m._2.toString))
        
        //将Tuple类型的RDD转成Dataset
        val ds = spark.createDataset(rdd)
        			.toDF("doc_id", "source")
                    .mapPartitions(iterator => {
                        iterator.map(m => {
                            JSON.parseObject(m.getAs("source").toString).toString
                        })
                    })
            var df = spark.read.json(ds)
    		//获取Dataframe中所有列名
            val columns = df.columns
            //将所有列名都转成小写
            val columnsDistinct = columns.map(_.toLowerCase()).distinct
    
            //是否有重复列
            if (columns.length != columnsDistinct) {
                val set = new mutable.HashSet[String]()
                //保存重复列
                val duplicatedCols = mutable.ArrayBuffer[String]()
                for (col <- columns) {
                    if (set.contains(col.toLowerCase)) {
                        duplicatedCols.append(col)
                    } else {
                        set.add(col.toLowerCase)
                    }
                }
                //对重复列进行重命名
                for (col <- duplicatedCols) {
                    df = df.withColumnRenamed(col, col + "_1")
                }
            }
    
            df
                        .repartition(3)
                        .write
                        .mode("overwrite")
                        .format("parquet")
                        .saveAsTable(s"db.table")
    }
    

参考

  • https://www.elastic.co/guide/en/elasticsearch/hadoop/current/spark.html
  • https://spark.apache.org/docs/3.1.2/sql-migration-guide.html#upgrading-from-spark-sql-23-to-24

你可能感兴趣的:(Spark,spark,elasticsearch,scala,Hive)