ElasticSearch 简介
ElasticSearch 简称 ES,是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储,检索数据;本身扩展性很好,可以扩展到上百台服务器上,处理PB级别的数据。ES也是用Java开发并使用Lucene作为其核心实现所有的索引和搜索的功能,但是他的目的是通过简单的 RestFul API来隐藏Lucene的复杂性,让全文搜索变得更简单
2016年1月,ElasticSearch 超过 Solr,成为排名第一的搜索引擎应用
ElasticSearch 和 Solr 的区别 ElasticSearch 用于 全文搜索、结构化搜索、分析
Solr 是 Apache 下的一个顶级开源项目,采用Java开发,基于 Lucene 的全文搜索服务器,Solr 提供了比 Lucene 更为丰富的查询语言,同时实现了可配置,可扩展,并对索引、搜索性能进行了优化
Solr 可以独立运行在 Jetty 等Servlet容器中,
Solr 是一个独立的企业级搜索应用服务器,实际上就是封装了 Lucene,对外提供类似于 Web-service的API接口,用户可以通过http请求,想搜索引擎服务器提交一定格式的文件,生成索引,也可以通过提出查找请求,得到结果
ElasticSearch vs Solr:
- ES 基本是开箱即用,解压就可以使用,非常简单,Solr 的使用略复杂
- Solr 利用 Zookeeper 进行分布式管理,ElasticSearch 自身带有分布式协调管理功能
- Solr 支持更多格式的数据,比如,JSON,XML,CSV ,ElasticSearch 仅支持 JSON 文件格式
- Solr 官网提供的功能更多,ElasticSearch 本身更专注核心功能,高级功能多由第三方插件提供,如图形化界面需要 kibana 友好支持
- Solr 查询快,但更新搜索引擎慢,适合用于电商扥个查询多的应用
- ES 建立搜索引擎模块,查询慢,即时性查询快,适用于facebook、新浪等实时搜索应用
- Solr 是传统搜索应用的有力解决方案,但ElasticSearch 更适用于新型的实时型搜索应用
- Solr比较成熟,有一个更大、更成熟的用户、开发贡献社区,而ElasticSearch 相对开发维护者较少,更新又太快,学习使用成本较高
- ElasticSearch 是未来的使用趋势
ElasticSearch 安装
软件安装
最低要求 JDK 1.8,maven,web 项目需要有前端环境 node.js相关
官网(比较慢):https://www.elastic.co/cn/downloads/?elektra=home&storm=hero
ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
logstash: https://mirrors.huaweicloud.com/logstash/?C=N&O=D
可视化界面:elasticsearch-head.https://github.com/mobz/elasticsearch-head
kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D
ik分词器 https://github.com/medcl/elasticsearch-analysis-ik
实际生产环境使用 Linux 版本,这里我们学习使用,Windows版本即可
window 下安装!
1、解压就可以使用了! 
2、熟悉目录!
bin 启动文件
config 配置文件
log4j2 日志配置文件
jvm.options java 虚拟机相关的配置
elasticsearch.yml elasticsearch 的配置文件! 默认 9200 端口! 跨域!
lib 相关jar包
logs 日志!
modules 功能模块
plugins 插件!
jvm.options java 虚拟机相关的配置

3、启动,访问 9200;  
4、访问测试!

安装可视化界面 es head 的插件
没有前端基础的,先去看我的 Vue ,把基本的环境安装完毕!
1、下载地址:https://github.com/mobz/elasticsearch-head/
2、启动
文档主目录下cmd 命令
npm install
npm run start

3、连接测试发现,存在跨域问题:配置 es
http://localhost:9100
 配置


