目录
- 一、新建Mapping
- 二、导入ES依赖
- 三、程序实现
- 1、创建spark入口、创建ES客户端
- 2、读取数据,处理前几行头信息
- 3、提取属性值
- 4、将坐标拼成WKT文本
- 5、将属性拼接成对象
- (1) XContentBuilder 形式
- (2)Gson的JsonObject 形式
- (3)fastJson的JSONObject 形式
- 6、关闭资源
一、新建Mapping
可以在Kibana中建好mapping,也可以在api中写好,执行程序的时候创建。
这里在Kibana已经创建好
PUT /realweather
{
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"realtime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"geometry": {
"type": "geo_shape"
},
"tem": {
"type": "integer",
"index": false
},
"vis": {
"type": "double",
"index": false
}
}
}
}
- 日期类型设定为“date”,并设定好日期的格式为"yyyy-MM-dd HH:mm:ss"
- geometry存储的为网格,类型设定为“geo_shape”(polygon),在以坐标或网格检索的时候方便检索
- “index”: false 意思是该字段不作为索引字段,默认为true
二、导入ES依赖
Java REST Client 有两种风格:
- Java Low Level REST Client :用于ES的低级客户端。它允许通过http与Elasticsearch集群通信。 将请求编排和响应反编排留给用户自己处理。
- Java High Level REST Client :用于ES的高级客户端。它提供很多API,并负责请求的编排与响应的反编排。使用需与ES版本保持一致。
就好比是,一个是传自己拼接好的字符串,并且自己解析返回的结果;
而另一个是传对象,返回的结果也已经封装好了直接是对象,更加规范了参数的名称以及格式,更加面向对象
- 这里使用的是高级API客户端
<!-- ES -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.0.0</version>
</dependency>
三、程序实现
1、创建spark入口、创建ES客户端
- 因为后续要使用spark ml算法库进行建模训练数据,所以我这里是使用spark程序来实现的,用java也是一样的。
- 注意地址要写对,端口要使用9200,因为使用的是http
import java.math.BigDecimal
import java.text.SimpleDateFormat
import com.google.gson.JsonObject
import org.apache.http.HttpHost
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.elasticsearch.action.bulk.{BulkRequest, BulkResponse}
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.client.{RequestOptions, RestClient, RestHighLevelClient}
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.common.xcontent.{XContentBuilder, XContentFactory, XContentType}
object ParseRealWeather {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
.setAppName(this.getClass.getSimpleName)
.setMaster("local[*]")
val sc = new SparkContext(conf)
// 创建es的客户端
val client = new RestHighLevelClient(RestClient.builder(new HttpHost("10.xx.xxx.xxx", 9200, "http"), new HttpHost
("10.xx.xxx.xxx", 9200, "http")))
2、读取数据,处理前几行头信息
val lines: RDD[String] = sc.textFile("D:\\testzip\\roadweather\\202008140950.txt")
val startArr: Array[String] = lines.take(5)
val timeStr: String = startArr(0) + "00"
val format1 = new SimpleDateFormat("yyyyMMddHHmmss")
val weaTime: Long = format1.parse(timeStr).getTime
val format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
// 格式化为 mapping中设定的日期格式
val realTime: String = format2.format(weaTime)
val latLon: Array[String] = startArr(1).split(" ")
val latPos: Double = latLon(0).toDouble
val lonPos: Double = latLon(1).toDouble
3、提取属性值
- 使用了BigDecimal来解决double类型的值,在做运算时,精度丢失的问题
// 处理的时候过滤掉前几行
val dataRdd: RDD[String] = lines.filter(!startArr.contains(_))
dataRdd.take(150).foreach(line => {
//25_3651:["00029000.00000.00-0.100.119.900801007"]
val lineArr: Array[String] = line.split(":")
val tileLat: Int = lineArr(0).split("_")(0).toInt
val tileLon: Int = lineArr(0).split("_")(1).toInt
// 解决double类型缺失精度的问题
val latPosBd = new BigDecimal(latPos.toString())
val tileLatBd = new BigDecimal((tileLat * 0.01).toString())
val lonPosBd = new BigDecimal(lonPos.toString())
val tileLonBd = new BigDecimal((tileLon * 0.01).toString())
val lat: Double = latPosBd.add(tileLatBd).doubleValue()
val lon: Double = lonPosBd.add(tileLonBd).doubleValue()
val tem: Int = lineArr(1).substring(4, 7).toInt
val vis: Double = lineArr(1).substring(27, 32).toDouble
val lonBd = new BigDecimal(lon.toString())
val zzBd = new BigDecimal(0.01.toString())
val latBd = new BigDecimal(lon.toString())
val zaBd = new BigDecimal(0.01.toString())
val finalLon: Double = lonBd.add(zzBd).doubleValue()
val finalLat: Double = latBd.add(zaBd).doubleValue()
4、将坐标拼成WKT文本
- 使用的是POLYGON的格式
- 因为存储的是四边形网格,es中要求网格必须是封闭的,所以网格首尾坐标必须重合
- 有关WKT的介绍, 请点击这里。
// 拼接字符串
val geometry: String = "POLYGON ((" + lat + " " + lon + "," + finalLat + " " + lon + "," + finalLat + "" +
" " + finalLon + "," + lat + " " + finalLon + "," + lat + " " + lon + "))"
5、将属性拼接成对象
(1) XContentBuilder 形式
val builder: XContentBuilder = XContentFactory.jsonBuilder()
.startObject()
.field("realtime", realTime)
.field("geometry", geometry)
.field("tem", tem)
.field("vis", vis)
.endObject()
// 批量处理
val request = new BulkRequest
request.add(new IndexRequest("realweather").source(builder))
val response: BulkResponse = client.bulk(request, RequestOptions.DEFAULT)
})
(2)Gson的JsonObject 形式
- 使用gson的JsonObject,在source的时候,需要自己写tostring方法,因为没有toJsonString()方法
val jsonObject = new JsonObject
jsonObject.addProperty("realWeaTime", realWeaTime)
jsonObject.addProperty("geometry", geometry)
jsonObject.addProperty("tem", tem)
jsonObject.addProperty("vis", vis)
// 批量处理
val request = new BulkRequest
request.add(new IndexRequest("realweather").source(jsonObject.toString, XContentType.JSON))
val response: BulkResponse = client.bulk(request, RequestOptions.DEFAULT)
})
(3)fastJson的JSONObject 形式
- 使用fastJson的JSONObject,本身有toJsonString()方法,因为source中可以转换
// 使用fastJson的JSONObject,本身有toJsonString()方法,因为source中可以转换
val nObject1 = new JSONObject()
nObject1.put("realWeaTime", realWeaTime)
nObject1.put("geometry", geometry)
nObject1.put("tem", tem)
nObject1.put("vis", vis)
// 批量处理
val request = new BulkRequest
request.add(new IndexRequest("realweather").source(nObject1 ))
val response: BulkResponse = client.bulk(request, RequestOptions.DEFAULT)
})
注意上面json对象的不同之处,否则在.source()的时候,会报错:The number of object passed must be even but was [1] |
6、关闭资源
client.close()
sc.stop()
}
}