万字长文,学会ElasticSearch,这一篇就够了!
ElasticSearch:介绍
先放官方中文网站镇楼:
https://www.elastic.co/cn/
按照本文的流程去学习,保证一定有收获。如果暂时没时间学,不妨点个收藏点个赞,找个时间慢慢学。那么我们接下来就开始ES的旅程吧!
首先,ElasticSearch是什么?
ElasticSearch是基于RESTful web接口的全文搜索引擎 ,简单说就是可以对存储的数据进行快速搜索的强大引擎。
MySQL同样可以存储数据,为什么一定要用ES?
mysql存储用作持久化存储,而ES的存储用作检索分析。
ElasticSearch中的各种名词
1. index索引
动词:相当于mysql的insert
名词:相当于mysql的db2. Type类型
在index中,可以定义一个或多个类型
类似于mysql的table,每一种类型的数据放在一起3. Document文档
保存在某个index下,某种type的一个数据document,文档是json格式的,
document就像是mysql中的某个table里面的内容。每一行对应的列叫属性其中index库 > type表 > document文档
为什么ES搜索快?
因为ES使用了倒排索引。
什么是倒排索引?
举个例子:ES对存入数据进行分词存储,比如红海行动,存储为红海、行动。探索红海行动存储为探索、红海、行动。用户搜索红海,则对红海进行索引搜索,搜索出红海行动,探索红海行动。
ElasticSearch:安装
基本介绍完毕,接下来我们来学学如何安装ES和Kibana,ES和kibana的关系可以看成是好基友的关系,kibana可以更方便地操作ES。
这里使用的是docker进行安装,方便,快捷,好使。
使用docker进行安装:
1、下载ElasticSearch(存储检索)和Kibana(可视化检索),两个版本要一致
docker pull elasticsearch:7.4.2
docker pull kibana:7.4.2
2、配置
#docker里的目录挂载到linux的/mydata目录中
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data#es可以被远程任何机器访问
echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml#更改权限
chmod -R 777 /mydata/elasticsearch/
3、启动ElasticSearch
#9200是用户交互端口 9300是集群心跳端口
#-e指定是单阶段运行
#-e指定占用的内存大小,生产时可以设置32G
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2 #设置开机启动elasticsearch
docker update elasticsearch --restart=always
访问:
http://xxxx:9200/
4、启动kibana
#kibana指定了了ES交互端口9200 # 5600位kibana主页端口
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://47.97.26.154:9200 -p 5601:5601 -d kibana:7.4.2#设置开机启动kibana
docker update kibana --restart=always#查看kibana运行日志
docker logs 实例ID
访问5601,出现以下则表示成功:
ElasticSearch:入门使用
安装完成了,那我们来试试ES好不好使。
我们对ES的所有操作,都被封装成了RestAPI,所以我们只要发送请求就行了。
入门:
1、_cat
功能:查看ES的基本信息
使用postman或者浏览器加上地址查询,或者直接Kibana操作。
GET _cat/nodes:查看所有结点
GET _cat/health:查看es健康状况
GET _cat/master:查看主节点
GET _cat/indices:查看所有索引[类似于show databases]
2、索引一个文档(保存)
用mysql的话来说,就是保存一条记录
索引到哪个类型下 == 保存到哪张表中
如:
PUT http://xxxx:9200/customer/external/1
JSON:{"name":"john" }
结果:
{"_index": "customer","_type": "external","_id": "1","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 0,"_primary_term": 1
}
我们分析下都返回了些什么:
返回结果中,所有带有_的都被称为元数据,用来携带一些基本信息
"_index":"customer":数据存在索引customer下[mysql即哪个数据库下]
"_type": "external":数据存在类型external下[mysql即哪张表下]
"_id": "1":数据ID
"_version": 1:数据版本
"result": "created":执行结果[若执行两遍PUT,则此处变为update]
细节:
保存操作可以使用PUT或者POST,但是经过多次操作可以发现:
使用PUT进行保存数据时,携带ID会创建或者修改数据,不携带ID则报错。
使用POST保存数据时,携带ID会创建或者修改数据,不携带ID则会自动生成一个唯一ID,因此对于保存操作。
虽然PUT和POST请求都可,但是我们一般还是使用POST,可以减少容错,显得更专业。
3、查询文档
拿刚刚的操作举个栗子:
GET http://xxxx:9200/customer/external/1
语句解读:不带任何参数,使用GET对customer索引下的external类型下的id为1的数据进行查询
结果:
{"_index": "customer","_type": "external","_id": "1","_version": 2,"_seq_no": 2,"_primary_term": 1,"found": true,"_source": {"name": "john"}
}
结果分析:
"_index": "customer",索引
"_type": "external",类型
"_id": "1",查询ID
"_version": 2,版本[被更新过几次,下标从1开始]
"_seq_no": 2,乐观锁[稍后说]
"_primary_term": 1,分片,用作乐观锁
"found": true,是否查询成功
"_source":数据查询的信息
我们模拟以下乐观锁操作,可以更有助于理解:
情境:A和B同时需要对同一条数据进行修改,于是两人各自开始操作:
A先查出数据如下:
{"_index": "customer","_type": "external","_id": "1","_version": 2,"_seq_no": 2,"_primary_term": 1,"found": true,"_source": {"name": "john"}
}
A开始修改数据,B来到工位,一查,seq_no也为2,刚准备开始修改数据,突然接到个电话,于是暂停手上工作。
A修改如下:
PUT http://XXXX:9200/customer/external/1?if_seq_no=2&if_primary_term=1
{"name":"yellow"
}
解读:加上了判断条件进行修改
修改结果:
{"_index": "customer","_type": "external","_id": "1","_version": 3,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 3,"_primary_term": 1
}
B打完电话,继续工作,由于原先已经查询了,只是还未来得及提交,于是B省得麻烦,直接提交。
PUT http://XXXX:9200/customer/external/1?if_seq_no=2&if_primary_term=1
{"name":"red"
}
结果:
{"error": {"root_cause": [{"type": "version_conflict_engine_exception","reason": "[1]: version conflict, required seqNo [2], primary term [1]. current document has seqNo [3] and primary term [1]","index_uuid": "kIKj5ZSlT32aH-ziXyQN2Q","shard": "0","index": "customer"}],"type": "version_conflict_engine_exception","reason": "[1]: version conflict, required seqNo [2], primary term [1]. current document has seqNo [3] and primary term [1]","index_uuid": "kIKj5ZSlT32aH-ziXyQN2Q","shard": "0","index": "customer"},"status": 409
}
于是B只好重新先查询,再进行修改。
这就是乐观锁修改。
4、更新文档
POST customer/external/1/_update
{"doc":{"name":"blue" }
}
或者
POST/PUT customer/external/1
{"name":"blue"
}
各更新操作分析:
第一种更新:
我们继续使用上面的数据,首先对1号数据进行第一种更新操作:
POST customer/external/1/_update
{"doc":{"name":"john"}
}
第一次结果:
"_version": 5,
"result": "updated"
第二次结果:
"_version": 5,
"result": "noop"
分析:使用这种更新操作进行重复更新时,若数据与原先一致,则不进行任何操作。
第二种更新:
POST customer/external/1
{"name":"blue"
}
第一次结果:
"_version": 6,
"result": "updated"
第二次结果:
"_version": 7,
"result": "updated"
分析:
使用第二种更新操作时[即不带update],无论是否重复更新,数据是否一致,都会进行更新,version版本相应增加。因此若使用PUT方式,结果也一致。
5、删除文档&索引
DELETE http://XXXX:9200/customer/external/1
手动[啪的一下],数据没了:
{"_index": "customer","_type": "external","_id": "1","_version": 9,"result": "deleted","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 10,"_primary_term": 1
}
我们再试试能不能查到:
GET http://XXXX:9200/customer/external/1
结果:
{"_index": "customer","_type": "external","_id": "1","found": false
}
数据可以删除,那类型和索引是否也可以删除,答案是能删,只能删一点点,ES只提供了直接删除索引的操作,并没有提供直接删除类型的操作,但是我们可以将类型下的所有文档清空,也相当于删除了整个类型。
6、bulk批量API
bulk是ES中可以给ES批量导入数据地语法。
如:
POST customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"John"}
{"index":{"id":"2"}}
{"name":"Jane"}
解析:
我们对customer索引下的external类型进行bulk的POST操作,批量index[保存]id为1的name为John和id为2的name为Jane的两条数据,可以看作每两行为一次操作,同样可以delete,get。
我们在postman中进行操作时发现,JSON数据报错,那这会kibana就派上用场了,我们进入kibana,找到Dev Tools:
在kibana中发送请求:
POST /customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"John"}
{"index":{"id":"2"}}
{"name":"Jane"}
结果成功。
接下来我们继续测试批量数据操作,官方为我们准备了一份样本数据:
https://www.elastic.co/guide/cn/kibana/current/tutorial-load-dataset.html
下载莎士比亚,将其中部分使用bulk批量导入
命令:
POST shakespeare/_bulk
{"index":{"_index":"shakespeare","_id":0}}
{"type":"act","line_id":1,"play_name":"Henry IV", "speech_number":"","line_number":"","speaker":"","text_entry":"ACT I"}
.....
结果若无误,OK,我们继续下一步操作。
ElasticSearch:进阶
进阶检索:
ES支持两种基本方式检索:
uri+检索参数检索
通过使用REST request URI 发送搜索参数(uri+检索参数)如:
GET shakespeare/_search?q=*
uri+请求体检索
通过使用REST request body进行发送(uri+请求体)如:
GET shakespeare/_search
{"query": {"match_all": {}},"sort": []
}
分析:
在以后我们可能更多采用第二种方式,因为可操作性更强,处理复杂请求时更得心应手
"query":"查询条件"
"match_all":"匹配所有"
"sort":"排序条件"
我们一个个试试:
uri+检索参数检索
即第一种请求参数方式检索
GET shakespeare/_search?q=*&sort=account_number:asc
分析:
q=* # 查询所有
sort # 排序字段
asc #升序
检索shakespeare下所有信息,包括type和docs
GET shakespeare/_search
查询结果:
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 500,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "shakespeare","_type" : "account","_id" : "0","_score" : 1.0,"_source" : {"type" : "act","line_id" : 1,"play_name" : "Henry IV","speech_number" : "","line_number" : "","speaker" : "","text_entry" : "ACT I"}},.... ]}
}
结果分析:
"took" – 花费多少ms搜索
"timed_out" – 是否超时
"_shards" – 多少分片被搜索了,以及多少成功/失败的搜索分片
"max_score" –文档相关性最高得分
"hits.total.value" - 多少匹配文档被找到
"hits.sort" - 结果的排序key(列),没有的话按照score排序
"hits._score" - 相关得分 (not applicable when using match_all)
uri+请求体检索
即第二种请求体方式检索
GET shakespeare/_search
{"query": {"match_all": {}},"sort": []
}
结果与上相同。
我们再进行更多测试,比如我们想只显示部分查询数据:
GET shakespeare/_search
{"query": {"match_all": {}},"sort": [],"from": 0,"size": 1
}
解析:查出的数据从"from"开始,长度为"size"进行显示,即显示从0开始的1条数据
结果:
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 500,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "shakespeare","_type" : "account","_id" : "0","_score" : 1.0,"_source" : {"type" : "act","line_id" : 1,"play_name" : "Henry IV","speech_number" : "","line_number" : "","speaker" : "","text_entry" : "ACT I"}}]}
}
继续测试,我们如果只想返回查询到的数据的某一个部分,那么可以用以下操作[上条件不变]:
GET shakespeare/_search
{"query": {"match_all": {}},"sort": [],"from": 0,"size": 1,"_source": ["line_id","line_number"]
}
解析:"_source"作用是对返回数据进行显示,默认是全部显示
结果:
"_source" : {"line_number" : "","line_id" : 1
}
可以看到只返回了我们指定的数据。
match全文检索
先来个简单的match检索[match的属性必须是_source中存在的]:
GET shakespeare/_search
{"query": {"match": {"line_id": "2"}}
}
结果:
"_source" : {"type" : "scene","line_id" : 2,"play_name" : "Henry IV","speech_number" : "","line_number" : "","speaker" : "","text_entry" : "SCENE I. London. The palace."
}
对于属性的match,不仅可以全属性值match,还可以部分属性值match,功能强大,原因我们之后再说。继续举个栗子,我们对某属性的部分值进行检索:
GET shakespeare/_search
{"query": {"match": {"text_entry": "London"}}
}
结果[方便起见,只放查询部分结果]:
"text_entry" : "SCENE I. London. The palace.""text_entry" : "SCENE III. London. The palace.""text_entry" : "SCENE II. London. An apartment of the Princes.""text_entry" : "riding to London with fat purses: I have vizards"
再再再举个栗子,使用match分词进行全文匹配:
GET shakespeare/_search
{"query": {"match": {"text_entry": "be of"}}
}
match条件是text_entry属性中存在be of的结果,无论be of是否在一起,是否相连,是否都存在,只要存在其中一个,就被match。
结果:
"text_entry" : "Of murderous subornation, shall it be,""text_entry" : "Than out of anger can be uttered.""text_entry" : "thieves of the days beauty: let us be Dianas""text_entry" : "The scourge of greatness to be used on it;""text_entry" : "Of my young Harry. O that it could be proved"....
为何ES能做到这么强大?原因是match进行了分词全文匹配,只要包含了匹配值的都会被查询出来,而ES底层维护了一张倒排索引表,以支持全文匹配和最大得分[匹配相似评比分],且查询结果会以最大得分排序。
match_phrase[短语匹配]
短语匹配不同于match的分词匹配,它必须组合在一起才能被match,举个栗子,使用match_phrase进行匹配查询:
GET shakespeare/_search
{"query": {"match_phrase": {"text_entry": "be of"}}
}
结果:
"hits" : [ ]
还是同一时间,同一地点,结果却不同,这就是match_phrase与match的区别
multi_match[多字段匹配]
可以在多个字段中进行match匹配。
举个栗子,我们同时match匹配两个字段:
GET shakespeare/_search
{"query": {"multi_match": {"query": 1,"fields": ["line_number","speech_number"]}}
}
解析:
"query":查询匹配内容
"fields":被查询的属性字段
结果:
"speech_number" : 1,"line_number" : "1.1.1","speech_number" : 1,"line_number" : "1.1.2",...
如果查询的内容是多词,同样满足match分词匹配条件,也会进行分词查询,最后得出最大分数进行排序。
bool[复合查询]
当我们要构造更复杂的查询时可以使用bool复合查询。其查询规格必须全部被满足。
我们举个栗子,使用bool进行复合查询:
GET shakespeare/_search
{"query": {"bool": {"must": [{"match": {"play_name": "Henry"}},{"match": {"speaker": "HENRY"}}],"must_not": [{"match": {"text_entry": "of"}}],"should": [{"match": {"play_name": "Henry"}}]}}
}
分析:
"bool":表示使用bool查询
"must":必须执行
"must_not":必须不被执行
"should":如果满足其中条件,可以加分[即增加最大分数]
结果:
"play_name" : "Henry IV","speaker" : "PRINCE HENRY","text_entry" : "and unbuttoning thee after supper and sleeping upon"....
结果过滤
filter可以理解为bool查询中的must_not,作为过滤条件存在。
举个栗子,查询属性text_entry中values中存在and after且line_id在 100 - 120之间的数据
GET shakespeare/_search
{"query": {"bool": {"must": [{"match": {"text_entry": "and after"}}],"filter": {"range": {"line_id": {"gte": "100","lte": "120"}}}}}
}
分析:
"filter":过滤条件
"range":区间
结果:
"_score" : 4.313828
"_score" : 1.2873212
"_score" : 1.2208143
可以发现,其相关性得分各不相同。
但是filter在使用过程中,并不会计算相关性得分, 文档是否符合每个“must”或“should”子句中的标准,决定了文档的“相关性得分”。 得分越高,文档越符合搜索条件。
而must_not会影响文档是否包含在结果中, 但不影响文档的评分方式。
term查询
term查询用于查询非文本字段的值
首先我们先进行一段查询,match的keyword查询与match_phrase查询的区别:
GET shakespeare/_search
{"query": {"match": {"text_entry.keyword": "Breathless and faint, leaning upon my sword,"}}
}
GET shakespeare/_search
{"query": {"match_phrase": {"text_entry": "Breathless and faint, leaning upon my sword,"}}
}
结果[相同]:
"_source" : {"type" : "line","line_id" : 361,"play_name" : "Henry IV","speech_number" : 6,"line_number" : "1.3.33","speaker" : "HOTSPUR","text_entry" : "Breathless and faint, leaning upon my sword,"
}
如果将keyword删去最后的逗号,再执行,发现没有结果,而match_phrase结果不变,于是我们可以发现,keyword的整句搜索就是该字段的全部值;而match_phrase则是短语匹配,只要该字段的全部值中包含搜索条件,则匹配成功。
这个作为一个小复习。
回到term,全文检索字段使用match,其它非text字段匹配用term。这是一个规范。
GET shakespeare/_search
{"query": {"term": {"line_id":361}}
}
聚合分析
通常与查询一起使用,查询检索聚合一步到位,十分强大,类似mysql中的groupby等操作,因此也十分重要。
举个栗子,查询大小为10的地址中包含Mill的人的年龄区间分布,并查出其年龄平均值、收入平均值,并且不想看结果:
#分别为包含mill、,平均年龄、
GET bank/_search
{"query": { # 查询出包含mill的"match": {"address": "Mill"}},"aggs": { #基于查询聚合"ageAgg": { # 聚合的名字,随便起"terms": { # 看值的可能性分布"field": "age","size": 10}},"ageAvg": { "avg": { # 看age值的平均"field": "age"}},"balanceAvg": {"avg": { # 看balance的平均"field": "balance"}}},"size": 0 # 不看详情
}
如上只是单个聚合,子聚合可以在父聚合的条件下继续聚合。使用起来十分强大。
如需详细了解,可移步官网深究。
ElasticSearch:映射
映射简单说就是定义文档如何被存储和检索的。
[以后不推荐使用类型映射,会降低检索效率,一般使用索引映射]。
查看映射:
GET shakespeare/_mapping
如果我们不满意默认的映射规则,可以自定义映射规则,如:
PUT /my_index
{"mappings": {"properties": {"age":{"type": "integer"}}}
}
解析:按上面规定的映射规格,我们以后在my_index索引下存储的数据都以该映射规则存储和检索。
如果我们在创建索引映射时未创建完全,仍想继续在原索引上添加映射,那么可以这样做:
PUT /my_index/_mapping
{"mappings": {"properties": {"name":{"type": "text"}"phone":{"type":"keyword","index":false}}}
}
解析:如上的作用仅为添加映射,不能作为创建新的索引映射或者更新映射。在映射属性上添加index=false则表示不能使用phone来检索数据,在默认清空下index都为true。
更新映射
很遗憾,在ES的语法中,我们对于已经存在的映射字段,不能更新。但是可以通过创建新的索引进行数据迁移达到相同目的。
数据迁移
固定写法:
POST _reindex
{"source":{"index":"old_index[name]" },"dest":{"index":"new_index[name]" }
}
ElasticSearch:分词
分词是ES中做全文检索的核心,作用是将一个字符流分成一个个词元[tokens],然后利用各单词的相关性匹配完成检索,举个栗子:“I am a pig”,默认的标准分词器会将其分为"I",“am”,“a”,“pig”,然后再进行全文检索。
分词操作是分词器[tokenizer]完成的。默认我们使用的是标准分词器。举个栗子:
POST _analyze
{"analyzer": "standard","text": "The 2 Brown-Foxes bone."
}
分词结果:
{"tokens" : [{"token" : "the","start_offset" : 0,"end_offset" : 3,"type" : "<ALPHANUM>","position" : 0},{"token" : "2","start_offset" : 4,"end_offset" : 5,"type" : "<NUM>","position" : 1},{"token" : "brown","start_offset" : 6,"end_offset" : 11,"type" : "<ALPHANUM>","position" : 2},{"token" : "foxes","start_offset" : 12,"end_offset" : 17,"type" : "<ALPHANUM>","position" : 3},{"token" : "bone","start_offset" : 18,"end_offset" : 22,"type" : "<ALPHANUM>","position" : 4}]
}
可以看出标准分词器使用了空格作为标准之一进行了分词。
但是如果我们使用中文进行分词,会发现,每一个中文词都被分割了,而不是我们平常使用的按照词义分词,因此我们需要引入其它分词器。
一般而言,中文大都使用ik分词器:github
注意分词器要和ES版本对应。
在前面安装的elasticsearch时,我们已经将elasticsearch容器的“/usr/share/elasticsearch/plugins”目录,映射到宿主机的“ /mydata/elasticsearch/plugins”目录下,所以比较方便的做法就是下载“/elasticsearch-analysis-ik-7.4.2.zip”文件,然后解压到该文件夹下即可。安装完毕后,需要重启elasticsearch容器。
这里操作其实还是推荐使用xftp软件,就一个字,方便。
进入映射目录:cd /mydata/elasticsearch/plugins
下载:wget 下载地址
解压:unzip xxx.zip /ik
卸磨杀驴:rm -rf *.zip
重启ES:docker restart xxxx
成功后,我们试试新的分词器[带有两种中文分词器,分别是ik_smart和ik_max_work]:
POST _analyze
{"analyzer": "ik_smart","text": "我是中国人"
}
结果:
{"tokens" : [{"token" : "我","start_offset" : 0,"end_offset" : 1,"type" : "CN_CHAR","position" : 0},{"token" : "是","start_offset" : 1,"end_offset" : 2,"type" : "CN_CHAR","position" : 1},{"token" : "中国人","start_offset" : 2,"end_offset" : 5,"type" : "CN_WORD","position" : 2}]
}
OK,到这里分词功能大功告成。
不过有些时候其自带库不能满足我们的需求【流行语等等…】,所以接下来我们来试试自定扩展词库:
这里我们使用nginx来实现自定义扩展词库方便点,如果没有nginx,先使用docker安装nginx:
docker run -p 80:80 --name nginx -d nginx:1.10
然后我们需要将容器中的配置文件拷贝到mydata目录下【记得先创建一个nginx文件夹,同时使用命令时结尾的.和空格需要相同】:
docker container cp nginx:/etc/nginx .
然后把容器中的nginx停掉并rm。
我们想将该文件夹中的内容移入nginx的conf文件夹中,因此我们来修改一下,先给nginx文件夹改个名:
mv nginx conf
然后再创建一个nginx文件夹:
mkdir nginx
把conf移入nginx:
mv conf nginx/
创建一个新的nginx,并执行以下命令【挂载】:
docker run -p 80:80 --name nginx \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.10
创建成功后,在nginx的conf目录下,我们可以看到三个文件夹,进入html文件夹中,创建一个es文件夹,用来存放我们自定义的词库:
cd mydata/nginx/html
mkdir es
cd es/
vi mycode.txt
nginx方面基本完成,接下来我们来到ES配置远程词库地址:
由于ES也是挂载了文件,因此我们找到ES中配置远程词库文件对应的Linux中的文件中:
cd mydata/elasticsearch/plugins/ik/config/
vi IKAnalyzer.cfg.xml
在这里进行远程词库配置:
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://XXX/es/mycode.txt</entry>
重启ES:
docker restart XXXX
试试效果,我在自定义中配置了"沙琪玛"和"好吃点":
POST _analyze
{"analyzer": "ik_smart","text": "我爱沙琪玛"
}
结果:
{"tokens" : [{"token" : "我","start_offset" : 0,"end_offset" : 1,"type" : "CN_CHAR","position" : 0},{"token" : "爱","start_offset" : 1,"end_offset" : 2,"type" : "CN_CHAR","position" : 1},{"token" : "沙琪玛","start_offset" : 2,"end_offset" : 5,"type" : "CN_WORD","position" : 2}]
}
成功!
至此基本的ES操作就结束了,如果想要更深入的学习ES,可以去官网查阅相关资料。
祝 进大厂,无996。886,陌生人。
万字长文,学会ElasticSearch,这一篇就够了!相关推荐
- 学会JavaScript这一篇就够了~
文章目录 [One] JavaScript语言基础 一.数据类型 1.数值型 2.字符串型 3.布尔型 二.常量和变量 1.常量 2.变量--例子:输出球员的信息 三.运算符 1.算术运算符--例子: ...
- 《万字长文》-吃透Docker-基础篇
文章目录 1 Docker概述 1.1 Docker诞生背景 1.2 Docker诞生了 1.3 Docker设计理念 1.4 Docker技术理念 2 Docker安装 3 Docker架构 4 测 ...
- 《万字长文》-吃透Docker-进阶篇
文章目录 1 Docker镜像原理 1.1 联合文件系统 1.2 镜像加载原理 1.3 镜像分层结构原理剖析 1.4 从容器创建一个镜像 2 容器数据卷 2.1 使用centos测试 2.2 使用my ...
- 四万字!掌握Flink Table一篇就够了
学习工具与软件版本:开发软件IDEA.Flink1.10.2.Kafka2.0.0.Scala2.11 本章建议有一定Flink基础的伙伴学习 Apache Flink介绍.架构.原理以及实现:点击这 ...
- 简单搞懂子网划分,学会子网划分这篇就够了(例题详解)
为什么要子网划分? 1.满足不同网络对IP地址的需求 2.实现网络的层次化 3.节省IP地址 4.默认子网掩码可以进一步划分,称为可变长子网掩码"VLSM" 有类IP地址规划的缺陷 ...
- 【超全汇总】学习数据结构与算法,计算机基础知识,看这篇就够了【ZT帅地】2020-3-7
https://blog.csdn.net/m0_37907797/article/details/104029002 由于文章有点多,并且发的文章也不是一个系列一个系列发的,不过我的文章大部分都是围 ...
- 读论文七步走!CV老司机万字长文:一篇论文需要读4遍
视学算法报道 编辑:LRS [新智元导读]读论文对于AI新手和工程师来说可能是一件比较难的事.最近一位从业超5年的CV老司机发布了一篇万字长文,讲述了读论文七步法,从找论文到总结,每篇论文由浅 ...
- 转载:Vmware 虚拟化 云桌面实操:万字长文,近百张图,完整步骤带你学会Vmware 虚拟化 云桌面
Vmware 虚拟化 云桌面实操:万字长文,近百张图,完整步骤带你学会Vmware 虚拟化 云桌面 这篇文章对我搭建自己电脑的云桌面雏形很有帮助,转载一下,保存
- elasticsearch 客户端工具_万字长文:详解 Spring Boot 中操作 ElasticSearch
点击上方"小强的进阶之路",选择"星标"公众号 优质文章,及时送达 预计阅读时间: 15分钟 一.ElasticSearch 简介 1.简介 ElasticSe ...
- uiautomation遍历windows所有窗口_万字长文!滑动窗口看这篇就够了!
大家好,我是小浩.今天是小浩算法 "365刷题计划" 滑动窗口系列 - 整合篇.之前给大家讲解过一些滑动窗口的题目,但未作系统整理. 所以我就出了这个整合合集,整合工作中除了保留原 ...
最新文章
- Ubuntu连结远程github
- 什么是网络推广带你了解新上线的网站该如何提升关键词排名?
- 《Linux设备驱动开发详解 A》一一2.3 接口与总线
- PostgreSQL在何处处理 sql查询之六十六
- php实现socket编程
- OpenCV中Kinect的使用(3)
- 外网DNS系统外网访问及邮件系统外网域名访问问题
- PHP中基本符号及使用方法
- 使用try-with-resources优雅的关闭IO流
- 【重识 HTML + CSS】CSS 伪类、伪元素
- 今天加班做了昨天晚上要写的页面,用到了一些之前用过但还不熟悉需要上网搜索才能用的知识点:...
- matlab 计算结果为nan,matlab 计算 结果总是为Nan
- 提示框插件toastr
- 逐浪海棠居刻本字-第一款基于unicode13标准构建的中文字库全面发布
- 怎么批量修改图片尺寸大小?
- IcedTea6 1.7.3
- 洛谷 P5520 青原樱(组合数学插板法 or 插空法 模板)
- win7右键计算机没有注册类,win7系统提示没有注册类别如何解决
- 2018 杭州见习报告
- 万年历农历程序(抄表法)