ElasticSearch
仿京东搜索Gitee地址:https://gitee.com/infiniteStars/elastic-search-jd
1.简介
-
Elasticsearch是一个基于==Lucene==的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。
-
Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。
-
它能很方便的使大量数据具有搜索、分析和探索的能力。
-
Elasticsearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elasticsearch 数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户。
2.安装
下载地址:https://www.elastic.co/cn/downloads/elasticsearch
- 下载后解压即可。
- 如果在bin目录下,双击elasticsearch.bat 闪退的话,可以打开命令提示符(管理员模式),到bin目录。输入elasticsearch.bat。
这样可以看到是哪里出错的,以便进行修改。
- 启动成功后,会出现以下界面。
- 访问进行
安装可视化界面 head插件
-
下载地址 https://github.com/mobz/elasticsearch-head
-
解压后,通过命令行进入该目录。
-
cnpm install #安装依赖 npm run strat #启动
-
访问
- 点击连接没有反应,因为跨域了。所以要进行配置。
- 进入 elasticsearch-7.13.0\config\elasticsearch.yml 目录,在下边添加以下代码
http.cors.enabled: true
http.cors.allow-origin: "*"
- 刷新http://localhost:9100/
3.Kibana
1.简介
- Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的。可以用kibana搜索、查看存放在Elasticsearch中的数据。Kibana与Elasticsearch的交互方式是各种不同的图表、表格、地图等,直观的展示数据,从而达到高级的数据分析与可视化的目的。
- Elasticsearch、Logstash和Kibana这三个技术就是我们常说的ELK技术栈,可以说这三个技术的组合是大数据领域中一个很巧妙的设计。一种很典型的MVC思想,模型持久层,视图层和控制层。Logstash担任控制层的角色,负责搜集和过滤数据。Elasticsearch担任数据持久层的角色,负责储存数据。而Kibana担任视图层角色,拥有各种维度的查询和分析,并使用图形化的界面展示存放在Elasticsearch中的数据。
2.安装
- 版本要和El对应asticSearch
- 下载地址 https://www.elastic.co/cn/downloads/kibana
- 解压,点击bin目录下的 kibana.bat 启动。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbhofHJh-1631692190556)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20210530190711883.png)]
- 访问 http://localhost:5601
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-35dWPWg2-1631692190557)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20210530190757191.png)]
-
可以看到页面是英文的,可以设置成中文。
到config目录下的 kibana.yml,进行编辑。在最后一行添加
i18n.locale: "zh-CN"
-
重新启动服务,可以看到已经汉化了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-71QaQJ32-1631692190558)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20210530191347220.png)]
4.核心概念
ElasticSearch是面向文档的,接下来用关系型数据库对ES中的概念进行理解。
ElasticSearch | DB(关系型数据库) |
---|---|
索引(indices) | 数据库(datebase) |
types | 表(table) |
documents(文档) | 行(rows) |
fields | 字段(columns) |
5.Ik分词器
- IK体用了两个分词算法:ik_smart和ik_max_word, ik_smart为最少切分 , ik_max_word为最细粒度划分!
- 下载 https://github.com/medcl/elasticsearch-analysis-ik
- 注意:下载第一个,否则启动的时候会报错。
- 解压完成后,放在 D:\Environment\ElasticSearch\elasticsearch-7.13.0\plugins 目录下即可。
- 启动elasticsearch
- 启动kibana测试
查看不同的分词效果
ik_smart
ik_max_word(将词的排列组合都列举出来)
-
有时候我们自定义的词,它拆分不出来。如果想让它完整显示出来,需要我们进行配置。
-
elasticsearch-7.13.0\plugins\ik\config 在这个目录下新建一个 XXX.dic 文件,将想要词写进去。
-
在 IKAnalyzer.cfg.xml 进行配置。
- 重启服务,可以发现能拆分成我们想要的词。
6.Restful风格操作
含义
- 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制.
基本Rest风格命令说明
method | url地址 | 描述 |
---|---|---|
PUT | 127.0.01:9200/索引名称/类型名称/文档id | 创建文档(指定id) |
POST | 127.0.01:9200/索引名称/类型名称 | 创建文档(随机id) |
POST | 127.0.01:9200/索引名称/类型名称/文档id/_update | 修改文档 |
POST | 127.0.01:9200/索引名称/类型名称/文档id/_search | 查询所有数据 |
GET | 127.0.01:9200/索引名称/类型名称/文档id | 通过文档id查询文档 |
DELETE | 127.0.01:9200/索引名称/类型名称/文档id | 删除文档 |
1.创建数据
- 创建索引规则
PUT /test2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date"
}
}
}
}
2.获取数据
GET test1/test1/1
GET test2 # 可以查看默认类型
GET test1/test1/_search?q=name: dz #条件查询
- 通过 GET _cat/indices 这个命令查看所有索引的信息。
3.修改数据
1.第一种方法
在 put 创建的地方直接修改数据。
2.第二种方法
POST /test1/test1/1/_update
{
"doc": {
"name": "yzc"
}
}
4.删除数据
DELETE test1
7.高级查询(重点)
GET test1/test1/_search
{
"query": {
"match": {
"name": "dz"
}
},
"_source": ["name","age"], #结果过滤,列过滤
"sort": {
"age": { #按年龄进行降序查询
"order": "desc"
}
},
"from": 0, #分页查询 从第几个数据开始,显示几条数据 单页查询
"size": 1
}
多条件查询
GET test1/test1/_search
{
"query": {
"bool": {
"must": [ #must 下边所有的条件都要符合 相当于and
{
"match": { #匹配
"name": "dz"
}
},
{
"match": {
"age": "18"
}
}
]
}
}
}
--------------------------------------------------
GET test1/test1/_search
{
"query": {
"bool": {
"should": [ #匹配一个即可 相当于 or
{
"match": {
"name": "dz"
}
},
{
"match": {
"age": "18"
}
}
]
}
}
}
--------------------------------
GET test1/test1/_search
{
"query": {
"bool": {
"must_not": [ #相当于 not
{
"match": {
"name": "dz"
}
}
]
}
}
}
-----------------------------------
GET test1/test1/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "dz"
}
}
],
"filter": { #对查询出来的数据进行过滤
"range": {
"age": {
"lt": 10
}
}
}
}
}
}
- gt (greater than) 大于
- gte(greater than equals) 大于等于
- lt (less than) 小于
- lte (less than equals) 小于等于
keyword 和 text的区别
使用 term 进行精确查询的时候,keyword 不会被分词器解析,而 text 会被分词器解析。
高亮显示
GET test1/test1/_search
{
"query": {
"match": {
"name": "dz"
}
},
"highlight": {
"fields": {
"name": {} #name 设置高亮
}
}
}
8.集成SpringBoot
- 学习文档 https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-compatibility.html
1.创建项目
- 新建一个SpringBoot项目
- 勾选 nosql中的 ElasticSearch。
- 在pom.xml中修改ElasticSearch版本
<properties>
<java.version>11</java.version>
<elasticsearch.version>7.13.0</elasticsearch.version>
</properties>
- 编写配置类
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient() {
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost",9200,"http")));
return client;
}
}
2.操作索引
1.创建索引
@Qualifier("restHighLevelClient")
@Autowired
private RestHighLevelClient client;
//创建索引
@Test
void createIndex() throws IOException {
//创建索引请求
CreateIndexRequest request = new CreateIndexRequest("dz");
//客户端执行请求
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(response);
}
2.判断索引是否存在
//判断索引是否存在
@Test
void existsIndex() throws IOException {
//创建索引请求
GetIndexRequest request = new GetIndexRequest("dz");
//客户端执行请求
boolean response = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(response);
}
3.删除索引
//删除索引
@Test
void deleteIndex() throws IOException {
//创建索引请求
DeleteIndexRequest delete = new DeleteIndexRequest("dz");
AcknowledgedResponse response1 = client.indices().delete(delete, RequestOptions.DEFAULT);
System.out.println(response1.isAcknowledged());
}
3.操作数据
1.插入数据
//向索引插入数据
@Test
void testAdd() throws IOException {
User user = new User("dz", 18);
IndexRequest request = new IndexRequest("dz");
request.id("1");
//将数据放入请求
request.source(JSON.toJSONString(user), XContentType.JSON);
IndexResponse index = client.index(request, RequestOptions.DEFAULT);
System.out.println(index.status());
}
2.获取数据
//获取数据
@Test
void getTest() throws IOException {
GetRequest request = new GetRequest("dz","1");
//判断数据是否存在
boolean exists = client.exists(request, RequestOptions.DEFAULT);
GetResponse fields = client.get(request, RequestOptions.DEFAULT);
System.out.println(fields.getSourceAsString());
}
3.更新数据
//更新数据
@Test
void updateTest() throws IOException {
UpdateRequest request = new UpdateRequest("dz", "1");
User user = new User("zsq", 18);
request.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse update = client.update(request, RequestOptions.DEFAULT);
System.out.println(update.status());
}
4.删除数据
//删除数据
@Test
void deleteTest() throws IOException {
DeleteRequest request = new DeleteRequest("dz", "1");
request.timeout("1s");
DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
System.out.println(delete.status());
}
5.批量插入数据
- 其他操作同理
//批量插入数据
@Test
void BulkInsertTest() throws IOException {
BulkRequest request = new BulkRequest();
request.timeout("10s");
ArrayList<User> users = new ArrayList<>();
users.add(new User("dz1",18));
users.add(new User("dz2",18));
users.add(new User("dz3",18));
users.add(new User("dz4",18));
users.add(new User("dz5",18));
for (int i = 0; i < users.size(); i++) {
request.add(
new IndexRequest("dz").id(""+i+1).source(JSON.toJSONString(users.get(i)),XContentType.JSON));
}
BulkResponse bulk = client.bulk(request, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures());
}
6.搜索数据(重点)
//查询
@Test
void SearchTest() throws IOException {
//搜索哪一个索引
SearchRequest request = new SearchRequest("dz");
//构建搜索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
//精准查询,name为实体类的属性
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "dz1");
//匹配所有
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
builder.query(termQueryBuilder);
request.source(builder);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
SearchHits hits = search.getHits();
System.out.println(JSON.toJSONString(hits));
}
4.高亮搜索(重点)
将数据库中的数据放在创建的索引中
//将数据库里面的数据添加到es的index中,这个操作应该在service层中实现
public void addes() throws IOException {
//获取数据
List<Book> books = bookService.list();
//批量存放
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
for (int i = 0; i < books.size(); i++) {
bulkRequest.add(new IndexRequest("books") //创建索引
.source(JSON.toJSONString(books.get(i)), XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(!bulk.hasFailures());
}
执行搜索
//条件搜索
public List<Map<String,Object>> searchPage(String keyword, int pageNo, int pageSize) throws IOException {
if (pageNo < 1)
pageNo = 1;
//条件搜索 搜索books索引里的内容
SearchRequest searchRequest = new SearchRequest("books");
//构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from(pageNo);
sourceBuilder.size(pageSize);
//精准匹配 如果name属性中含有keyword,则会高亮显示
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", keyword);
sourceBuilder.query(termQueryBuilder);
//超时设置
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
//高亮字段
highlightBuilder.field("name");
//如果name里面有多个keyword,则只显示第一个
highlightBuilder.requireFieldMatch(false); //关闭多个高亮显示,只显示第一个
//自定义如何高亮
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
sourceBuilder.highlighter(highlightBuilder);
//执行搜索
searchRequest.source(sourceBuilder);
//搜索的结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
List<Map<String, Object>> list = new ArrayList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
//获取所有的高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField title = highlightFields.get("name");
Map<String, Object> sourceAsMap = hit.getSourceAsMap(); //原来的字段,
//解析高亮的字段,将原来的字段替换为高亮的字段
if (title != null){
//高亮字段
Text[] fragments = title.fragments();
String n_title = "";
for (Text text : fragments) {
n_title += text;
}
sourceAsMap.put("name",n_title); //执行替换
}
list.add(hit.getSourceAsMap());
}
return list;
}