景点打卡助手(二)

前一篇文章描述了数据的获取(采集)和预处理,本文介绍数据的存储

存储设计

为了实现更加快速便捷的数据访问,需要对数据存储方式进行一定的设计。虽然目前只有251个景点,但是系统应该采用适用考虑更大规模数据的设计。

设计目标

存储的核心是索引,索引设计的关键就是选择合适的索引技术。根据我们的目标:

  1. 景点查找,需求可以理解为根据名称、地区、排名、是否可用等条件进行检索
  2. 附近景点:即为需要根据经纬度进行查找
  3. 我是否来过?这个最简单,需要一张记录表。
    总的来说,系统需要支持根据关键词(名称或地区)、时间范围、true/false、经纬度等查询。显然,这需要用到全文索引、范围索引、经纬度索引,当然还有哈希索引,这基本是所有系统必备的,它实现的就是根据ID进行查找。

存储选型

传统的关系数据库基本上支持范围索引和哈希索引,尽管新版本也开始支持全文,但是毕竟这些数据库大部分是国外的,对中文支持不好。

ElasticSearch(简称ES,别和EcmaScript搞混了)是一个很直接的选择,能够支持所有四种索引,并且建索引的过程是高度可控的。ES提供了RESTFul访问接口,可以方便快速构建Web应用。

数据库准备

ES安装部署

安装非常简单,到官网下载压缩包,解压后即可运行。


文件夹结构

启动:

  bin\elasticsearch.bat

通常需要改一下配置文件 config/elasticsearch.yml。主要配置项如下:

cluster.name: es7-2019
node.name: node-1
path.data: data
path.logs: logs
network.host: 127.0.0.1
http.port: 9200
xpack.ml.enabled: false
http.cors.enabled: true 
http.cors.allow-origin: "*"

最底下的http.cors. 配置是为了后面安装的head插件跨域访问的,该插件相当于是ES的管理界面。

head插件安装

在2.x以前版本,head插件是放在ES的plugins目录下,使用http://localhost:9200/_plugin/head访问。版本5以后head插件需要单独运行。

参考这里 下载安装运行head插件。该项目基于npm+grunt,需要提前安装nodejs。

由于前面已经配置了ES支持跨域请求,所以可以看到看到界面:


head插件界面-概览

这是我已经写入数据(place库)后的样子。可以看到系统中有两个索引(相当于数据库)place和bank。集群健康状态是黄色,因为bank索引声明了1一个副本,但是集群只有一个节点(node-1),只有一份数据。place索引是完全正常的,它分为5块(12345),没有副本。

head插件界面-基本查询

图中显示了采用基本采用,loc字段匹配“延庆”的结果。这正式全文索引实现的效果,因为它会对“延庆区”、“北京延庆”等进行拆分,获得“延庆”词条。检索时是基于该词条进行查找的。

分词插件

ES 7版本自带了一个中文分词器 smartcn,效果还未检验。之前一般用IK分词器,这个插件需要单独安装。安装比较简单,参考https://github.com/medcl/elasticsearch-analysis-ik

数据处理入库

基于存储设计,现在来实现数据入库建索引。

文档结构

基于之前采集的信息和需要查询的字段,设计如下文档结构:

{  "loc": "北京.丰台", 
    "name": "北戏书馆", 
    "img": "http://zglynk.com/ITS/upload/areaIcon/3512bab5-3437-4821-bb48-edea8a185dfc.jpg", 
    "url": "http://zglynk.com/ITS/wechatPortalInfo/goAreaDetail.action?id=370", 
    "price": 20, 
    "stop": false, 
    "detail": "不限次 2018.11.1-2019.12.31(每周六接待)", 
    "rank": null, 
    "times": 0, 
    "phone": "010-67572221-2197、18612500357", 
    "time": "每周六19:00-21:00   2018.11.1-2019.12.31", 
    "geo": [116.377354, 39.85597 ] 
}

ES中对于记录唯一标识_id是在整个文档的外层。_id采用原网站的id,如果不存在,由ES生成一个UUID。

ES Mapping

ES默认会自动识别文档字段类型,建立相应的索引,但是对于一些文本和经纬度,由于检索方式多样,因此需要自己定制。索引设置index-setting.json如下:

