目录
一、ElasticSearch介绍
1.1 为什么要用ElasticSearch?
1.2 ElasticSearch介绍
1.3 原理与应用
1.3.1.索引结构
1.3.2.倒排索引
1.4 ELK是什么?
1.5 ES特点及优势
二、安装 ElasticSearch
2.1 环境需求
2.2 安装ES
2.2.1 下载
2.2.2.设置虚拟机内存
2.2.3 创建用户
2.2.4 安装
2.2.5 ES目录结构
2.3 配置文件
2.3.1 elasticsearch.yml
2.3.2 jvm.options
2.3.3 log4j2.properties
2.4 启动ES
2.4.1.启动和关闭
2.4.2.解决内核问题 编辑
2.4.3.解决文件创建权限问题
2.4.4.解决线程开启限制问题
2.4.5.解决虚拟内存问题
2.5.测试
三、安装Kibana
3.1 什么是Kibana
3.2 下载
3.3 安装
3.4 修改配置
3.5 启动
3.6 测试
四、安装head
4.1.什么是head
4.2.安装
4.3.测试
五、ES快速入门
5.1.index管理
5.1.1.创建index
5.1.2.修改index
5.1.3.删除index
5.2.mapping管理
5.2.1 创建mapping
5.2.2.查询mapping
5.2.3.更新mapping
5.2.4.删除mapping
5.3.document管理
5.3.1.创建document
5.3.2.查询document
5.3.3.删除Document
5.4.ES读写过程
5.4.1.documnet routing(数据路由)
5.4.4.为什么primary shard数量不可变?
5.5.luke查看ES的逻辑结构
六、IK分词器
6.1.测试分词器
6.2.中文分词器
6.2.1.Lucene自带中文分词器
6.2.2.第三方中文分析器
6.3.安装IK分词器
6.4.两种分词模式
6.5.自定义词库
七、field(域,字段)详细介绍
7.1.字段的属性介绍
7.1.1.type数据类型:
7.1.2.analyzer指定分词模式 :
7.1.3.index:
7.1.4.source:
7.2.常用field类型
7.3.field属性的设置标准
八、Spring Boot整合ElasticSearch
8.1.ES客户端
8.2.搭建工程
8.2.1.pom.xml
8.2.2.application.yml
8.2.3.app
8.3.索引管理
8.3.1.创建索引库
8.3.2.删除索引库
8.3.2.添加文档
8.3.3.批量添加文档
8.3.4.修改文档
8.3.5.删除文档
8.4.文档搜索
8.4.1.准备环境
8.4.2.简单搜索
8.4.3.DSL搜索
8.4.3.1.match_all查询
8.3.3.2.分页查询
8.3.3.4.match查询
8.3.3.5.multi_match查询
8.3.3.6.bool查询
8.3.3.7.filter查询
8.3.3.8.highlight查询
九、集群管理
9.2.创建结点2
9.3.查看集群健康状态
9.4.测试
当我们访问购物网站的时候,我们可以根据我们随意所想的内容输入关键字就可以查询出相关的内容,这是怎么做到呢?这些随意的数据不可能是根据数据库的字段查询的,那是怎么查询出来的呢,为什么千奇百怪的关键字都可以查询出来呢?
答案就是全文检索服务,ElasticSearch是一个基于Lucene的全文检索服务器,而lucene采用了词元匹配方案。举个例子:北京天安门----Lucene切分词:北京 天安门 等等这些词元,当我们搜索的时候这些词元都可以检索到北京天安门。
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个基于RESTful web接口的分布式全文搜索引擎。ElasticSearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。ElasticSearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。根据DB-Engines的排名显示,ElasticSearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基Lucene)。
总结:
1、elasticsearch是一个基于Lucene的分布式全文检索服务器。
2、elasticsearch隐藏了Lucene的复杂性,对外提供Restful 接口来操作索引、搜索。
es和solr选择哪个?
1.如果你公司现在用的solr可以满足需求就不要换了。
2.如果你公司准备进行全文检索项目的开发,建议优先考虑elasticsearch,因为像Github这样大规模的搜索都在用它。
下图是ElasticSearch的索引结构,右边黑蓝色色部分是原始文档,左边黄色部分是逻辑结构,逻辑结构也是为了更好的去描述ElasticSearch的工作原理及去使用物理结构中的索引文件。
倒排索引(Inverted index):也常被称为反向索引,倒排索引是从关键字到文档的映射(已知关键字求文档)。
逻辑结构部分是一个倒排索引表,由三部分组成:
1、将搜索的文档最终以Document方式存储起来。
2、将要搜索的文档内容分词,所有不重复的词组成分词列表。
3、每个分词和docment都有关联。
如下:
现在,如果我们想搜索 包含quick brown词条的文档:
两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单 相似性算法 ,那么,我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。
ELK=elasticsearch+Logstash+kibana
ELK架构为数据分布式存储、可视化查询和日志解析创建了一个功能强大的管理链。 三者相互配合,取长补短,共同完成分布式大数据处理工作。
1)分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到。
2)实时分析的分布式搜索引擎。
分布式:索引分拆成多个分片,每个分片可有零个或多个副本。集群中的每个数据节点都可承载一个或多个分片,并且协调和处理各种操作;
负载再平衡和路由在大多数情况下自动完成。
3)可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。也可以运行在单台PC上(已测试)
4)支持插件机制,分词插件、同步插件、Hadoop插件、可视化插件等。
1、jdk必须是jdk1.8.0_131以上版本。
2、ElasticSearch 需要至少4096 的线程池、65536个文件创建权限和 262144字节以上空间的虚拟内存才能正常启动,所以需要为虚拟机分配至少1.5G以上的内存
3、从5.0开始,ElasticSearch 安全级别提高了,不允许采用root帐号启动
4、Elasticsearch的插件要求至少centos的内核要3.5以上版本
ElasticSearch官网:免费且开放的搜索:Elasticsearch、ELK 和 Kibana 的开发者 | Elastic
从5.0开始,ElasticSearch 安全级别提高了,不允许采用root帐号启动,所以我们要添加一个用户。
1.创建elk 用户组
groupadd elk
2.创建用户admin
useradd admin
passwd admin
3.将admin用户添加到elk组
usermod -G elk admin
5.为用户分配权限
#chown将指定文件的拥有者改为指定的用户或组 -R处理指定目录以及其子目录下的所有文件
chown -R admin:elk /usr/upload
chown -R admin:elk /usr/local
切换用户:
su admin
ES是Java开发的应用,解压即安装:
tar -zxvf elasticsearch-6.2.3.tar.gz -C /usr/local
bin 目录:可执行文件包
config 目录:配置相关目录
lib 目录:ES 需要依赖的 jar 包,ES 自开发的 jar 包
logs 目录:日志文件相关目录
modules 目录:功能模块的存放目录,如aggs、reindex、geoip、xpack、eval
plugins 目录:插件目录包,三方插件或自主开发插件
data 目录:在 ES 启动后,会自动创建的目录,内部保存 ES 运行过程中需要保存的数据。
ES安装目录config中配置文件如下:
elasticsearch.yml:用于配置Elasticsearch运行参数
jvm.options:用于配置Elasticsearch JVM设置
log4j2.properties:用于配置Elasticsearch日志
本项目配置如下:
cluster.name: power_shop
node.name: power_shop_node_1
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
discovery.zen.ping.unicast.hosts: ["192.168.115.135:9300", "192.168.115.136:9300"]
path.data: /usr/local/elasticsearch-6.2.3/data
path.logs: /usr/local/elasticsearch-6.2.3/logs
http.cors.enabled: true
http.cors.allow-origin: /.*/
注意意path.data和path.logs路径配置正确。
常用的配置项如下:
cluster.name:
配置elasticsearch的集群名称,默认是elasticsearch。建议修改成一个有意义的名称。
node.name:
节点名,通常一台物理服务器就是一个节点,es会默认随机指定一个名字,建议指定一个有意义的名称,方便管理一个或多个节点组成一个cluster集群,集群是一个逻辑的概念,节点是物理概念,后边章节会详细介绍。
path.data:
设置索引数据的存储路径,默认是es_home下的data文件夹,可以设置多个存储路径,用逗号隔开。
path.logs:
设置日志文件的存储路径,默认是es_home下的logs文件夹
network.host:
设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网访问,生产环境建议设置为具体的ip。
http.port: 9200
设置对外服务的http端口,默认为9200。
transport.tcp.port: 9300
集群结点之间通信端口
discovery.zen.ping.unicast.hosts:[“host1:port”, “host2:port”, “…”]
设置集群中master节点的初始列表。
discovery.zen.ping.timeout: 3s
设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些。
http.cors.enabled:
是否支持跨域,默认为false
http.cors.allow-origin:
当设置允许跨域,默认为*,表示支持所有域名
设置最小及最大的JVM堆内存大小:
在jvm.options中设置 -Xms和-Xmx:
1) 两个值设置为相等
2) 将Xmx
设置为不超过物理内存的一半。
默认内存占用太多了,我们调小一些:
-Xms512m
-Xmx512m
日志文件设置,ES使用log4j,注意日志级别的配置。
1、启动
./elasticsearch
#或
./elasticsearch -d
2、关闭
ps-ef|grep elasticsearch
kill -9 pid
我们使用的是centos6,其linux内核版本为2.6。而Elasticsearch的插件要求至少3.5以上版本。不过没关系,我们禁用这个插件即可。
修改elasticsearch.yml文件,在最下面添加如下配置:
bootstrap.system_call_filter: false
[1]: max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
Linux 默认来说,一般限制应用最多创建的文件是 4096个。但是 ES 至少需要 65536 的文件创建权限。我们用的是admin用户,而不是root,所以文件权限不足。
使用root用户修改配置文件:
vim /etc/security/limits.conf
追加下面的内容:
* soft nofile 65536
* hard nofile 65536
[2]: max number of threads [3795] for user [es] is too low, increase to at least [4096]
默认的 Linux 限制 root 用户开启的进程可以开启任意数量的线程,其他用户开启的进程可以开启1024 个线程。必须修改限制数为4096+。因为 ES 至少需要 4096 的线程池预备。
如果虚拟机的内存是 1G,最多只能开启 3000+个线程数。至少为虚拟机分配 1.5G 以上的内存。
使用root用户修改配置:
vim /etc/security/limits.conf
追加:
* hard nproc 4096
[3]: max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]
ES 需要开辟一个 262144字节以上空间的虚拟内存。Linux 默认不允许任何用户和应用直接开辟虚拟内存。
vim /etc/sysctl.conf
追加下面内容:
vm.max_map_count=655360 #限制一个进程可以拥有的VMA(虚拟内存区域)的数量
然后执行命令,让sysctl.conf配置生效:
sysctl -p
ES 中只要启动了任意一个 ES 应用就是启动了一个 ES的 cluster 集群。默认的 ES集群命名为 elasticsearch。如果启动了多个应用(可以在多个节点或单一节点上启动多个应用),默认的ES 会自动找集群做加入集群的过程。
浏览器访问:http://192.168.204.132:9200
返回结果如下:
{
"name" : "power_shop_node_1", # node name 结点名称。随机分配的结点名称
"cluster_name" : "power_shop", # cluster name 集群名称。 默认的集群名称
"cluster_uuid" : "RqHaIiYjSoOyrTGq3ggCOA", # 集群唯一 ID
"version" : {
"number" : "6.2.3", #版本号
"build_hash" : "c59ff00",
"build_date" : "2018-03-13T10:06:29.741383Z",#发布日期
"build_snapshot" : false,#是否快照版本
"lucene_version" : "7.2.1",#lucene版本号
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
Kibana是ES提供的一个基于Node.js的管理控制台, 可以很容易实现高级的数据分析和可视化,以图标的形式展现出来。
kibana可以用来编辑请求语句的,方便学习操作es的语法。有时在进行编写程序,写到查询语句时,往往我会使用kibana进行书写,然后再粘贴到程序中。(不容易出错)
ElasticSearch官网:免费且开放的搜索:Elasticsearch、ELK 和 Kibana 的开发者 | Elastic
在window中安装Kibana很方便,解压即安装
修改config/kibana.yml配置:
server.port: 5601
server.host: "0.0.0.0" #允许来自远程用户的连接
elasticsearch.url: http://192.168.116.135:9200 #Elasticsearch实例的URL
./bin/kibana
浏览器访问:http://127.0.0.1:5601
head插件是ES的一个可视化管理插件,用来监视ES的状态,并通过head客户端和ES服务进行交互,比如创建映射、创建索引等。从ES6.0开始,head插件支持使得node.js运行。
1、下载head
下载地址:https://github.com/mobz/elasticsearch-head
2、运行
npm run start
http://127.0.0.1:9100浏览器访问: http://127.0.0.1:9100
ES作为一个索引及搜索服务,对外提供丰富的REST接口,快速入门部分的实例使用kibana来测试,目的是对ES的使用方法及流程有个初步的认识。
索引库。包含若干相似结构的 Document 数据,相当于数据库的database。
语法:PUT /index_name
如:
PUT /java2202
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
}
}
number_of_shards - 表示一个索引库将拆分成多片分别存储不同的结点,提高了ES的处理能力
number_of_replicas - 是为每个 primary shard分配的replica shard数,提高了ES的可用性,如果只有一台机器,设置为0
效果:
注意:索引一旦创建,primary shard 数量不可变化,可以改变replica shard 数量。
语法:PUT /index_name/_settings
如:
PUT /java06/_settings
{
"number_of_replicas" : 1
}
ES 中对 shard 的分布是有要求的,有其内置的特殊算法:
Replica shard 会保证不和他的那个 primary shard 分配在同一个节点上;如过只有一个节点,则此案例执行后索引的状态一定是yellow。
DELETE /java06[, other_index]
映射,创建映射就是向索引库中创建field(类型、是否索引、是否存储等特性)的过程,下边是document和field与关系数据库的概念的类比:
elasticsearch | 关系数据库 |
---|---|
index(索引库) | database(数据库) |
type(类型) | table(表) |
document(文档) | row(记录) |
field(域) | column(字段) |
注意:6.0之前的版本有type(类型)概念,type相当于关系数据库的表,ES6.x 版本之后,type概念被弱化ES官方将在ES7.0版本中彻底删除type。
语法:POST /index_name/type_name/_mapping
如:
POST /java06/course/_mapping { "properties": { "name": { "type": "text" }, "description": { "type": "text" }, "studymodel": { "type": "keyword" } } }
效果:
查询所有索引的映射:
GET /java06/course/_mapping
映射创建成功可以添加新字段,已有字段不允许更新。
通过删除索引来删除映射。
ES中的文档相当于MySQL数据库表中的记录。
5.3.1.1.POST语法
此操作为 ES 自动生成 id 的新增 Document 方式。
语法:POST /index_name/type_name/id
如:
POST /java06/course/1 { "name":"python从入门到放弃", "description":"人生苦短,我用Python", "studymodel":"201002" } POST /java06/course { "name":".net从入门到放弃", "description":".net程序员谁都不服", "studymodel":"201003" }
5.3.1.2.PUT语法
此操作为手工指定 id 的 Document 新增方式。
语法:PUT/index_name/type_name/id{field_name:field_value}
如:
PUT /java06/course/2 { "name":"php从入门到放弃", "description":"php是世界上最好的语言", "studymodel":"201001" }
结果:
{ "_index": "test_index", 新增的 document 在什么 index 中, "_type": "my_type", 新增的 document 在 index 中的哪一个 type 中。 "_id": "1", 指定的 id 是多少 "_version": 1, document 的版本是多少,版本从 1 开始递增,每次写操作都会+1 "result": "created", 本次操作的结果,created 创建,updated 修改,deleted 删除 "_shards": { 分片信息 "total": 2, 分片数量只提示 primary shard "successful": 1, 数据 document 一定只存放在 index 中的某一个 primary shard 中 "failed": 0 }, "_seq_no": 0, "_primary_term": 1 }
通过head查询数据:
语法:
GET /index_name/type_name/id
或
GET /index_name/type_name/_search?q=field_name:field_value
如:根据课程id查询文档
GET /java06/course/1
如:查询所有记录
GET /java06/course/_search
如:查询名称中包括php 关键字的的记录
GET /java06/course/_search?q=name:门
结果:
{
"took": 1, # 执行的时长。单位毫秒
"timed_out": false, # 是否超时
"_shards": { # shard 相关数据
"total": 1, # 总计多少个 shard
"successful": 1, # 成功返回结果的 shard 数量
"skipped": 0,
"failed": 0
},
"hits": { # 搜索结果相关数据
"total": 3, # 总计多少数据,符合搜索条件的数据数量
"max_score": 1, # 最大相关度分数,和搜索条件的匹配度
"hits": [# 具体的搜索结果
{
"_index": "java06",# 索引名称
"_type": "course", # 类型名称
"_id": "1",# id 值
"_score": 1, # 匹配度分数,本条数据匹配度分数
"_source": { # 具体的数据内容
"name": "php从入门到放弃",
"description": "php是世界上最好的语言",
"studymodel": "201001"
}, {
"_index": "java06",
"_type": "course",
"_id": "2",
"_score": 0.13353139,
"_source": {
"name": "php从入门到放弃",
"description": "php是世界上最好的语言",
"studymodel": "201001"
}
}, {
"_index": "java06",
"_type": "course",
"_id": "6ljFCnIBp91f7uS8FkjS",
"_score": 0.13353139,
"_source": {
"name": ".net从入门到放弃",
"description": ".net程序员谁都不服",
"studymodel": "201003"
}
}
]
}
}
ES 中执行删除操作时,ES先标记Document为deleted状态,而不是直接物理删除。当ES 存储空间不足或工作空闲时,才会执行物理删除操作,标记为deleted状态的数据不会被查询搜索到(ES 中删除 index ,也是标记。后续才会执行物理删除。所有的标记动作都是为了NRT(近实时)实现)
语法:DELETE /index_name/type_name/id
如:
DELETE /java06/course/3
结果:
{
"_index": "java06",
"_type": "course",
"_id": "2",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
当客户端创建document的时候,es需要确定这个document放在该index哪个shard上,这个过程就是document routing。
路由过程:
路由算法:shard = hash(5) %number_of_primary_shards
id:document的_id,可能是手动指定,也可能是自动生成,决定一个document在哪个shard上
number_of_primary_shards:主分片數量。
原因:假如我们的集群在初始化的时候有5个primary shard,我们往里边加入一个document id=5,假如hash(5)=23,这时该document 将被加入 (shard=23%5=3)P3这个分片上。如果随后我们给es集群添加一个primary shard ,此时就有6个primary shard,当我们GET id=5 ,这条数据的时候,es会计算该请求的路由信息找到存储他的 primary shard(shard=23%6=5) ,根据计算结果定位到P5分片上。而我们的数据在P3上。所以es集群无法添加primary shard,但是可以扩展replicas shard。
拷贝elasticsearch-6.2.3/data到windows
双击luke.bat,启动luke
使用luke打开data\nodes\0\indices路径
在添加文档时会进行分词,索引中存放的就是一个一个的词(term),当你去搜索时就是拿关键字去匹配词,最终找到词关联的文档。
测试当前索引库使用的分词器:
POST /_analyze
{
"text":"测试分词器,后边是测试内容:spring cloud实战"
}
结果如下:
会发现分词的效果将“测试”这个词拆分成两个单字“测”和“试”,这是因为当前索引库使用的分词器对中文就是单字分词。
StandardAnalyzer:
单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”, 效果:“我”、“爱”、“中”、“国”。
CJKAnalyzer
二分法分词:按两个字进行切分。如:“我是中国人”,效果:“我是”、“是中”、“中国”“国人”。
上边两个分词器无法满足需求。
SmartChineseAnalyzer
对中文支持较好,但扩展性差,扩展词库和禁用词库等不好处理
paoding: 庖丁解牛最新版在 https://code.google.com/p/paoding/ 中最多支持Lucene 3.0,且最新提交的代码在 2008-06-03,在svn中最新也是2010年提交,已经过时,不予考虑。
IK-analyzer:最新版在https://code.google.com/p/ik-analyzer/上,支持Lucene 4.10从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开 始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词 歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。 但是也就是2012年12月后没有在更新。
使用IK分词器可以实现对中文分词的效果。
下载IK分词器:(Github地址:https://github.com/medcl/elasticsearch-analysis-ik )
1、下载zip:
2、解压,并将解压的文件拷贝到ES安装目录的plugins下的ik(重命名)目录下,重启es
3、测试分词效果:
POST /_analyze
{
"text":"中华人民共和国人民大会堂",
"analyzer":"ik_smart"
}
ik分词器有两种分词模式:ik_max_word和ik_smart模式。
1、ik_max_word
会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民大会堂、人民、共和国、大会堂、大会、会堂等词语。
2、ik_smart
会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
如果要让分词器支持一些专有词语,可以自定义词库。
iK分词器自带的main.dic的文件为扩展词典,stopword.dic为停用词典。
也可以上边的目录中新建一个my.dic文件(注意文件格式为utf-8(不要选择utf-8 BOM))
可以在其中自定义词汇:
比如定义:
配置文件中 配置my.dic,
上边章节安装了ik分词器,如何在索引和搜索时去使用ik分词器呢?如何指定field的类型?比如日期类型、数值类型等。
ES6.2核心的字段类型如下:
通过type属性指定字段的类型。
比较常用的类型:
文本:text、keyword(往索引目录写不进行分词)
数字:integer、long、float、double
通过analyzer属性指定分词模式。 ik_max_word将搜索内容进行细粒度分词,ik_smart粗粒度分词
"name": {
"type": "text",
"analyzer":"ik_max_word",#字段分词到索引目录时,使用细粒度
"search_analyzer":"ik_smart"#搜索时,用粗粒度精度更高
}
通过index属性指定是否往索引目录写。
默认为index=true,即要进行索引,只有进行索引才可以从索引库搜索到。 但是也有一些内容不需要索引,比如:商品图片地址只被用来展示图片,不进行搜索图片,此时可以将index设置 为false。
删除索引,重新创建映射,将pic的index设置为false,尝试根据pic去搜索,结果搜索不到数据
"pic": {
"type":"text",
"index":false
}
就是说当前字段是否展示出来,比如:商品描述,当你搜索商品后搜出来的东西,肯定不会有一堆的商品描述的,但是搜索的时候可以按描述搜索,这时候就用到这个了。可以想象如果不做处理,成千上万条商品搜出来的数据量是非常庞大的
includes:存储到document文档中
POST /java06/course/_mapping
{
"_source": {
"includes":["description"]
}
}
excludes:不存到document文档中,但是设置分词也可以搜索的到
POST /java06/course/_mapping
{
"_source": {
"excludes":["description"]
}
}
1)文本:text、keyword(往索引目录写不进行分词)
2)date日期类型
日期类型不用设置分词器,通常日期类型的字段用于排序。 1)format 通过format设置日期格式,多个格式使用双竖线||分隔, 每个格式都会被依次尝试, 直到找到匹配的
例如: 1、设置允许date字段存储年月日时分秒、年月日及毫秒三种格式。
POST /java06/course/_mapping
{
"properties": {
"timestamp": {
"type": "date",
"format": "yyyy-MM-dd"
}
}
}
2、插入文档:
PUT /java06/course/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"pic":"250.jpg",
"timestamp":"2018-07-04 18:28:58"
}
3)Numeric类型
es中的数字类型经过分词(特殊)后支持排序和区间搜索
例如: 1、更新已有映射:
POST /java06/course/_mapping
{
"properties": {
"price": {
"type": "float"
}
}
}
2、插入文档
PUT /java06/course/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"pic":"250.jpg",
"price":38.6
}
属性 | 标准 |
---|---|
type | 是否有意义 |
index | 是否搜索 |
source | 是否展示 |
ES提供多种不同的客户端:
1、TransportClient
ES提供的传统客户端,官方计划8.0版本删除此客户端。
2、RestClient
RestClient是官方推荐使用的,它包括两种:REST Low Level Client和 REST High Level Client。ES在6.0之后提供REST High Level Client, 两种客户端官方更推荐使用 REST High Level Client,不过当前它还处于完善中,有些功能还没有。
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.2.RELEASE
com.bjpowernode
springboot_elasticsearch
1.0-SNAPSHOT
6.2.3
org.springframework.boot
spring-boot-starter-web
org.elasticsearch.client
elasticsearch-rest-high-level-client
${elasticsearch.version}
org.springframework.boot
spring-boot-starter-test
spring:
elasticsearch:
rest:
uris:
- http://192.168.204.132:9200
package com.bjpowernode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ElasticsearchApp {
public static void main(String[] args) {
SpringApplication.run(ElasticsearchApp.class, args);
}
}
8.3.1.1.api
创建索引库:
PUT /java06
{
"settings":{
"number_of_shards" : 2,
"number_of_replicas" : 0
}
}
创建映射:
POST /java06/course/_mapping
{
"_source": {
"excludes":["description"]
},
"properties": {
"name": {
"type": "text",
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart"
},
"description": {
"type": "text",
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart"
},
"studymodel": {
"type": "keyword"
},
"price": {
"type": "float"
},
"pic":{
"type":"text",
"index":false
}
}
}
8.3.1.2.Java Client
package com.bjpowernode.test;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {ElasticsearchApp.class})
public class IndexWriterTest {
@Autowired
private RestHighLevelClient restHighLevelClient;
//创建索引库
@Test
public void testCreateIndex() throws IOException {
//创建“创建索引请求”对象,并设置索引名称
CreateIndexRequest createIndexRequest = new CreateIndexRequest("java06");
//设置索引参数
createIndexRequest.settings("{\n" +
" \"number_of_shards\" : 2,\n" +
" \"number_of_replicas\" : 0\n" +
" }", XContentType.JSON);
createIndexRequest.mapping("course", "{\r\n" +
" \"_source\": {\r\n" +
" \"excludes\":[\"description\"]\r\n" +
" }, \r\n" +
" \"properties\": {\r\n" +
" \"name\": {\r\n" +
" \"type\": \"text\",\r\n" +
" \"analyzer\":\"ik_max_word\",\r\n" +
" \"search_analyzer\":\"ik_smart\"\r\n" +
" },\r\n" +
" \"description\": {\r\n" +
" \"type\": \"text\",\r\n" +
" \"analyzer\":\"ik_max_word\",\r\n" +
" \"search_analyzer\":\"ik_smart\"\r\n" +
" },\r\n" +
" \"studymodel\": {\r\n" +
" \"type\": \"keyword\"\r\n" +
" },\r\n" +
" \"price\": {\r\n" +
" \"type\": \"float\"\r\n" +
" },\r\n" +
" }\r\n" +
"}", XContentType.JSON);
//创建索引操作客户端
IndicesClient indices = restHighLevelClient.indices();
//创建响应对象
CreateIndexResponse createIndexResponse =
indices.create(createIndexRequest);
//得到响应结果
boolean acknowledged = createIndexResponse.isAcknowledged();
System.out.println(acknowledged);
}
}
8.3.2.1.api
DELETE /java06
8.3.2.2.java client
//删除索引库
@Test
public void testDeleteIndex() throws IOException {
//创建“删除索引请求”对象
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("java06");
//创建索引操作客户端
IndicesClient indices = restHighLevelClient.indices();
//创建响应对象
DeleteIndexResponse deleteIndexResponse =
indices.delete(deleteIndexRequest);
//得到响应结果
boolean acknowledged = deleteIndexResponse.isAcknowledged();
System.out.println(acknowledged);
}
8.3.2.1.api
POST /java06/course/1
{
"name":"spring cloud实战",
"description":"本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。",
"studymodel":"201001",
"price":5.6
}
8.3.2.2.java client
//添加文档
@Test
public void testAddDocument() throws IOException {
//创建“索引请求”对象:索引当动词
IndexRequest indexRequest = new IndexRequest("java06", "course", "1");
indexRequest.source("{\n" +
" \"name\":\"spring cloud实战\",\n" +
" \"description\":\"本课程主要从四个章节进行讲解: 1.微服务架构入门 " +
"2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心nacos。\",\n" +
" \"studymodel\":\"201001\",\n" +
" \"price\":5.6\n" +
"}", XContentType.JSON);
IndexResponse indexResponse =
restHighLevelClient.index(indexRequest);
System.out.println(indexResponse.toString());
}
支持在一次API调用中,对不同的索引进行操作。支持四种类型的操作:index、create、update、delete。
语法:
POST /_bulk
{ action: { metadata }}
{ requestbody }\n
{ action: { metadata }}
{ requestbody }\n
...
8.3.3.1.api
POST /_bulk
{"index":{"_index":"java06","_type":"course"}}
{"name":"php实战","description":"php谁都不服","studymodel":"201001","price":"5.6"}
{"index":{"_index":"java06","_type":"course"}}
{"name":"net实战","description":"net从入门到放弃","studymodel":"201001","price":"7.6"}
8.3.3.2.java client
@Test
public void testBulkAddDocument() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.add(new IndexRequest("java06", "course").source("{...}",
XContentType.JSON));
bulkRequest.add(new IndexRequest("java06", "course").source("{...}",
XContentType.JSON));
BulkResponse bulkResponse =
restHighLevelClient.bulk(bulkRequest);
System.out.println(bulkResponse.hasFailures());
}
8.3.4.1.api
PUT /java06/course/1
{
"price":66.6
}
8.3.4.2.java client
//更新文档
@Test
public void testUpdateDocument() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("java06", "course", "1");
updateRequest.doc("{\n" +
" \"price\":7.6\n" +
"}", XContentType.JSON);
UpdateResponse updateResponse =
restHighLevelClient.update(updateRequest);
System.out.println(updateResponse.getResult());
}
8.3.5.1.api
DELETE /java06/coures/1
8.3.4.2.java client
//根据id删除文档
@Test
public void testDelDocument() throws IOException {
//删除请求对象
DeleteRequest deleteRequest = new DeleteRequest("java06","course","1");
//响应对象
DeleteResponse deleteResponse =
restHighLevelClient.delete(deleteRequest);
System.out.println(deleteResponse.getResult());
}
向索引库中插入以下数据:
PUT /java06/course/1
{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}PUT /java06/course/2
{
"name": "java编程基础",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}PUT /java06/course/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}
简单搜索就是通过url进行查询,以get方式请求ES。 语法:
GET /index_name/type_name/doc_id GET [/index_name/type_name/]_search[?parameter_name=parameter_value&...]
例如:
GET /java06/course/_search?q=name:spring&sort=price:desc
注意: 如果查询条件复杂,很难构建搜索条件 ,生产环境中很少使用。 例如:要求搜索条件为商品名称包含手机,价格在 1000~5000之间,销量在每月 500 以上,根据价格升序排列,分页查询第二页,每页 40 条数据:?q=xxxx:xxx&range=xxx:xxx:xxx&aggs&sort&from&size
8.4.2.1.api
GET /java06/course/1
8.4.2.2.java client
//查询文档
@Test
public void getDoc() throws IOException {
GetRequest getRequest = new GetRequest("java06","course","1");
GetResponse getResponse = restHighLevelClient.get(getRequest);
boolean exists = getResponse.isExists();
System.out.println(exists);
String source = getResponse.getSourceAsString();
System.out.println(source);
}
DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索时传入特定的json格式的数据来完成不同的搜索需求,DSL比URI搜索方式功能强大,在项目中建议使用DSL方式来完成搜索。 语法:
GET /index_name/type_name/_search
{
"commond":{
"parameter_name" : "parameter_value"
}
}
8.4.3.1.1.api
GET /java06/course/_search
{
"query" : {
"match_all" : {}
}
}
8.4.3.1.2.java client
package com.bjpowernode.test;
import com.bjpowernode.ElasticsearchApp;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.IOException;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {ElasticsearchApp.class})
public class IndexReaderTest {
@Autowired
private RestHighLevelClient restHighLevelClient;
private SearchRequest searchRequest;
private SearchResponse searchResponse;
@Before
public void init(){
searchRequest = new SearchRequest();
searchRequest.indices("java06");
searchRequest.types("course");
}
@Test
public void testMatchAll() throws IOException {
//2、创建 search请求对象
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("java06");
searchRequest.types("course");
//3、创建 参数构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//4、设置请求参数
searchRequest.source(searchSourceBuilder);
//1、调用search方法
SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
SearchHits searchHits = searchResponse.getHits();
long totalHits = searchHits.getTotalHits();
System.out.println("共搜索到"+totalHits+"条文档");
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
@After
public void show(){
SearchHits searchHits = searchResponse.getHits();
long totalHits = searchHits.getTotalHits();
System.out.println("共搜索到"+totalHits+"条文档");
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
}
8.3.3.2.1.api
GET /java06/course/_search
{
"query" : { "match_all" : {} },
"from" : 1, # 从第几条数据开始查询,从0开始计数
"size" : 3, # 查询多少数据
"sort" : [
{ "price" : "asc" }
]
}
8.3.3.2.2.java client
//分页查询
@Test
public void testSearchPage() throws Exception {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchSourceBuilder.from(1);
searchSourceBuilder.size(5);
searchSourceBuilder.sort("price", SortOrder.ASC);
// 设置搜索源
searchRequest.source(searchSourceBuilder);
// 执行搜索
searchResponse = restHighLevelClient.search(searchRequest);
}
match Query即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。
8.3.3.4.1.api
query:搜索的关键字 operator:or 表示 只要有一个词在文档中出现则就符合条件,and表示每个词都在文档中出现则才符合条件。
1、基本使用:
GET /java06/course/_search
{
"query" : {
"match" : {
"name": {
"query": "spring开发"
}
}
}
}
2、operator:
GET /java06/course/_search
{
"query" : {
"match" : {
"name": {
"query": "spring开发",
"operator": "and"
}
}
}
}
上边的搜索的执行过程是: 1、将“spring开发”分词,分为spring、开发两个词 2、再使用spring和开发两个词去匹配索引中搜索。 3、由于设置了operator为and,必须匹配两个词成功时才返回该文档。
7.3.3.4.2 java client
@Test
public void testMatchQuery() throws Exception {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("name", "spring开
发").operator(Operator.AND));
// 设置搜索源
searchRequest.source(searchSourceBuilder);
// 执行搜索
searchResponse = restHighLevelClient.search(searchRequest);
}
matchQuery是在一个field中去匹配,multiQuery是拿关键字去多个Field中匹配。
8.3.3.5.1.api
1、基本使用 例子:关键字 “开发”去匹配name 和description字段
GET /java06/course/_search
{
"query": {
"multi_match": {
"query": "开发",
"fields": ["name","description"]
}
}
}
注意:此搜索操作适合构建复杂查询条件,生产环境常用。
8.3.3.5.2.java client
@Test
public void testMultiMatchQuery() throws Exception {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("开发","name","description"));
// 设置搜索源
searchRequest.source(searchSourceBuilder);
// 执行搜索
searchResponse = restHighLevelClient.search(searchRequest);
}
布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来。 参数: must:表示必须,多个查询条件必须都满足。(通常使用must) should:表示或者,多个查询条件只要有一个满足即可。 must_not:表示非。
8.3.3.6.1.api
例如:查询name包括“开发”并且价格区间是1-100的文档
GET /java06/course/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "开发"
}
},
{
"range": {
"price": {
"gte": 50,
"lte": 100
}
}
}
]
}
}
}
7.3.3.6.2.java client
@Test
public void testBooleanMatch() throws IOException {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//json条件
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("name","开发"));
boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte("50").lte(100));
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
}
过滤查询。此操作实际上就是 query DSL 的补充语法。过滤的时候,不进行任何的匹配分数计算,相对于 query 来说,filter 相对效率较高。Query 要计算搜索匹配相关度分数。Query更加适合复杂的条件搜索。
8.3.3.7.1.api
如:使用bool查询,搜索 name中包含 "开发"的数据,且price在 10~100 之间 1、不使用 filter, name和price需要计算相关度分数:
GET /java06/course/_search
{
"query": {
"bool" : {
"must":[
{
"match": {
"name": "开发"
}
},
{
"range": {# 范围, 字段的数据必须满足某范围才有结果。
"price": {
"gte": 10, # 比较符号 lt gt lte gte
"lte": 100
}
}
}
]
}
}
}
2、使用 filter, price不需要计算相关度分数:
GET /java06/course/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "开发"
}
}
],
"filter": {# 过滤,在已有的搜索结果中进行过滤,满足条件的返回。
"range": {
"price": {
"gte": 1,
"lte": 100
}
}
}
}
}
}
8.3.3.7.2.java client
@Test
public void testFilterQuery() throws IOException {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("name","开发"));
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(10).lte(100))
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
searchResponse = restHighLevelClient.search(searchRequest);
}
高亮显示:高亮不是搜索条件,是显示逻辑,在搜索的时候,经常需要对搜索关键字实现高亮显示。
8.3.3.8.1.api
例如:
GET /java06/course/_search
{
"query": {
"match": {
"name": "开发"
}
},
"highlight": {
"pre_tags": [""],
"post_tags": [""],
"fields": {"name": {}}
}
}
8.3.3.8.2.java clent
1、查询:
@Test
public void testHighLightQuery() throws Exception {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("name", "spring"));
//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("");
highlightBuilder.postTags("");
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
searchSourceBuilder.highlighter(highlightBuilder);
searchRequest.source(searchSourceBuilder);
searchResponse = restHighLevelClient.search(searchRequest);
}
2、遍历:
@After
public void displayDoc() {
SearchHits searchHits = searchResponse.getHits();
long totalHits = searchHits.getTotalHits();
System.out.println("共搜索到" + totalHits + "条文档");
SearchHit[] hits = searchHits.getHits();
for (int i = 0; i < hits.length; i++) {
SearchHit hit = hits[i];
String id = hit.getId();
System.out.println("id:" + id);
String source = hit.getSourceAsString();
System.out.println(source);
Map highlightFields = hit.getHighlightFields();
if (highlightFields != null) {
HighlightField highlightField = highlightFields.get("name");
Text[] fragments = highlightField.getFragments();
System.out.println("高亮字段:" + fragments[0].toString());
}
}
}
ES通常以集群方式工作,这样做不仅能够提高 ES的搜索能力还可以处理大数据搜索的能力,同时也增加了系统的
容错能力及高可用。
下图是ES集群结构的示意图:
此处的设置为:每个主分片有两个副本, 如果某个节点挂了也不怕,比如节点1挂了,我们可以查询位于节点3和节点3上的副本0
添加文档过程:
(1)假设用户把请求发给了节点1
(2)系统通过余数算法得知这个’文档’应该属于主分片2,于是请求被转发到保存该主分片的节点3
(3)系统把文档保存在节点3的主分片2中,然后将请求转发至其他两个保存副本的节点。
查询文档过程:
(1) 请求被发给了节点1
(2)节点1计算出该数据属于主分片2,这时候,有三个选择,分别是位于节点1的副本2, 节点2的副本2,节点3
的主分片2, 假设节点1负载均衡,采用轮询的方式,选中了节点2,把请求转发。
(3) 节点2把数据返回给节点1, 节点1 最后返回给客户端。
1、拷贝节点elasticsearch-1
2、修改elasticsearch.yml内容如下:
node.name: power_shop_node_2
discovery.zen.ping.unicast.hosts: ["192.168.204.132:9300", "192.168.204.133:9300"]
3、删除节点2的data目录
1、查询当前集群的健康信息:
GET /_cluster/health
2、结果:
{
"cluster_name": "power_shop",
"status": "green",
"timed_out": false,
"number_of_nodes": 2,
"number_of_data_nodes": 2,
"active_primary_shards": 2,
"active_shards": 4,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 0,
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"number_of_in_flight_fetch": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 100
}
status:用三种颜色来展示健康状态
green:索引库的每个 primary shard 和 replica shard 都是 active 的
yellow:索引库的每个 primary shard 都是 active 的,但部分的 replica shard 不是 active 的,如单节点创建
备份分配
red:不是所有的 primary shard 都是 active 状态的。
1、启动两个节点 ,测试集群健康状况和分片
2、关闭节点2,测试集群状态
3、创建备份分配,关闭节点2,再测试集群状态