http.cors.enabled: true
http.cors.allow-origin: "*"
4、重启 es 服务器,然后再次连接 
安装 Kibana
Kibana 是一个针对 Elasticsearch 的开源分析及可视化平台,用来搜索、查看交互存储在 Elasticsearch 索引中的数据。使用 Kibana,可以通过各种图表进行高级数据分析及展示。Kibana 让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard)实时显示 Elasticsearch 查询动态。设置 Kibana 非常简单。无需编码或者额外的基础架构,几分钟内就可以完成 Kibana 安装并启动 Elasticsearch 索引监测。
官网:https://www.elastic.co/cn/kibana
Kibana 版本要和 Es 一致!
下载完毕后,解压也需要一些时间!是一个标准的工程!
好处:ELK 基本上都是拆箱即用!
解压后 Kibana 已经包含了依赖,且有bin目录,进入bin目录,右键管理员运行 kibana.bat 文件,启动比较慢,
访问页面测试可以使用 Postman、curl、elasticsearch-head、这里我们就使用 kibana
以后的搜索测试 请求语句 可以写在这里,通过kibana 来向搜索引擎 elasticsearch 进行查询请求
访问测试 http://localhost:5601
kibana 默认英文,如何汉化?
我们修改 kibana 核心配置文件,在 kibana-7.6.1-windows-x86_64\config 目录下,kibana.yml 添加国际化配置,默认是英文
末尾添加即可 i18n.locale: "zh-CN"
ElasticSearch 使用详解
1.ES 核心概念
- 索引
- 字段类型(mapping)
- 文档(documents)
集群,节点,索引,类型,文档,分片,映射是什么?
elasticsearch是面向文档,关系型数据库和elasticsearch客观的对比!一切都是json
Relational DB | Elasticsearch |
---|
数据库(database) | 索引(indices) | 表(tables) | types | 行(rows) | documents | 字段(columns) | fields |
2.ik分词器
什么是IK分词器 ?
分词:
- 即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词。
- 比如“我爱狂神”会被分为"我",“爱”,“狂”,“神” ,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。
如果要使用中文,建议使用ik分词器!
IK提供了两个分词算法:
- ik_ smart和ik_ max_ word
- ik_ smart为最少切分,ik_ max_ _word为最细粒度划分!
下载
- 下载:https://github.com/medcl/elasticsearch-analysis-ik
- 版本不全到这里找:https://elasticsearch.cn/download/
下载完毕后,直接放到目录中 elasticsearch\elasticsearch-7.6.1\plugins 即可,注意将分词器的目录名改为 ik
重启 elasticsearch,在启动过程中可以看到,elasticsearch 加载了 ik 分词器
重启失败原因:
- elasticsearch 与 ik 分词器版本一定要完全对应上,只对应大版本号不够
- 如何查看 ik版本号,查看目录下 plugin-descriptor.properties 文件,

- 解压后,压缩包删除,分词器目录取名ik
- kabana 和 head 最好也关掉,一起重启
如何确认 elasticsearch 启动成功加载插件?启动 elasticsearch bin 目录中提供的工具 elasticsearch-plugin.bat
进入 elasticsearch bin 目录启动命令行 elasticsearch-plugin list 
表示加载成功
测试使用
进入 kibana 界面 Dev Tools 菜单,发起请求,选择分词算法
【ik_smart】(最少切分)测试:
GET _analyze
{
"analyzer": "ik_smart",
"text": "我是社会主义接班人"
}
【ik_max_word】(最细粒度划分)测试
GET _analyze
{
"analyzer": "ik_max_word",
"text": "我是社会主义接班人"
}
ik分词器如何增加配置
进入分词器目录 elasticsearch\elasticsearch-7.6.1\plugins\ik\config ,这个目录包含了各种字典,
我们可以创建的自己的字典,然后配置到 IKAnalyzer.cfg.xml 文件中
新建字典文件,比如,swy.dic(就在config目录下),编辑内容,比如添加
泥萌好
swy
这样,泥萌好,swy,就会默认成为一个词,被ik分词器识别
在配置文件 IKAnalyzer.cfg.xml 中,添加
<entry key="ext_dict">swy.dic</entry>
重启es,查看启动日志可以看到,我们的新的分词字典已经被加载

3.关于索引的基本操作
格式 PUT /索引名/类型名/文档id
注意,PUT 后面有空格
创建文档
- 添加数据
PUT /kuangshen/user/1
{
"name": "狂神说",
"age": 23,
"desc": "一顿操作猛如虎,一看工资2500",
"tags": ["技术宅","温暖","直男"]
}
2、获取数据 GET
GET kuangshen/user/1
3、更新数据 PUT
PUT /kuangshen/user/3
{
"name": "李四233",
"age": 30,
"desc": "逍遥自在",
"tags": ["靓仔","大餐","编程"]
}
4、POST _update , 推荐使用这种更新方式!
POST /kuangshen/_update/1
{
"doc": {
"name": "狂神说Java"
}
}
简单地搜索!
GET kuangshen/user/1
简答的条件查询,可以根据默认的映射规则,产生基本的查询!
GET kuangshen/user/_search?q=name:狂神说Java
4.复杂操作搜索 select
复杂操作查询:select(排序,分页,高亮,模糊查询,精准查询)
//查询具体的参数使用 json
//1.条件查询
GET kuangshen/user/_search
{
"query": {
"match": {
"name": "狂神"
}
}
}

