前言

作为程序员入职一家新公司,当你看到前任程序员写的代码的时候,你是不是经常有这样的感觉:我屮艸芔茻!这代码真他喵的烂!

问题

对于程序员来说,代码水平良莠不齐比较常见的事情,之所以代码质量不高,一方面原因是自身不太关注于代码的整体管理,另一方面原因是经验不足。实际上在处理公司业务的时候,这种前人挖坑,后人填坑的事情时有发生。

对,我们今天暂时不讨论代码规范的问题,而是说一说,再处理搜索业务的时候,如果你已经接手了一些无法描述的代码,咱们怎么补救。

首先对于任何问题而言,预防问题发生永远胜于问题发生之后再对其进行补救,这是一定的。不过既然问题已经发生,我们就只能以最小代价或者针对自身情况选择处理方式,比如,我们可以选择是先治标还是先治本,最主要区别在于见效速度。下面我们分别来说一下这两种解决方案的区别和适用场景:

常见场景

场景一

字段类型错误:这种情况一般发生在项目初期对业务的预估错误或者技术负责人对ES本身不够了解。

很多人都知道,为了优化性能,可以适当缩减字段属性。比如确定不需要聚合或者排序的字段,可对其关闭正排索引,不需要对其进行检索的字段可关闭倒排索引,不需要评分的字段可以关闭评分功能以此可节约字段存储空间,提高效率。

另外一种情况是在创建索引的时候,对ES的字段本身不够了解,比如之前我们提到的“yyyy-MM-dd HH:mm:ss”这种格式并非默认支持的时间类型,如果一开始技术员并不知道,而将数据直接写入,有可能的结果就是,时间总段存储成为了“text”类型,而给后期埋下隐患。

场景二

上述问题是字段类型错误,还有一种情况是针对于数据本身。比如:当我们某个索引报错了不同来源或渠道的数据,每个渠道可能采集数据负责的程序员并不相同,最常见的情况比如,我们采集用户行为日志,需要涉及“APP”、“Web”、“PC”、“小程序”等来源,可能负责开发的程序员分别来自不同的部门,如果在最开始没有最好约定,可能就会出现字段或者单位不统一或者一些列其他问题。总之就是协调发生问题而导致数据不统一。这也给后期做数据分析或者统计造成很大影响。

案例

我们来看下面一个例子,创建以下映射并写入一条数据:

PUT twitter
{"mappings": {"properties": {"uid": {"enabled": false}}}
}
POST twitter/_doc
{"uid": "1"
}

以上代码为我们在项目初期,负责人对将来业务的预估是不会出现通过uid 进行检索的情况,因此错误的把此字段的enabled 设置为了false, 此时数据将不能通过此字段进行检索。注意是不能通过此字段检索而不是不能将此字段检索出来,只是没有创建索引而非删除了元数据。具体体现为:下面代码第一行执行有数据,第二行无数据。

以下代码执行有结果

# 有数据
GET twitter/_search

以下代码执行无结果

# 无数据
GET twitter/_search
{"query": {"term": {"uid": {"value": 1}}}
}

痛点及现状

后期经过业务的发展和迭代,逐渐发现了此字段无法检索的问题,而很多公司的选择都是Reindex,的确,重建索引可以解决很多问题,但是这样未来过于“劳师动众!”,因为基本上ES的应用场景都是基于海量数据的索引,重建索引可能耗费大量时间,无异于杀鸡取卵,得不偿失。

有没有更好的解决方案 ?

那必须有啊!关注我就对了。

同类问题还包括如之前提到的,“yyyy-MM-dd HH:mm:ss”而导致的类型不匹配问题,在处理此一类问题时,皆可采用runtime_fields来解决。

案例:

我们沿用之前“yyyy-MM-dd HH:mm:ss 是时间类型?别再错下去了!”提到的不是时间类型导致的错误的案例,如果没看过之前的文章,戳:传送门

假如生产环境我们有如下索引,存储了一些地震的经纬度以及发生时间和描述等信息:

需求

写一个查询满足以下要求

  • 1:按星期分桶统计地震数据
  • 2:输出星期一至星期日中平均地震等级 没有数据的不显示
  • 3:返回平均地震等级最大的一个 是星期几
  • 4:进阶问题 每个星期的平均地震等级
  • 5:进阶问题 平均地震等级最大的是哪个星期
