实现一个外部数据源

要定义一个外部数据源可以参考
JDBCrelation和JDBCrelationprovider

JDBCrelation相当于用户可以使用里面的方法实现数据select,过滤和插入。

JDBCrelationprovider相当于用户和JDBCrelation的中转站,解析用户传入的参数。

JDBCrelationprovider

在这里插入图片描述createrelation可以接受用户参数,调用JDBCrelation

模仿JDBCrelationprovider

package sql04.text

import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.sources.{BaseRelation, RelationProvider, SchemaRelationProvider}
import org.apache.spark.sql.types.StructType

//jdbcrelationprovider
//必须叫DefaultSource,不然调用报错
//schemarelationprovider可以让用户传入schema
class DefaultSource extends RelationProvider  with SchemaRelationProvider{

  def createRelation(
                      sqlContext: SQLContext,
                      parameters: Map[String, String],
                      schema: StructType): BaseRelation = {
    //读文件就要path
    val path = parameters.get("path")
    path match {
      case Some(x) => new TextDatasourceRelation(sqlContext, x, schema)
      case _ => throw new IllegalArgumentException("path is required for custom-text-datasource-api")

    }
  }


  override def createRelation(sqlContext: SQLContext, parameters: Map[String, String]): BaseRelation = {
        createRelation(sqlContext,parameters,null)

  }
}

这里继承的RelationProvider和SchemaRelationProvider区别在于,schemarelationprovider可以接受schema码。
在这里插入图片描述
注意,这里的类名要是DefaultSource。
在这里插入图片描述

JDBCrelation

在这里插入图片描述在这里插入图片描述可以看出JDBCRelation继承自Baserelation,PrunedFilteredScan,InsertableRelation

Baserelation

定义了schema信息,相当于StructType

PrunedFilteredScan

用户可以做行过滤和列裁剪
select a,b,c from student where id>10;

类似的还有
TableScan
只能显示全部数据
select a,b,c,d,e from student;

PrunedScan
可以做列裁剪
select a,b,c from student;

InsertableRelation
可以写数据

在这里插入图片描述实现这些的是需要用户自定义的buildScan函数。

模仿JDBCrelation

package sql04.text

import org.apache.spark.internal.Logging
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.sources._
import org.apache.spark.sql.types.{LongType, StringType, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Row, SQLContext, SaveMode}

//jdbcrelation
class TextDatasourceRelation(override val sqlContext: SQLContext,
                             path:String,
                             userSchema:StructType)
