下载Elasticsearch7.17.7版本:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.7-linux-x86_64.tar.gz
因为8.6.2版本,与springboot整合ES的版本不一致无法解析响应正文,而且7.17.7也是比较新的
将该压缩包通过Mobax终端上传到目录/中,进入到目录/中,然后解压到目录/usr/local/中:tar -zxvf elasticsearch-7.17.7-linux-x86_64.tar.gz -C /usr/local/
安装ik分词器到该ES服务中:
1)下载IKAnalyzer:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.7/elasticsearch-analysis-ik-7.17.7.zip
2)通过mobax上传至虚拟机的目录/,进入到目录/中,然后进行解压缩:unzip elasticsearch-analysis-ik-7.17.7.zip -d /usr/local/elasticsearch-7.17.7/plugins/analysis-ik
安装拼音分词器到该ES服务中:
1)下载拼音分词器:https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v7.17.7/elasticsearch-analysis-pinyin-7.17.7.zip
2)通过mobax上传到虚拟机的/目录,进入到目录/中,解压到elasticsearch的plugins/analysis-pinyin目录下:unzip elasticsearch-analysis-pinyin-7.17.7.zip -d /usr/local/elasticsearch-7.17.7/plugins/analysis-pinyin
给es用取得该文件件的权限:chown -R es:es /usr/local/elasticsearch-7.17.7
修改配置文件:vim /usr/local/elasticsearch-7.17.7/config/elasticsearch.yml
,添加如下
#集群名称,保证唯一
cluster.name: my_elasticsearch
#节点名称,必须不一样
node.name: node1
#可以访问该节点的ip地址
network.host: 0.0.0.0
#该节点服务端口号
http.port: 9200
#集群间通信端口号
transport.tcp.port: 9300
#候选主节点的设备地址
discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#候选主节点的节点名
cluster.initial_master_nodes: ["node1","node2","node3"]
#关闭安全认证,才能访问es
xpack.security.enabled: false
xpack.security.http.ssl:
enabled: false
将elasticsearch-7.17.7文件夹修改名字为myes1:mv /usr/local/elasticsearch-7.17.7/ /usr/local/myes1
切换为es用户:su es
后台启动第一个ES节点:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes1/bin/elasticsearch -d
,默认占用4G,只是为了搭建集群而搭建集群,所以设置512m的内存。
测试ES服务是否正常启动:curl 127.0.0.1:9200
切换为root用户:su root
进入到目录/中,然后将ES压缩包解压到目录/usr/local/中:tar -zxvf elasticsearch-7.17.7-linux-x86_64.tar.gz -C /usr/local/
进入到目录/中,然后将ik分词器压缩包进行解压缩:unzip elasticsearch-analysis-ik-7.17.7.zip -d /usr/local/elasticsearch-7.17.7/plugins/analysis-ik
进入到目录/中,然后将pinyin分词器解压到elasticsearch的plugins/analysis-pinyin目录下:unzip elasticsearch-analysis-pinyin-7.17.7.zip -d /usr/local/elasticsearch-7.17.7/plugins/analysis-pinyin
给es用取得该文件件的权限:chown -R es:es /usr/local/elasticsearch-7.17.7
修改配置文件:vim /usr/local/elasticsearch-7.17.7/config/elasticsearch.yml
,添加如下
#集群名称,保证唯一
cluster.name: my_elasticsearch
#节点名称,必须不一样
node.name: node2
#可以访问该节点的ip地址
network.host: 0.0.0.0
#该节点服务端口号
http.port: 9201
#集群间通信端口号
transport.tcp.port: 9301
#候选主节点的设备地址
discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#候选主节点的节点名
cluster.initial_master_nodes: ["node1","node2","node3"]
#关闭安全认证,才能访问es
xpack.security.enabled: false
xpack.security.http.ssl:
enabled: false
将elasticsearch-7.17.7文件夹修改名字为myes2:mv /usr/local/elasticsearch-7.17.7/ /usr/local/myes2
进入到目录/中,然后将ES压缩包解压到目录/usr/local/中:tar -zxvf elasticsearch-7.17.7-linux-x86_64.tar.gz -C /usr/local/
进入到目录/中,然后将ik分词器压缩包进行解压缩:unzip elasticsearch-analysis-ik-7.17.7.zip -d /usr/local/elasticsearch-7.17.7/plugins/analysis-ik
进入到目录/中,然后将pinyin分词器解压到elasticsearch的plugins/analysis-pinyin目录下:unzip elasticsearch-analysis-pinyin-7.17.7.zip -d /usr/local/elasticsearch-7.17.7/plugins/analysis-pinyin
给es用取得该文件件的权限:chown -R es:es /usr/local/elasticsearch-7.17.7
修改配置文件:vim /usr/local/elasticsearch-7.17.7/config/elasticsearch.yml
,添加如下
#集群名称,保证唯一
cluster.name: my_elasticsearch
#节点名称,必须不一样
node.name: node3
#可以访问该节点的ip地址
network.host: 0.0.0.0
#该节点服务端口号
http.port: 9202
#集群间通信端口号
transport.tcp.port: 9302
#候选主节点的设备地址
discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#候选主节点的节点名
cluster.initial_master_nodes: ["node1","node2","node3"]
#关闭安全认证,才能访问es
xpack.security.enabled: false
xpack.security.http.ssl:
enabled: false
将elasticsearch-7.17.7文件夹修改名字为myes3:mv /usr/local/elasticsearch-7.17.7/ /usr/local/myes3
切换为es用户:su es
后台启动第二个ES节点:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes2/bin/elasticsearch -d
后台启动第三个ES节点:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes3/bin/elasticsearch -d
查看集群是否搭建成功,访问浏览器:http://192.168.126.24:9200/_cat/nodes
查看集群的状态:curl -uelastic:pwd -XGET "http://127.0.0.1:9200/_cluster/health?pretty"
https://artifacts.elastic.co/downloads/kibana/kibana-7.17.7-linux-x86_64.tar.gz
su root
tar -zxvf kibana-7.17.7-linux-x86_64.tar.gz -C /usr/local/
cd /usr/local/kibana-7.17.7-linux-x86_64/config
vim kibana.yml
server.host: "192.168.126.24"
elasticsearch.hosts: ["http://127.0.0.1:9200","http://127.0.0.1:9201","http://127.0.0.1:9202"]
chown -R es:es /usr/local/kibana-7.17.7-linux-x86_64/
su es
/usr/local/kibana-7.17.7-linux-x86_64/bin/kibana
http://192.168.126.24:5601/
创建product索引
PUT /product
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"id": {
"type": "integer",
"store": true,
"index": true
},
"productName": {
"type": "text",
"store": true,
"index": true
},
"productDesc": {
"type": "text",
"store": true,
"index": true
}
}
}
}
“number_of_shards”: 5,即设置该索引的分片数量为5,“number_of_replicas”: 1,每个分片的副本数为1。
查看集群健康状态:GET /_cat/health?v
查看索引状态:GET /_cat/indices?v
查看分片状态:GET /_cat/shards?v
ES集群可以自动进行故障应对,即可以自动进行水平扩容。
改变每个分片的副本数
PUT /product/_settings
{
"number_of_replicas": 2
}
分片数不能改变,但是分片的副本数可以改变。
每个分片的底层都是一个Lucene索引,会消耗一定的系统资源。搜索请求需要命中索引中的所有分片,分片过多会降低搜索的性能。
分片原则:
1)每个分片占用的磁盘容量不超过ES的JVM的最大堆空间设置(一般设置不超过32G)。
2)分片数一般不超过节点数的三倍。
3)推迟分片分配:节点中断后集群会重新分配分片。但默认集群会等待一分钟来查看节点是否重新加入。可以设置等待的时长,减少分配的次数。
PUT /product/_settings
{
"settings":{
"index.unassigned.node_left.delayed_timeout": "5m"
}
}
自动补全对性能要求极高,ES不是通过倒排索引来实现的,所以需要将对应的查询字段类型设置为completion。
创建索引,添加自动补全字段
PUT /product2
{
"mappings": {
"properties": {
"id": {
"type": "integer",
"store": true,
"index": true
},
"productName": {
"type": "completion",
"store": true,
"index": true
},
"productDesc": {
"type": "text",
"store": true,
"index": true
}
}
}
}
POST /product2/_doc
{
"id":1,
"productName":"elasticsearch1",
"productDesc":"elasticsearch1 is a good search engine"
}
POST /product2/_doc
{
"id":2,
"productName":"elasticsearch2",
"productDesc":"elasticsearch2 is a good search engine"
}
测试自动补全功能
GET /product2/_search
{
"suggest": {
"prefix_suggestion": {
"prefix": "elastic",
"completion": {
"field": "productName",
"skip_duplicates": true,
"size": 10
}
}
}
}
创建news索引
PUT /news
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"ik_pinyin": {
"tokenizer": "ik_smart",
"filter": "pinyin_filter"
},
"tag_pinyin": {
"tokenizer": "keyword",
"filter": "pinyin_filter"
}
},
"filter": {
"pinyin_filter": {
"type": "pinyin",
"keep_joined_full_pinyin":true,
"keep_original": true,
"remove_duplicated_term": true
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "integer",
"index": true
},
"title": {
"type": "text",
"index": true,
"analyzer": "ik_pinyin",
"search_analyzer": "ik_smart"
},
"content": {
"type": "text",
"index": true,
"analyzer": "ik_pinyin",
"search_analyzer": "ik_smart"
},
"url": {
"type": "keyword",
"index": true
},
"tags": {
"type": "completion",
"analyzer": "tag_pinyin"
}
}
}
}
通过命令行进入mysql中导入SQL文件:source 绝对路径/xxx.sql;
下载logstash:https://artifacts.elastic.co/downloads/logstash/logstash-7.17.7-windows-x86_64.zip
将logstash压缩包进行解压,在/logstash-7.17.7/config/目中下创建mysql.conf文件。写入以下脚本:
input {
jdbc {
jdbc_driver_library => "F:\SoftWare\mysql\mysql-connector-java-8.0.27.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql:///news"
jdbc_user => "root"
jdbc_password => "root"
schedule => "* * * * *"
jdbc_default_timezone => "Asia/Shanghai"
statement => "SELECT * FROM news;"
}
}
filter {
mutate {
split => {"tags" => ","}
}
}
output {
elasticsearch {
hosts => ["192.168.126.24:9200","192.168.126.24:9201","192.168.126.24:9202"]
index => "news"
document_id => "%{id}"
}
}
在解压路径/logstash-7.17.7下,打开cmd窗口,运行命令:bin\logstash -f config\mysql.conf
测试自动补齐功能
GET /news/_search
{
"suggest": {
"YOUR_SUGGESTION": {
"prefix": "zhu", //需要补全的关键词
"completion": {
"field": "tags",
"skip_duplicates": true,//去重复值
"size": 10
}
}
}
}
在resources目录下,创建配置文件application.yml,添加如下配置:
spring:
elasticsearch:
uris: http://192.168.126.24:9200,http://192.168.126.24:9201,http://192.168.126.24:9202
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
创建com.zzx.escase.document包,在包下创建实体类News
package com.zzx.escase.document;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.suggest.Completion;
@Document(indexName = "news")
@Data
public class News {
@Id
private Integer id;
@Field
private String title;
@Field
private String content;
@Field
private String url;
@CompletionField
@Transient
private Completion tags;
}
创建com.zzx.escase.repository包,在包下创建接口Repository
package com.zzx.escase.repository;
import com.zzx.escase.document.News;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface NewsRepository extends ElasticsearchRepository<News,Integer> {
}
创建com.zzx.escase.service包,在包下创建类NewsService
package com.zzx.escase.service;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class NewsService {
@Autowired
private ElasticsearchRestTemplate template;
//自动补齐
public List<String> autoSuggest(String keyword){
// 1.创建补齐请求
SuggestBuilder suggestBuilder = new SuggestBuilder();
// 2.构建补齐条件
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("tags").prefix(keyword).skipDuplicates(true).size(10);
suggestBuilder.addSuggestion("prefix_suggestion",suggestionBuilder);
// 3.发送请求
SearchResponse response = template.suggest(suggestBuilder, IndexCoordinates.of("news"));
// 4.处理结果
List<String> result = response.getSuggest().getSuggestion("prefix_suggestion").getEntries().get(0).getOptions().stream()
.map(Suggest.Suggestion.Entry.Option::getText)
.map(Text::toString)
.collect(Collectors.toList());
return result;
}
}
在Test根目录下,创建com.zzx.escase.service包,在包下创建NewsServiceTest测试类
package com.zzx.escase.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class NewsServiceTest {
@Autowired
private NewsService newsService;
@Test
public void testAutoSuggest(){
List<String> list = newsService.autoSuggest("zhu");
list.forEach(System.out::println);
}
}
即打印出tags域中自动补齐的10个关键词
在com.zzx.escase.repository的接口Repository下,添加高亮搜索关键字方法
@Highlight(fields = {@HighlightField(name = "title"),@HighlightField(name="content")})
List<SearchHit<News>> findByTitleMatchesOrContentMatches(String title, String content);
在com.zzx.escase.service的接口NewsService下,添加高亮搜索关键字方法
//查询关键字
public List<News> highLightSearch(String keyword){
List<SearchHit<News>> result = newsRepository.findByTitleMatchesOrContentMatches(keyword, keyword);
//处理结果,封装News类型的集合
ArrayList<News> newsList = new ArrayList();
for (SearchHit<News> newsSearchHit : result) {
News news = newsSearchHit.getContent();
//高亮字段
Map<String, List<String>> highlightFields = newsSearchHit.getHighlightFields();
if(highlightFields.get("title") != null){
news.setTitle(highlightFields.get("title").get(0));
}
if(highlightFields.get("content") != null){
news.setTitle(highlightFields.get("content").get(0));
}
newsList.add(news);
}
return newsList;
}
将elasticsearch内部的处理高亮的结果封装到News实体类中,然后返回
在Test根目录下的com.zzx.escase.service包的NewsServiceTest测试类中添加高亮测试方法
@Test
public void testHighLightSearch(){
List<News> newsList = newsService.highLightSearch("演员");
newsList.forEach(System.out::println);
}
此时每个演员的关键字都会被套上em的标签,再由前端对em标签进行渲染。
创建com.zzx.escase.controller包,在包下创建类NewsController
package com.zzx.escase.controller;
import com.zzx.escase.document.News;
import com.zzx.escase.service.NewsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class NewsController {
@Autowired
private NewsService newsService;
@GetMapping("/autoSuggest")
public List<String> autoSuggest(String term){
return newsService.autoSuggest(term);
}
@GetMapping("/highLightSearch")
public List<News> highLightSearch(String term){
return newsService.highLightSearch(term);
}
}
启动该springboot项目,然后在浏览器中查看:http://localhost:8080/autoSuggest?term="zhu"
以及http://localhost:8080/highLightSearch?term="演员"
前端页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>结果</title>
<link rel="stylesheet" type="text/css" href="css/jquery-ui.min.css"/>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script src="js/jquery-2.1.1.min.js"></script>
<script src="js/jquery-ui.min.js"></script>
<style>
body {
padding-left: 14px;
padding-top: 14px;
}
ul, li {
list-style: none;
padding: 0;
}
li {
padding-bottom: 16px;
}
a, a:link, a:visited, a:hover, a:active {
text-decoration: none;
}
em {
color: red;
font-style: normal;
}
</style>
</head>
<body>
<div>
<input id="newsTag" class="form-control" style="display: inline; width: 50%;" name="keyword">
<button class="btn btn-primary" onclick="search()">搜索一下</button>
</div>
<hr>
<div>
<ul id="news">
</ul>
</div>
</body>
<script>
$("#newsTag").autocomplete({
source: "/autoSuggest", // 请求路径
delay: 100, //请求延迟
minLength: 1 //最少输入多少字符像服务器发送请求
})
function search() {
var term = $("#newsTag").val();
$.get("/highLightSearch", {term: term}, function (data) {
var str = "";
for (var i = 0; i < data.length; i++) {
var document = data[i];
str += "- "
+
" "
+
" " + document.title + "" +
" " +
" "
+ document.content + "" +
"