干货 |《从Lucene到Elasticsearch全文检索实战》拆解实践
1、题记
2018年3月初,萌生了一个想法:对Elasticsearch相关的技术书籍做拆解阅读,该想法源自非计算机领域红火已久的【樊登读书会】、得到的每天听本书、XX拆书帮等。
目前市面上Elasticsearch的中文书籍就那么基本,针对ES5.X以上的三本左右;国外翻译有几本,都是针对ES1.X,2.X版本,其中《深入理解Elasticsearch》还算比较经典。
拆书的目的:
- 1)梳理已有的Elasticsearch知识体系;
- 2)拾遗拉在角落的Elasticsearch知识点;
- 3)通过手敲动代码或命令行,在实践中再次“温故知新”,提前增加知识储备,避免项目/产品实战中的“临阵抱佛脚”;
- 4)最大化的节省您的宝贵时间,让您最快的时间吸取最精华的“干货”。
本次解读是《从Lucene到Elasticsearch全文检索实战》。
2、本书梗概
作者是中科院硕士姚攀(90后)在读研究生期间根据实习写成CSDN博客,最终成书。
- 该书1/4章节讲解Lucence相关原理及实战;
- 1/2章节讲解Elasticsearch基本概念:集群入门、搜索分类详解、聚合分析、Java API;
- 1/4章节讲解Elasticsearch集群管理、项目实战、Hadoop实战。
总体评价:
优点:
- 1)涵盖了Elasticsearch相关的基本概念、基础原理;
- 2)有两个实战项目分享;
缺点:
- 1)某些概念就只是有分类,没有讲解出不同分类的区别,不同分类的适用场景;
- 2)某些细节点涵盖不全,偏理论,好多知识技术点,实战中应用会有不同。
- 3)书基于Elasticsearch5.4.0讲解,一些特性6.X已不适用。
3、核心知识点梳理
以下的DSL都是通过ElasticsearchV6.2.2版本试验过的。
3.1 mget 一次获取多个文档。
1GET test_index/test_type/_mget
2{
3 "docs":[
4 {"_id":1},
5 {"_id":3}
6 ]
7}
最小简化版本:
1GET test_index/test_type/_mget
2{
3 "ids":[1,3]
4}
3.2 update更新
——添加、删除、更新字段
1POST test_index/test_type/1
2{
3 "no":1,
4 "name":"奔驰X100",
5 "addr":"德国",
6 "price":1000000,
7 "tags" : ["red"]
8}
3.2.1 添加字段
以下添加了新字段tags,赋值为“red”。
1POST test_index/test_type/1/_update
2{
3 "script":"ctx._source.tags = \"red\""
4}
修改后结果为:
1{
2 "_index": "test_index",
3 "_type": "test_type",
4 "_id": "1",
5 "_version": 6,
6 "found": true,
7 "_source": {
8 "no": 1,
9 "name": "奔驰X100",
10 "addr": "德国",
11 "price": 1000000,
12 "tags": "red"
13 }
14}
3.2.2 删除字段
1POST test_index/test_type/1/_update
2{
3 "script":"ctx._source.remove(\"new_field\")"
4}
3.2.3 更新字段-添加
1POST test_index/test_type/1/_update
2{
3 "script" : {
4 "source": "ctx._source.tags.add(params.tag)",
5 "lang": "painless",
6 "params" : {
7 "tag" : "blue"
8 }
9 }
10}
更新后结果如下:
1{
2 "_index": "test_index",
3 "_type": "test_type",
4 "_id": "1",
5 "_version": 8,
6 "found": true,
7 "_source": {
8 "no": 1,
9 "name": "奔驰X100",
10 "addr": "德国",
11 "price": 1000000,
12 "tags": [
13 "red",
14 "blue"
15 ]
16 }
17}
3.2.4 删除字段(if判定)
1POST test_index/test_type/1/_update
2{
3 "script" : {
4 "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
5 "lang": "painless",
6 "params" : {
7 "tag" : "red"
8 }
9 }
10}
3.3 bulk批量请求的注意事项
- 每一行的结尾处都必须有换行符"\n",最后一行也要有,换行符可以有效的分隔每行。
- 注意一次提交文件的大小,整个批量请求需要被加载到请求节点的内存里,所以请求越大,给其他请求可用的内存越小。
- 最佳bulk请求的大小,完全取决于服务器的硬件、文档的大小和复杂度以及索引和搜索的负载。
3.4 并发修改文档导致版本冲突的问题
以下是社区的问题,我认为更切合知识点。
线上的场景可能会对一个文档同一秒进行并发修改,导致会出现个别的VersionConflictEngineException 异常,我猜测是并发upsert请求 可能存在先获取到版本号的请求 比 后获取到版本号的请求 执行慢或者执行晚导致的,
毕竟默认es不会对文档操作加锁。但是如在不做锁机制的情况下处理这个问题呢。
解决方案(初步):
es版本控制有内部和外部两种类型。默认情况下,es使用内部版本控制。
version_type=external的时候是外部值控制。在使用外部版本类型时,
系统会检查传递给索引请求的版本号是否大于当前存储的文档的版本,
如果为true,则文档将被索引并使用新的版本号。
如果提供的值小于或等于存储文档的版本号,则会发生版本冲突,索引操作将失败。
1PUT /test_index/test_type/10?version=1520834740000&version_type=external
2{
3 "newadd":11,
4 "test":"true"
5}
返回结果:
1{
2 "_index": "test_index",
3 "_type": "test_type",
4 "_id": "10",
5 "_version": 1520834740000,
6 "found": true,
7 "_source": {
8 "newadd": 11,
9 "test": "true"
10 }
11}
所以最简单的实现方式就是每次更新使用当前时间戳作为版本号,
3.5 动态映射和静态映射的区分
- 动态映射:文档写入ES中,它会根据字段的类型自动识别,这种称为:动态映射;
- 静态映射:写入数据之前对字段的属性进行手工设置。
3.6 text字段的特殊性
不用于排序,很少用于聚合(termsAggrions除外,未来版本会彻底禁止text类型聚合操作)。
题外话:如果需要可以借助 multi-fields.使用:keyword 类型。
官网解读:
3.7 数据类型存储建议
对于数字类型的字段,在满足需求的情况下,要尽可能的选择范围小的数字类型。
3.8 过滤和搜索的区别
- 过滤:只根据条件对文档进行过滤,不计算评分;
- 搜索:解决的是相关度的问题。
当用户输入一个查询,Elasticsearch通过排序模型计算文档和查询关键词之间的相关度,按照评分排序后返回最想关的文档给用户。
e
细化:Elasticsearch接受到关键词以后到倒排索引中进行查询,通过倒排索引中维护的倒排记录表找到关键词对应的文档集合,然后做评分、排序、高亮处理,最终返回搜索结果给用户。
注意:ES是按照查询和文档的相关度进行排序的,默认按照评分降序排序。
3.9指定搜索字段的权重
1GET _search
2{
3 "query":{
4 "multi_match": {
5 "query": "美国",
6 "fields": ["addr^5", "name"]
7 }
8 }
9}
3.10 返回字段中至少有一个非控制的文档。
1GET _search
2{
3 "query":{
4 "exists":{
5 "field":"name"
6 }
7 }
8}
3.11 固定得分检索
1GET /_search
2{
3 "query": {
4 "constant_score" : {
5 "filter" : {
6 "term" : { "addr.keyword" : "美国"}
7 },
8 "boost" : 1.2
9 }
10 }
11}
返回结果:
1{
2 "took": 1,
3 "timed_out": false,
4 "_shards": {
5 "total": 32,
6 "successful": 32,
7 "skipped": 0,
8 "failed": 0
9 },
10 "hits": {
11 "total": 3,
12 "max_score": 1.2,
13 "hits": [
14 {
15 "_index": "test_index",
16 "_type": "test_type",
17 "_id": "5",
18 "_score": 1.2,
19 "_source": {
20 "no": 5,
21 "name": "福特500",
22 "addr": "美国",
23 "price": 180000
24 }
25 },
26 {
27 "_index": "test_index",
28 "_type": "test_type",
29 "_id": "6",
30 "_score": 1.2,
31 "_source": {
32 "no": 6,
33 "name": null,
34 "addr": "美国",
35 "price": 180000
36 }
37 },
38 {
39 "_index": "test_index",
40 "_type": "test_type",
41 "_id": "3",
42 "_score": 1.2,
43 "_source": {
44 "no": 3,
45 "name": "福特300",
46 "addr": "美国",
47 "price": 300000
48 }
49 }
50 ]
51 }
52}
3.12 修改文档得分检索
借助:function Score Query 实现。
3.13 获取相似文章
1{
2 "query": {
3 "more_like_this": {
4 "fields": [
5 "title"
6 ],
7 "like": "新时代的领路人",
8 "min_term_freq": 1,
9 "max_query_terms": 12
10 }
11 },
12 "_source": "title",
13 "from": 1000,
14 "size": 5
15}
3.14 脚本检索
以下内容是6.X验证的。
5.X版本要把source改成inline。
1POST test_index/_search
2{
3 "query":{
4 "bool":{
5 "must":{
6 "script":{
7 "script":{
8 "source": "doc['price'].value > 100000",
9 "lang":"painless"
10 }
11 }
12 }
13 }
14 }
15}
3.15 多字段高亮
字段高亮已经比较熟悉,有一种场景是:当我搜索title字段的时候,我期望高亮:title、content、abstr如何做到呢?
通俗的讲:
不搜索某个字段,可以顺带高亮该字段。
1POST test_index/test_type/_search
2{
3 "query":{
4 "match_phrase":{
5 "addr":"美国"
6 }
7 },
8 "highlight": {
9 "require_field_match":false,
10 "fields":{
11 "addr":{"pre_tags":["<strong>"],
12 "post_tags":["</strong>"]
13 },
14 "name":{"pre_tags":["<strong>"],
15 "post_tags":["</strong>"]}
16 }
17 }
18}
1{
2 "took": 116,
3 "timed_out": false,
4 "_shards": {
5 "total": 5,
6 "successful": 5,
7 "skipped": 0,
8 "failed": 0
9 },
10 "hits": {
11 "total": 3,
12 "max_score": 1.1143606,
13 "hits": [
14 {
15 "_index": "test_index",
16 "_type": "test_type",
17 "_id": "6",
18 "_score": 1.1143606,
19 "_source": {
20 "no": 6,
21 "name": "大片美国",
22 "addr": "美国",
23 "price": 180000
24 },
25 "highlight": {
26 "name": [
27 "大片<strong>美</strong><strong>国</strong>"
28 ],
29 "addr": [
30 "<strong>美</strong><strong>国</strong>"
31 ]
32 }
33 },
34 {
35 "_index": "test_index",
36 "_type": "test_type",
37 "_id": "5",
38 "_score": 0.5753642,
39 "_source": {
40 "no": 5,
41 "name": "福特500",
42 "addr": "美国",
43 "price": 180000
44 },
45 "highlight": {
46 "addr": [
47 "<strong>美</strong><strong>国</strong>"
48 ]
49 }
50 },
51 {
52 "_index": "test_index",
53 "_type": "test_type",
54 "_id": "3",
55 "_score": 0.5753642,
56 "_source": {
57 "no": 3,
58 "name": "福特300",
59 "addr": "美国",
60 "price": 300000
61 },
62 "highlight": {
63 "addr": [
64 "<strong>美</strong><strong>国</strong>"
65 ]
66 }
67 }
68 ]
69 }
70}
3.16 分片影响评分
Elasitcsearch 5.4 之后对于text类型的字段,默认采用是BM25评分模型,而不是基于tf-idf的向量空间模型,评分模型的选择可以通过similarity参数在映射中指出。
需要注意的是:ES在每个分片上单独打分,分片的数量会影响打分的结果。
这个问题比较有趣的讨论如下:
https://elasticsearch.cn/question/2275
3.17 集群统计
统计集群的两个方面信息:
一:索引层面
- 分片数、存储大小、内存使用情况;
二:节点层面
- 节点数量、节点角色、操作系统、JVM版本、内存、CPU、插件信息x-pack等。
1GET /_cluster/stats
4、核心工具推荐
工欲善其事必先利其器,好的工具能提升开发效率。
4.1 Luke 工具
1、功能介绍:
查看Luncene、Solr、Elasitcsearch索引的GUI工具,方便开发和诊断。
2、核心功能点:
- 查看分析字段内容;
- 搜素索引;
- 执行索引维护;
- 从HDFS读取索引;
- 将全部或者部分索引转换为XML格式导出。
- 测试自定义的Lucene分词器。
3、工具地址:
https://github.com/DmitryKey/luke
4、最新版本
- Upgrade to 7.2.0
- http://t.cn/RnKU9dz
5、注意
Luke的版本要和Lucene一致。
4.2 Tika工具
1、简介
Apache Tika是一个用于文本检测和文件内容提取的库。
2、特点
Tika 可以检测超过1000种不同类型的文档,比如PPT、PDF、DOC、XLS,所有的文档类型可以通过一个简单的接口被解析。
3、应用
Tika广泛应用于搜素引擎、内容分析、文本翻译、数字管理等领域。
4、下载地址
http://tika.apache.org/download.htm
5、扩展
如果有全文知识库检索的项目,可以考虑使用Tika对多种不同类型的文档进行文档解析。
5、小结
此为拆解的第一本书,印证了我之前说的,核心知识点在Elasticsearch官网文档中都有更详尽的英文解读。
目前市面上没有一本书能涵盖全部的知识点。
书的目的多半是作者的一些学习、实践积累,更多的知识还得靠实践中总结、实践、再总结。
“书写是为了更好的思考”,与大家共勉!一起加油!
下一本书,紧张梳理中…..
推荐阅读:
为什么选择 Spring 作为 Java 框架?
SpringBoot RocketMQ 整合使用和监控
上篇好文:
Elasticsearch实战 | 必要的时候,还得空间换时间!

