Elasticsearch

回顾

  如果要实现一个搜索功能,用来匹配用户需要查看的内容,如何实现。我们当时学习数据库的时候,进行过大量的匹配,比如查询所有的所有姓张的人,可以这样查询SELECT * FROM person WHERE NAME LIKE '张%';这种简单的查询可以直接通过数据库的模糊查询实现,但是如果是查询这种效果呢?
例如:在京东搜索栏中中搜索罗技蓝牙鼠标会查询到这种情况!!!

不难发现,搜索栏相对于模糊查询更加灵活,可以进行关键词匹配。
通过数据库的模糊查询来实现搜索功能有下面的缺点:
1.搜索内容是一个整体,不能拆分
2.效率较低,没有使用索引


简介

  Elasticsearch,简称ES,是一款开源的高扩展的分布式全文搜索引擎,可以帮助我们存储(只负责存储需要查询显示的内容)和检索数据,还可以帮助我们实现日志统计,分析,系统监控等功能。
  上述的存储功能,我们只需将商品的展示信息存储到Elasticsearch中,例如上述从京东搜索栏的查询情况来看,基本信息有(商品id,商品图片,商品价格,商品名,商品评论数,所在店铺),将这些数据存储到Elasticsearch中之后便可以通过Elasticsearch来快速检索数据。


环境搭建

安装Elasticsearch

ES下载地址:https://www.elastic.co/cn/downloads/elasticsearch 默认下载的是最新版本
下载之后解压系统环境的文件夹中,之后进入 bin 目录,双击启动 elasticsearch.bat
最后访问:http://127.0.0.1:9200/ 会见到如下画面

到此ES下载成功

安装数据可视化界面elasticsearch head

此前端项目只能看数据不能操作数据
需要提前安装nodejs
然后下载可视化界面,是一个前端项目
github 下载: https://github.com/mobz/elasticsearch-head/
github 加速器: https://github.ur1.fun/
下载完成后解压至指定文件夹中
这里还需要修改 elasticsearch 中 config 中的 elasticsearch.yml 文件中配置

# 开启跨域
http.cors.enabled: true
# 所有人访问
http.cors.allow-origin: "*"

然后进入项目目录,下载前端项目依赖并启动项目
使用 npm install 下载依赖,使用npm run start启动项目
这里使用npm下载依赖可能无法成功,我们可以使用cnpm命令下载
最后项目运行成功是这样的界面

如果在下载以来过程中出现问题,可以使用淘宝镜像
npm config set registry https://registry.npm.taobao.org/
安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org

安装kibana组件

kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索,查看交互存储在Elasticsearch索引中的数据。kibana可以通过各种图表进行高级数据分析和展示,可以使得海量数据刚容易理解
下载地址下载地址: https://www.elastic.co/cn/downloads/kibana 默认打开是最新版本
7.6.1 下载版:https://artifacts.elastic.co/downloads/kibana/kibana-7.6.1-windows-x86_64.zip
汉化kibana:修改 config 目录下的 kibana.yml 文件 i18n.locale: “zh-CN”

双击 bin 目录下的 kibana.bat 启动,最后访问 http://127.0.0.1:5601
最后会看到下图所示:

安装ik分词器

为什么需要 IK 分词器?
在搜索时,需要对用户输入的内容进行分词。默认的分词规则对中文处理并不友好。
在kibana的 DevTools中测试
下面测试英文分词

下面测试标准分词

上述分词器无法对中文进行有效分词,我们可以下载ik分词器,使得搜索结果更好的匹配用户的输入

ik分词器下载地址
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.1/elasticsearchanalysis-ik-7.6.1.zip
解压,在elasticsearch-7.6.1\plugins目录下创建名称为ik的文件夹,将解压后的文件复制到ik目录

添加分词器之后就可以得到更适合中文分词的分析器

最少切分分

最细粒度切分,下图只展示了部分结果,总之分词比较细致

自定义ik分词(可以根据实际情况进行配置)
在 elasticsearch-7.6.1\plugins\ik\config
添加 xxx.dic 文件 定义词组, .dic 文件必须是 utf-8 编码格式,否则启动报错
在 IKAnalyzer.cfg.xml 文件添加自定义分词器文件

最后进行测试

可以明显观察到与上次切分的结果不同,就是我们在my.dic中定义了“我是”词组


ES的基本概念

Elasticsearch是面向文档存储的,可以是数据库中的一条记录。文档数据会被序列化成json格式后存储在elasticsearch中。
例如下图:

索引:同类型文档的集合,可以理解成数据库中的一张表,一张表中存储同种类型的数据
文档:一条数据就是一个文档,ES中是json格式,对应数据库表中的一行记录
字段:对应数据库中的一列
映射:索引中文档的约束,比如字段名称、类型

关系型数据库MySQL和Elasticsearch对比

MySQL擅长事务类型操作,可以保证数据的安全和一致性
Elasticsearch擅长海量数据的搜索,分析和计算。

正向索引和倒排索引
MySQL采用正向索引:基于id创建索引,查找必须先找到文档,而后判断是否包含搜索内容
正向索引的工作原理非常简单,但检索效率非常低下。
Elasticsearch采用倒排索引:以关键字为索引,关键字对应和该关键字相关的所有文档
由于每个关键字对应的文档数量在动态变化,索引倒排索引的建立与维护较为复杂,但是由于可以一次性得到查询关键字所对应的所有文档,所以效率高于正向索引。在全文检索中,检索的快速响应是一个关键的性能,而索引的创建在后台运行,尽管效率相对较低一些,但不会影响搜索的效率