POST task1/_bulk?refresh=true
{"index":{}}
{"time":"2011-06-16 12:12:21","magnitude" : 1.4, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-06-16 12:12:21","magnitude" : 1.3, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-06-17 12:12:21","magnitude" : 1.5, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-04-18 12:12:21","magnitude" : 1.6, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-06-19 12:12:21","magnitude" : 1.9, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-06-20 12:12:21","magnitude" : 2.0, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":1308544245123,"magnitude" : 2.1, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":1308717045123,"magnitude" : 2.8, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-06-20 12:12:21","magnitude" : 2.9, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}
{"index":{}}
{"time":"2011-06-20 12:12:21","magnitude" : 3.3, "lon" : -116.0902, "lat" : 33.2253, "depth" : 9.98, "area" : " 10km NNE of Ocotillo Wells"}

这是我为Elastic认证考试出的一道模拟题,在此我们简化题目,只看前两个问题

但是,因为不可描述的原因,time字段有的数据存储成为了yyyy-MM-dd HH:mm:ss,有的存储成为了时间戳,最终time字段的类型并没有像预想的那样为date类型他们可能来自于同一个公司的不同部门,所以导致了这种问题。

面临的问题

此时,面对上述需求,存在两个问题

  • time字段为text而非date类型,而导致我们无法使用日期时间类型的所有函数。
  • 数据存储的格式不统一,有时间戳,有"yyyy-MM-dd HH:mm:ss",无法统一计算。

解决方案:runtime_fields

运行时字段是在执行查询时动态对字段类型和索引重新定义的字段。runtime_fields具备以下特点:

  • 在不重新索引数据的情况下向现有文档添加字段
  • 在不了解数据结构的情况下开始处理数据
  • 在查询时覆盖从索引字段返回的值
  • 为特定用途定义字段而不修改底层架构

由于运行时字段未编入索引,因此添加运行时字段不会增加索引大小。直接在索引映射中定义运行时字段,可以节省存储成本并提高预处理速度。

如果将运行时字段设为索引字段,则无需修改任何引用运行时字段的查询。而且可以引用字段是运行时字段的一些索引,以及字段是索引字段的其他索引。可以灵活地选择要索引哪些字段以及保留哪些字段作为运行时字段。

就其核心而言,运行时字段最重要的好处是能够在提取文档后将字段添加到文档中。此功能简化了映射决策,所以不需要预先决定如何解析数据,并且可以使用运行时字段随时修改映射。使用运行时字段索引更小且更快的预处理信息,这结合使用更少的资源并降低运营成本。

上述问题,解决方案代码如下:

