0、引言
疫情当下,大家几乎每天都在用疫情地图的相关功能,其中的核心功能即地理位置检索,使用 ES 可以非常轻松的实现,下面我简单说一下核心的几个功能如何设计和实现。
本文不涉及前段UI的设计和实现以及客户端调用代码,如果大伙儿需要,可以给我留言,需求量比较大的话可以考虑做一个完整项目开源出来,点赞支持下吧
PS:学习本文需要对 Elasticsearch 地理位置检索 的基本功能有一定的了解,推荐阅读:todo
本文案例中所使用的数据,可在文末下载。
1、功能模块
下图为疫情地图的基本木块,本文目的为将地理位置检索应用于项目落地,因此和核心功能无关的业务模块将不再罗列。主要的实现的功能如图所示
2、索引结构设计
2.1 地区索引
省市区联动是在很多场景下都是非常常见的,其表结构也非常简单,通过一个pid
或者pcode
即可保存起级联关系。
我这里仍然是保留了省市区之间的逻辑关系,但是这一点对本文要实现的功能是没有用处的。建议在索引中为每一个省份、城市、地区、街道单独创建一个文档,使用polygon
存储。
ES 支持仅支持基本的几何图形,并不支持不规则几何图形,省份地区这种图形如何存储?
其实不规则几何图形可以看成是边很多的多边形,当边的数量足够多,经可以足够精确的描述一个不规则多边形。这其实和你玩游戏的时候一样,一些需要显示曲线的场景,其实就是由无数个多边形来描述的,当边的数量足够多的时候,你就看不出的他是个多边形了。
PUT area
{
"mappings": {
"properties": {
"location": {
"type": "geo_shape"
},
"city": {
"properties": {
"location": {
"type": "geo_shape"
},
"district": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
}
}
}
}
2.2 机构索引
机构可以是:医院、学校、桥梁、公司、隧道等任意单位,具体包含什么取决于你在做功能时想搜索到什么,一般机构为一个点坐标即可,即:geo_shape:point
。为了简化代码,我这里只存储一个医院信息,也可以理解为核酸检测机构的网点。
索引 mapping 如下:
PUT hospital
{
"mappings": {
"properties": {
"properties" : {
"address" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"district" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"location" : {
"type" : "geo_shape"
},
"lv" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
2.3 人员信息索引(上报信息)
人员信息上报其实包含了如健康码、行程码(行程轨迹)、核酸检测报告、健康状态等信息,信息上报基本发生在门禁扫码登记、核酸检测结果发生时期等,行程轨迹上报时间可通过手机连接的基站发生变化而做出轨迹测算和上报。为了简化功能,我们人员的健康信息只保留一个状态字段:及健康或感染(确诊)。
索引 mapping:
PUT case_person
{
"mappings": {
"properties": {
"date": {
"type": "date"
},
"location": {
"type": "geo_shape"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"status": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
3、实现
3.1 统计当日新增
这里只是做一个简单的统计,不再区分本土、外来、无症状等业务字段,统计当日新增,即表示满足
- 确诊时间为当日
- 状态为已确诊
两个条件的所有人员信息,代码如下:
GET case_person/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{"range": {
"date": {
"gte": "now/d"
}
}},
{
"term": {
"status.keyword": {
"value": "确诊"
}
}
}
]
}
},
"aggs": {
"today_add": {
"value_count": {
"field": "status.keyword"
}
}
}
}
3.2 查询周边的确诊病例
查询周边确诊病例或者查询所在行政区的确诊病例,可应用如判断所属地区是否属于中高风险地区等业务,实现起来也非常简单。只需提供两个条件
- 当前所处经纬度坐标
- 搜索半径
如果是在APP中,当前所处坐标直接通过调用SDK中提供的位置接口即可获取,如果是用于测试,可通过百度地图提供的开放接口在地图上选点获取坐标。操作步骤为:
可以在地图中选出自己心仪的坐标,模拟自己当前所处位置。比如我当前所处位置经纬度为:
- “lon”:116.238334,
- “lat”:39.900112
以图中定位的永辉超市为圆心,半径三公里,搜索到了 八角游乐园、八宝山、玉泉路、五棵松四个地铁站(这里地铁站可以看做是确认病例),即三公里范围内有四个确诊病例。
GET province_bak/_search
{
"query": {
"term": {
"name.keyword": {
"value": "海淀区"
}
}
}
}
GET hospital/_search
{
"_source": {"include":["name","district"]},
"query": {
"geo_shape": {
"location": {
"indexed_shape": {
"index": "province_bak",
"id": "110108",
"path": "location"
},
"relation": "within"
}
}
}
}
索引数据下载:https://t.zsxq.com/037YJUJia