//日志
  extends BaseRelation
    with TableScan
    with PrunedScan
    with PrunedFilteredScan
    with InsertableRelation
    with Logging {
  //可以用外面传进来StructType,也可以默认
  override def schema: StructType = {
    if (userSchema != null) {
      userSchema
    } else {
      StructType(
        //::和,皆可
        StructField("id",LongType,false)::
        StructField("name",StringType,false)::
          StructField("gender",StringType,false)::
          StructField("salary",LongType,false)::
          StructField("comm",LongType,false)::Nil
      )
    }
  }

  override def buildScan(): RDD[Row] = {

    logWarning("this is ruozedata custom buildScan().")

    //wholeTextFiles,返回前面文件名后面内容
    val rdd = sqlContext.sparkContext.wholeTextFiles(path).map(x=>x._2)
    //返回的是schema的数组
    val schemaFields = schema.fields

    /*
    把field作用到rdd上面去
    如何根据schema的field的数据类型以及字段顺序整合到rdd
     */

    val rows = rdd.map(fileContent => {
      //注意fileContent是一段内容
      val lines = fileContent.split("\n")
      //转换成sequencefile
      val data = lines.map(x => x.split(",").map(x=>x.trim).toSeq)

      //按照字段field整合到rdd
      //按照index压在一起
      /**
        * 10000 long
        * ruoze string
        */
      val typedValues = data.map(x => x.zipWithIndex.map {
        case (value, index) => {
          val colName = schemaFields(index).name

          Utils.castTo(if (colName.equalsIgnoreCase("gender")) {
            if (value == "0") {
              "男"
            } else if (value == "1") {
              "女"
            } else {
              "未知"
            }
          } else {
            value
          }, schemaFields(index).dataType)
        }
      })

      typedValues.map(x=>Row.fromSeq(x))
    })

     rows.flatMap(x=>x)

  }

  override def buildScan(requiredColumns: Array[String]): RDD[Row] = {
    logWarning("this is ruozedata custom buildScan(requiredColumns).")

    //wholeTextFiles,返回前面文件名后面内容
    val rdd = sqlContext.sparkContext.wholeTextFiles(path).map(x=>x._2)
    //返回的是schema的数组
    val schemaFields = schema.fields

    /*
    把field作用到rdd上面去
    如何根据schema的field的数据类型以及字段顺序整合到rdd
     */

    val rows = rdd.map(fileContent => {
      //注意fileContent是一段内容
      val lines = fileContent.split("\n")
      val data = lines.map(x => x.split(",").map(x=>x.trim).toSeq)

      //按照字段field整合到rdd
      //按照index压在一起
      /**
        * 10000 long
        * ruoze string
        */
      val typedValues = data.map(x => x.zipWithIndex.map {
        case (value, index) => {
          val colName = schemaFields(index).name

          val castedValue = Utils.castTo(if (colName.equalsIgnoreCase("gender")) {
            if (value == "0") {
              "男"
            } else if (value == "1") {
              "女"
            } else {
              "未知"
            }
          } else {
            value
          }, schemaFields(index).dataType)

          if (requiredColumns.contains(colName)){
            Some(castedValue)
          } else {
            None
          }

        }
      })

      typedValues.map(x=>Row.fromSeq(x.filter(_.isDefined).map(x=>x.get)))
    })

    rows.flatMap(x=>x)
  }

  override def buildScan(requiredColumns: Array[String], filters: Array[Filter]): RDD[Row] = {
    logWarning("this is ruozedata custom buildScan(requiredColumns,filters).")

    logWarning("Fileter:")
    filters.foreach(x => println(x.toString))

    //wholeTextFiles,返回前面文件名后面内容
    val rdd = sqlContext.sparkContext.wholeTextFiles(path).map(x=>x._2)
    //返回的是schema的数组
    val schemaFields = schema.fields

    /*
    把field作用到rdd上面去
    如何根据schema的field的数据类型以及字段顺序整合到rdd
     */

    val rows = rdd.map(fileContent => {
      //注意fileContent是一段内容
      val lines = fileContent.split("\n")
      val data = lines.map(x => x.split(",").map(x=>x.trim).toSeq)

      //按照字段field整合到rdd
      //按照index压在一起
      /**
        * 10000 long
        * ruoze string
        */

      //见下图
      val typedValues = data.map(x => x.zipWithIndex.map {
        //第一列的索引都是0,第二列都是1.。。
        case (value, index) => {
          val colName = schemaFields(index).name

          /**
            * public boolean equalsIgnoreCase(String anotherString)将此 String 与另一个 String 进行比较,不考虑大小写。如果两个字符串的长度相等,并且两个字符串中的相应字符都相等(忽略大小写),则认为这两个字符串是相等的。
            */
            //(value,dataType)
          val castedValue = Utils.castTo(if (colName.equalsIgnoreCase("gender")) {
            if (value == "0") {
              "男"
            } else if (value == "1") {
              "女"
            } else {
              "未知"
            }
          } else {
            value
          }, schemaFields(index).dataType)

          if (requiredColumns.contains(colName)){

            Some(castedValue)
          } else {
            None
          }

        }
      })
      //这个row.fromseq可以通过sequencefile构建一个row
      //isDefined,如果是some类型返回true
      //x把some里的值取出来
      val asd  = typedValues.map(x=>Row.fromSeq(x))
      println(asd)
      typedValues.map(x=>Row.fromSeq(x.filter(_.isDefined).map(x=>x.get)))
    })

    rows.flatMap(x=>x)
  }

  override def insert(data: DataFrame, overwrite: Boolean): Unit = {
    data.write
      .mode(if (overwrite) SaveMode.Overwrite else SaveMode.Append)
      .save(path)
  }
}

filecontent的值

在这里插入图片描述在这里插入图片描述

lines的值

在这里插入图片描述在这里插入图片描述

data的值

在这里插入图片描述

zipwithindex

在这里插入图片描述

typedValues

在这里插入图片描述

typedvalues转换为row的值

在这里插入图片描述

代码里的Utils.castTo

功能是根据schema的数据类型对相应数据进行类型转换

package sql04.text

import org.apache.spark.sql.types.{DataType, LongType, StringType}

object Utils {
  def castTo(value:String,dataType: DataType)={
    dataType match {
        // case LongType => value.toLong 一样
      case _:LongType => value.toLong
      case _:StringType => value
    }
  }

}

提交到spark执行

只要–jars即可实现插入式调用。
format里指定外部数据源文件夹在jar包的位置即可

val people = sparkSession.read.format("sql04.text").option("path"," ").load()
people.select("id","name").filter('id>2).filter('salary >2000).show()

猜你喜欢

转载自blog.csdn.net/qq_36459386/article/details/85869605