GET task1/_search
{"size": 0,"runtime_mappings": {"day_of_week": {"type": "keyword","script": "emit(doc['time'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL,Locale.ROOT))"},"time": {"type": "date","format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"}},"aggs": {//1:按照周统计地震信息,也就是每周有几天地震了"week_agg": {"date_histogram": {"field": "time","calendar_interval": "week"},"aggs": {"week_avg_magnitude": {"avg": {"field": "magnitude"}}}},//一周中的每一天的震级"day_of_week_magnitude":{"terms": {"field": "day_of_week"},"aggs": {//2: 一周中每一天的平均地震等级"day_of_week_avg_magnitude": {"avg": {"field": "magnitude"}}}}}
}

此段代码中,其核心代码如下

"runtime_mappings": {"day_of_week": {"type": "keyword","script": "emit(doc['time'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL,Locale.ROOT))"},"time": {"type": "date","format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"}}

其在runtime_mappings 中定义了两个“新字段”,即day_of_weektime,其中day_of_week利用运行时字段中执行脚本进行动态计算,从而得出每天分别是一周内的星期几。这种用法可用于各种其他复杂的运算。

time字段则是对原有字段进行重新映射,改变其原有字段的类型和其他属性,如format,使其原本不支持的时间类型变为支持。

不必Reindex,利用runtime_fields优雅地解决字段类型错误问题相关推荐

  1. 转: 利用SQL SERVER2005的XML字段类型实现类似商品扩展属性

    今天自己翻了两页自己的BLOG,发现真是越来越懒了,已经好长时间没有写技术相关的日志了,记得刚开始写BLOG的时候还经常写写自己的技术积累的,现在除了发布ZJ-BLOG的程序更新信息外,好像全成了自己 ...

  2. 【vue.js开发】如何在vue里面优雅的解决跨域,路由冲突问题

    [vue.js开发]如何在vue里面优雅的解决跨域,路由冲突问题 当我们在路由里面配置成以下代理可以解决跨域问题 proxyTable: {'/goods/*': {target: 'http://l ...

  3. es如何修改es索引字段类型 reindex

    今天因为上线之前,忘记提单先把es的索引新增的几个字段先建好,导致需求上线后,一些触发推送到es的数据,使得es自动根据数据类型创建索引字段类型,原本我打算增加的mapping字段是数组keyword ...

  4. Mybatis优雅存取json字段的解决方案 - TypeHandler (一)

    起因 在业务开发过程中,会经常碰到一些不需要检索,仅仅只是查询后使用的字段,例如配置信息,管理后台操作日志明细等,我们会将这些信息以json的方式存储在RDBMS表里 假设某表foo的结构如下,字段b ...

  5. 【转】MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突

    [转]MyBatis学习总结(四)--解决字段名与实体类属性名不相同的冲突 在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体 ...

  6. MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突

    2019独角兽企业重金招聘Python工程师标准>>> 一.准备演示需要使用的表和数据 CREATE TABLE orders(order_id INT PRIMARY KEY AU ...

  7. CVPR 2018 | ETH Zurich提出利用对抗策略,解决目标检测的域适配问题

    CVPR 2018 | ETH Zurich提出利用对抗策略,解决目标检测的域适配问题 原创: Panzer 极市平台 今天 ↑ 点击蓝字关注极市平台 识别先机 创造未来 论文地址:https://a ...

  8. 【s操作】上瘾了!继续轻松优雅的解决孩子的初中数学题,matlab入门新方法解题无压力...

    前两天发了用在线工具(不用安装的工具)解方程([PC工具]如何简单粗暴无脑的解方程)和用这个工具解决小学应用题([s操作]轻松优雅的解决孩子的小学数学题).有朋友就问了,你这简单的题还用开电脑,我口算 ...

  9. Mybatis解决字段名与实体类属性名不相同的冲突

    在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体类属性名不相同的冲突. 一.准备演示需要使用的表和数据 CREATE TAB ...

最新文章

  1. MS SQL入门基础:移动数据库
  2. 11g新特性-SQL Plan Management
  3. 利用优盘安装win2008r2系统
  4. iOS之深入解析消息转发objc_msgSend的应用场景
  5. 【SDL】SDL简介
  6. oracle基本笔记整理
  7. 15天掌握Al声纹识别,同盾开启智能风控建模赛训
  8. [渝粤教育】陕西师范大学《教育社会学》作业
  9. F.grid_sample
  10. 字体 流光css,实例详解CSS3制作文字流光渐变特效
  11. fatal: unable to access ‘http://g.oho168.com:88/yanjiaxue/anymz.git/‘: Could not resolve host: g.oho
  12. office邮箱不能预览附件问题
  13. 登录失败:用户帐户限制。可能的原因包括不允许空密码,登录时间限制,或强制的策略限制。 ...
  14. 使用Echarts制作动态嵌套饼图
  15. MySQL优化系列3-Linux查看CPU、内存、磁盘、网络信息
  16. pr系统兼容性报告不支持视频驱动程序有什么影响?怎么解决?
  17. 非参数统计:两样本和多样本的Brown-Mood中位数检验;Wilcoxon(Mann-Whitney)秩和检验及有关置信区间;Kruskal-Wallis秩和检验
  18. linux我的世界乱码,我的世界附魔台文字翻译成普通文字 附魔台文字乱码解决办法...
  19. 利用500万条微博语料对微博评论进行情感分析
  20. Python的线程17 Condition类,田径赛场上的主裁判

热门文章

  1. 前端自动化部署,基于scp2,ssh2
  2. 怎么写安卓手机脚本_拉结尔手游攻略,云手机全自动挂机刷副本及装备
  3. 百度一下,你就知道”你真的知道嘛?
  4. 桌上有一空盘,最多允许存放一只水果。爸爸只向盘中放一个苹果,妈妈只向盘中放一个桔子,儿子专等吃盘中的桔子,女儿专等吃苹果。用wait、signal操作实现爸爸、妈妈、儿子、女儿四个并发进程的同步。
  5. NFT元宇宙游戏开发铸造源代码怎么写案例分享
  6. 【python】利用两层神经网络(网络必须用类)来训练mnist数据(要求准确率90%以上)
  7. halo开源博客搭建微信小程序实现内容同步-总结
  8. 拒绝做工具小子—编写Python漏洞验证脚本
  9. Flutter弹起键盘页面布局超限问题以及布局上移问题
  10. TimesTen缓存管理员用户到底需要什么权限?