//结果过滤,查询并输出的结果不需要那么多字段
GET kuangshen/user/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"_source": ["name","desc"]
}

//3.排序操作,sort 表示通过哪个字段排序
GET kuangshen/user/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
//4.分页查询,需要包含从哪里开始,以及页面大小
//数据下标从0开始,与其他数据库是一样的
GET kuangshen/user/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 0,
"size": 1
}
//5.多条件查询,使用布尔值查询
//must(相当于and),should(相当于or),must_not(不等于xxx条件)
GET kuangshen/user/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神说"
}
},{
"match": {
"age": 3
}
}
]
}
}
}
GET kuangshen/user/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "狂神说"
}
},{
"match": {
"age": 3
}
}
]
}
}
}
GET kuangshen/user/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"age": 23
}
}
]
}
}
}
过滤器 filter
//6.过滤器 filter,
//这里附加了范围 gt-大于 gte-大于等于 lt-小于 lte-小于等于!
GET kuangshen/user/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神说"
}
}
],
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 30
}
}
}
}
}
}
//7.匹配多个条件,tags,比如根据性格标签,可以计算出权重
//多个条件使用空格分开即可
GET swy/user/_search
{
"query": {
"match": {
"tags": "白 富 美"
}
}
}
8.精确查询
term查询是直接通过倒排索引指定的词条进程精确查找的
关于分词
- term,直接查询精确的
- match,会使用分词器解析,先分析文档,再通过分析的文档进行查询
关于两个类型
- text类型可以被分词解析
- keyword不会被分词解析
举个例子,创建索引
添加数据
改变id,以及属性参数,添加多条数据
当成 keyword 方式分词,是看做一个整体
standard 普通方式,普通分词处理,当然我们也可以使用中文分词
开始查询
当我们搜索 name属性(text类型)时,由于这种数据默认可以被分词器解析,所以,即使我们搜索部分单词,也可以找到完整相关数据
当我们搜素 desc 属性(keyword类型)时,整个属性值被看做一个整体,搜索单个词无法找到,只有搜索整体才有结果
结论:keyword 属性的字段不会分词器解析,设置属性的时候需要注意
//9.多个值匹配的精确查询
//先添加一些数据,
PUT testdb/_doc/3
{
"t1": "22",
"t2": "2020-04-06"
}
PUT testdb/_doc/4
{
"t1": "33",
"t2": "2020-04-07"
}
//开始查询
GET testdb/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"t1": "22"
}
},
{
"term": {
"t1": "33"
}
}
]
}
}
}
//10.高亮查询
//搜索的结果中包含搜索词条的部分,用高亮显示
//查询的时候,添加高亮属性,并设置需要高亮的字段
//搜索相关的结果,会被自动加上标签
GET swy/user/_search
{
"query": {
"match": {
"name": "swy"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
小结:
- 匹配
- 按条件匹配
- 精确匹配
- 区间范围匹配
- 匹配字段过滤
- 多条件匹配
- 高亮查询
这些 MySQL 也可以做,只不过效率比较低
尤其在海量数据的情况下,es的性能优势非常明显
SpringBoot 集成 ElasticSearch
项目准备
创建maven项目作为父项目,删除多余文件,创建子模块springboot项目,添加项目依赖

//检查springboot-starter下载的依赖,es依赖版本是否与我们安装的版本一致,如果不一致,将连接不上,一定要确保一致
//添加与本地版本相匹配的依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
项目初始化
添加配置类,注入bean
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient() {
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")));
return client;
}
}
API 索引操作
使用前确保,elasticsearch 已经启动
编写测试类,有了之前通过 kibana 的使用,我们现在开始转为使用Java API进行操作
@SpringBootTest
class EsApiApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
// 测试索引的创建
@Test
void testCreateIndex() throws IOException {
// 1.创建索引的请求
CreateIndexRequest request = new CreateIndexRequest("swy_index");
// 2.客户端执行请求,请求后获得响应
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(response);
}
// 测试索引是否存在
@Test
void testExistIndex() throws IOException {
// 1.创建索引的请求
GetIndexRequest request = new GetIndexRequest("swy_index");
// 2.客户端执行请求,请求后获得响应
boolean exist = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println("测试索引是否存在-----"+exist);
}
// 删除索引
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("swy_index");
AcknowledgedResponse delete = client.indices().delete(request,RequestOptions.DEFAULT);
System.out.println("删除索引--------"+delete.isAcknowledged());
}
}
API 文档操作
创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
private String name;
private int age;
}
编写测试类
@SpringBootTest
class EsApiApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
// 测试添加文档
@Test
void testAddDocument() throws IOException {
User user = new User("swy",18);
IndexRequest request = new IndexRequest("swy_index");
request.id("1");
// 设置超时时间
request.timeout("1s");
// 将数据放到json字符串
request.source(JSON.toJSONString(user), XContentType.JSON);
// 客户端发送请求,获取响应结果
IndexResponse response = client.index(request,RequestOptions.DEFAULT);
System.out.println("添加文档-------"+response.toString());
System.out.println("返回状态-------"+response.status());
}
// 测试文档是否存在
@Test
void testExistDocument() throws IOException {
// 测试文档的 没有index
GetRequest request= new GetRequest("swy_index","1");
// 没有indices()了
boolean exist = client.exists(request, RequestOptions.DEFAULT);
System.out.println("测试文档是否存在-----"+exist);
}
// 测试获取文档
@Test
void testGetDocument() throws IOException {
GetRequest request= new GetRequest("swy_index","1");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
System.out.println("测试获取文档-----"+response.getSourceAsString());
System.out.println("测试获取文档-----"+response);
}
// 测试修改文档
@Test
void testUpdateDocument() throws IOException {
User user = new User("托克马克", 200);
// 修改是id为1的
UpdateRequest request= new UpdateRequest("swy_index","1");
request.timeout("1s");
request.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
System.out.println("测试修改文档-----"+response);
System.out.println("测试修改文档-----"+response.status());
}
// 测试删除文档
@Test
void testDeleteDocument() throws IOException {
DeleteRequest request= new DeleteRequest("swy_index","1");
request.timeout("1s");
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
System.out.println("测试删除文档------"+response.status());
}
//测试批量添加文档
@Test
void testBulkAddDocument() throws IOException {
ArrayList<User> userlist = new ArrayList<User>();
userlist.add(new User("swy1",5));
userlist.add(new User("swy2",6));
userlist.add(new User("swy3",40));
userlist.add(new User("swy4",25));
userlist.add(new User("swy5",15));
userlist.add(new User("swy6",35));
// 批量操作的Request
BulkRequest request = new BulkRequest();
request.timeout("1s");
// 批量处理请求
for (int i = 0; i < userlist.size(); i++) {
request.add(
new IndexRequest("swy_index")
.id(""+(i+1))
.source(JSON.toJSONString(userlist.get(i)),XContentType.JSON)
);
}
BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
// response.hasFailures()是否是失败的
System.out.println("测试批量添加文档-----"+response.hasFailures());
}
//查询操作
//SearchRequest 搜索请求
//SearchSourceBuilder 请求条件构造,
//highlighter 设置高亮
//TermQueryBuilder 精确查询
//MatchAllQueryBuilder 匹配全部查询
//xxxQueryBuilder 对应了我们刚才看到的所有命令
// 测试查询文档
@Test
void testSearchDocument() throws IOException {
SearchRequest request = new SearchRequest("swy_index");
// 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 设置了高亮
sourceBuilder.highlighter();
// term name为swy1的
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "swy1");
// 匹配所有
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
request.source(sourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 查询结果都封装在hit中
System.out.println("测试查询文档-----" + JSON.toJSONString(response.getHits()));
System.out.println("=====================");
for (SearchHit documentFields : response.getHits().getHits()) {
System.out.println("测试查询文档--遍历参数--" + documentFields.getSourceAsMap());
}
}
}
|