干货 |《从Lucene到Elasticsearch全文检索实战》拆解实践的更多相关文章
- 《从Lucene到Elasticsearch全文检索实战》的P184页
curl -XPOST "http://localhost:9200/_bulk?pretty" --data-binary @books.json 这句话在书中是以crul的命令 ...
- Elasticsearch全文检索实战小结
一.项目概述 这是一个被我称之为“没有枪.没有炮,硬着头皮自己造”的项目.项目是和其它公司合作的三个核心模块开发. 使用ES的目的是: 1).采集数据.网站数据清洗后存入ES: 2).对外提供精确检索 ...
- 开源搜索引擎评估:lucene sphinx elasticsearch
开源搜索引擎评估:lucene sphinx elasticsearch 开源搜索引擎程序有3大类 lucene系,java开发,包括solr和elasticsearch sphinx,c++开发,简 ...
- Elasticsearch全文检索工具入门
Elasticsearch全文检索工具入门: 1.下载对应系统版本的文件 elasticsearch-2.4.0.zip 1.1运行elasticsearch-2.4.0\elasticsearch- ...
- ElasticSearch 全文检索— ElasticSearch概述
ElasticSearch 产生背景 1.海量数据组合条件查询 2.毫秒级或者秒级返回数据 Lucene 定义 lucene是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一 ...
- 使用Lucene.Net实现全文检索
使用Lucene.Net实现全文检索 目录 一 Lucene.Net概述 二 分词 三 索引 四 搜索 五 实践中的问题 一 Lucene.Net概述 Lucene.Net是一个C#开发的开源全文索引 ...
- Lucene作为一个全文检索引擎
Lucene作为一个全文检索引擎,其具有如下突出的优点: (1)索引文件格式独立于应用平台.Lucene定义了一套以8位字节为基础的索引文件格式,使得兼容系统或者不同平台的应用能够共享建立的索引文件. ...
- 《从Lucene到Elasticsearch:全文检索实战》学习笔记五
今天我给大家讲讲tf-idf权重计算 tf-idf权重计算: tf-idf(中文词频-逆文档概率)是表示计算词项对于一个文档集或语料库中的一份文件的重要程度.词项的重要性随着它在文档中出现的次数成正比 ...
- 《从Lucene到Elasticsearch:全文检索实战》学习笔记四
今天我给大家讲讲布尔检索模型基本概念 布尔检索模型: 检索模型是判断文档内容与用户相关性的核心技术,以大规模网页搜索为例,在海量网页中与用户查询关键词相关的网页可能会有成千上万个,甚至耕读哦.那么信息 ...
随机推荐
- jsp传值
是由a1.jsp发出请求然后由a2.jsp转发给ok.jsp,由ok.jsp响应a1.jsp. 但是这个转发过程是在服务端发生的,客户端不知道所以地址是不变的 转发请求的代码: request.get ...
- select自定义下拉三角符号,css样式小细节
本来没有写文章的习惯,但是闲下来了,整理资料,发现还挺纠结,对前端来说.所以整理下,希望对看到的人有所帮助,毕竟我不是前端开发. 起因,是前端告诉我select 框的三角箭头不能自定义.但是第二次的时 ...
- WebGL2系列之多采样渲染缓冲对象
在很久很久以前,盘古开辟了天地,他的头顶着天,脚踩着地,最后他挂了.他的毛发变成了森林,他的血液变成了河流,他的肌肉变成了大地......卡! 哦,不对,在很久很久以前,你属于我,我拥有你.你还有没有 ...
- ServiceFabric极简文档-1.3删除群集
删除群集 若要删除群集,请运行包文件夹中的 RemoveServiceFabricCluster.ps1 Powershell 脚本,并传入 JSON 配置文件的路径. 可以选择性地指定删除日志的位置 ...
- 浅谈对static的理解
相信很多朋友在面试过程中都遇到过关于static的相关题目,接下来我们来分析一下static. static(静态的),用来修饰成员变量,成员方法,它随着类的加载而加载,使用static修饰的数据可以 ...
- Kafka API操作
Kafka API实战 环境准备 在eclipse中创建一个java工程 在工程的根目录创建一个lib文件夹 解压kafka安装包,将安装包libs目录下的jar包拷贝到工程的lib目录下,并buil ...
- hadoop的运行模式
概述 1)资料查询(官方网址) (1)官方网站: http://hadoop.apache.org/ (2)各个版本归档库地址 https://archive.apache.org/dist/hado ...
- Python基础之用户交互、流程控制、循环语句
python的简洁性真的令人叹为观止,没有JAVA那种输入还要导个包,不学不知道,一学吓一跳啊! 我在学这些基础的时候,会把python和java.c做对比,这样更好的理解记忆! 用户交互(输入) i ...
- 15款好用超赞的chrome插件, 开发者们的必备~
今天推荐一波Chrome插件干货.这些插件带给我开发效率上的提升.所以在这里整理一下,分享给朋友们. 作为一名程序开发者,推荐一波常用的chrome插件,用了就舍不得丢,包括免费FQ工具,github ...
- 在ts+vue中实现前端批量下载打包二维码
---恢复内容开始--- 一.插件安装 首先是插件的安装与引入,这里我们用的是qrcode的这个插件,直接使用npm install qrcodejs2安装即可,但是这里要注意,如果你用的是ts进行开 ...