为了更加清楚的理解倒排索引,先看一下这张图

我们可以看到倒排索引会将关键字拆分,将某个关键字与它对应的所有文档绑定,并且根据相关的文档数进行降序排列,所以称为倒排索引

ES索引库的基本操作

mapping是对索引库中文档的约束,常见的mapping属性包括:
type:字段数据类型,常见的简单类型有:
1.字符串:text(可分词的文本),keyword(精确值,例如:品牌,国家,邮箱)
2.数值:long,integer,short,byte,double,float
3.布尔:boolean
4.日期:date
5.对象:object
index:是否创建索引参与搜索,默认为true,如果不参与搜索设置为false
analyzer:使用哪种分词器

创建索引库和mapping的语法如下:

PUT /索引库名
{
  "mappings": {
    "properties": {
      "字段1": {
        "type": "数据类型",
        "index": false
      },
      "title": {
        "type": "数据类型",
        "analyzer": "ik_max_word"
      },
      "time": {
        "type": "数据类型",
        "index": false
      }
    }
  }
}

例如:创建一个新闻索引库:

查看索引库
语法: GET /索引库名
实例: GET /news
删除索引库
语法: DELETE /索引库名
实例: DELETE /news
修改索引库
索引库和mapping一旦创建无法修改,但是可以添加新的字段,语法如下:
例如我们向上述新增的新闻索引库添加观看人数字段:

PUT /news/_mapping
{
  "properties": {
    "count": {
      "type": "long"
    }
  }
}

新增之后查询新闻索引库,可以看到明显多了一个字段

ES文档操作

新增文档
语法:

POST /索引库名/_doc/文档 id
{
	“字段名 1”:”值 1”
	“字段名 2”:”值 2”
	.....
}

查询文档
语法:
GET /索引库名/_doc/文档 id
删除文档
语法:
DELETE /索引库名/_doc/文档 id
修改文档

POST /索引库名/_update/文档 id
{
	"doc":{
		"要修改的字段":"新值"
	}
}

搜索文档

GET /news/_search
{
	"query":{
		"match":{
			"title":"美国"
		}
	}
}

SpringBoot集成ES

搭建,官网地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html
1.首先导入依赖
指定版本,版本必须与安装的 ES 版本一致

<properties>
	<java.version>1.8</java.version>
	<elasticsearch.version>7.6.1</elasticsearch.version>
</properties>

<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

2.添加初始化 RestHighLevelClient 的配置类

@Configuration
public class ElasticSearchConfig {

    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http")));
        return client;
    }

}

索引库操作

创建索引库

CreateIndexRequest request = new CreateIndexRequest("users");
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request,
RequestOptions.DEFAULT);

判断索引库是否存在

GetIndexRequest request = new GetIndexRequest("users");
boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);

删除索引库

DeleteIndexRequest indexRequest = new DeleteIndexRequest("users");
AcknowledgedResponse delete = restHighLevelClient.indices().delete(indexRequest,
RequestOptions.DEFAULT);
delete.isAcknowledged();//返回 true 删除成功,返回 false 删除失败

文档操作

添加文档

//将新闻添加到 mysql 的同时,将数据同步更新到 ES,为搜索提供数据
News news = new News();
news.setId(3);
news.setTitle("美国今年要总统选择,拜登着急了");
news.setImg("aaaaasssss.jpg");
IndexRequest indexRequest = new IndexRequest("news").id(news.getId().toString());
//将对象转为 json 存进 ES
indexRequest.source(new ObjectMapper().writeValueAsString(news),XContentType.JSON);
restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);

修改文档

News news = new News();
news.setId(3);
news.setTitle("中国航母开往美国,准备开战,拜登着急了");
news.setImg("dddddddddddd.jpg");
UpdateRequest updateRequest = new UpdateRequest("news",news.getId().toString());
updateRequest.doc(new ObjectMapper().writeValueAsString(news), XContentType.JSON);
restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);

查询文档

GetRequest getRequest = new GetRequest("news","1");
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
//获取查询的内容,返回 json 格式
String json = getResponse.getSourceAsString();
//使用 jackson 组件将 json 字符串解析为对象
News news = new ObjectMapper().readValue(json, News.class);

删除文档

DeleteRequest deleteRequest = new DeleteRequest("news","1");
DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);

搜索文档

SearchRequest searchRequest = new SearchRequest("news");
SearchRequest searchRequest = new SearchRequest("news");
//精确条件查询
searchRequest.source().query(QueryBuilders.termQuery("title","美国"));
//发送查询请求
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//接收查询结果
SearchHits hits = search.getHits();
//组装查询结果
ArrayList<News> list = new ArrayList<>();
//取出结果集
for (SearchHit searchHit : hits.getHits()){
String json = searchHit.getSourceAsString();
News news = new ObjectMapper().readValue(json,News.class);
list.add(news);
}

高亮显示

searchRequest.source().highlighter(new
HighlightBuilder().field("title").requireFieldMatch(false));
//组装查询结果
ArrayList<News> list = new ArrayList<>();
for (SearchHit searchHit : hits.getHits()){
String json = searchHit.getSourceAsString();
News news = new ObjectMapper().readValue(json,News.class);
//获取高亮名称
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
HighlightField titleField = highlightFields.get("title");
String highttitle = titleField.getFragments()[0].string();
news.setTitle(highttitle);//用添加了高亮的标题替换原始文档标题内容
list.add(news);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值