{
    "properties":{
        "name": {"type": "text", "analyzer": "ik_smart"},
        "detail": {"type": "text", "analyzer": "smartcn"},
        "loc":  {"type": "text", "analyzer": "ik_smart"},
        "img": {"type":  "text", "index": false},
        "url": {"type": "text" , "index": false},
        "rank": {"type": "text", "analyzer": "keyword"},
        "geo": {"type": "geo_point"}
    }
}

这里对name/detail/loc三个字段采用了全文检索。不过为了试验分词器效果,我分别采用了smartcn和IK。对于img和url字段,由于不需要检索,因此不建索引。对于rank字段,它本身就是一个词(2A 3A),因此类型是keyword。geo字段设置类型为geo_point,ES为该类型简历geohash索引,支持经纬度范围查询。

注意,一般的数值、bool字段不需要特殊设置,ES会自动建立合适的索引。另外,ES还有很多参数,包括_all _source、多字段、嵌套字段、数组等,要想用好,通读一遍官方文档很重要!

创建索引

索引基本设置:index-setting.json

{
    "settings" : {
        "index" : {
            "number_of_shards" : 5, 
            "number_of_replicas" : 0 
        }
    }
}

本自己本机做测试,没有副本,分区也较少。在数据规模较大时,应该设置较多的shard数。同时为了数据安全,应该设置1-2个副本(前提是分布式集群部署)

通过curl命令创建:

host=localhost:9200
indexn=place
HEADER="Content-Type: application/json"
PREFIX="http://$host/$indexn"
curl -XPUT -H "$HEADER" $PREFIX -d "@index-setting.json"
curl -XPUT -H "$HEADER" ${PREFIX}/_mapping -d "@index-mapping.json"

小技巧:使用curl命令的 -d "@filename" 可以将复杂的body参数写在文件里面。

另外,我用的ES 7版本提示必须要加Header,默认的Content-Type: application/x-www-form-urlencoded 是不支持的。

数据处理与提交

将之前生成获取的数据,可以很容易地生成相应的文档格式。ES入库可以一条一条执行PUT请求,也可以使用它的bulk API。

为了使用bulk API,先将数据生成xjson格式:

{"index": {"_id": "370", "_index": "place"}}
{"loc": "北京.丰台", "name": "北戏书馆", "img": "http://zglynk.com/ITS/upload/areaIcon/3512bab5-3437-4821-bb48-edea8a185dfc.jpg", "url": "http://zglynk.com/ITS/wechatPortalInfo/goAreaDetail.action?id=370", "price": 20, "stop": false, "detail": "不限次 2018.11.1-2019.12.31(每周六接待)", "rank": null, "times": 0, "phone": "010-67572221-2197、18612500357", "time": "每周六19:00-21:00   2018.11.1-2019.12.31", "geo": [116.377354, 39.85597]}
{"index": {"_index": "place"}}
{"loc": "丰台区", "name": "丰台雪乡冰雪嘉年华", "price": 120, "stop": true, "detail": "接待结束", "rank": null, "times": 0}

创建索引时,每条数据分为2行,第一行指明操作及索引名和_id,第二行是需要写入的文档。换行采用\n。

为了生成这个格式,python函数:

def writeJsonArray(arr, filename):
    with open(filename, 'w') as f:
        for item in arr:
            f.write(json.dumps(item, ensure_ascii = False)) #不要格式化
            f.write('\n') #添加换行符

最后,提交整个文件给ES:

curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary "@list2.json"

小节

通过ES可以很方便地检索查询数据。不过ES本身是近实时的,采用最终一致性模型,因此不适合做一些事务类的业务,因此主要用于数据查询分析,尤其是全文检索、日志分析等。

下一步

下一步将基于vue.js框架,构建基本查询检索界面。通过openlayers或地图平台(如高德)API 进行地图展示。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,652评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,678评论 3 401
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,645评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,483评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,503评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,039评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,420评论 3 427
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,391评论 0 278
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,921评论 1 322
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,973评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,114评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,761评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,437评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,928评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,042评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,596评论 3 380
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,142评论 2 363