一、安装es
* 下载:https://www.elastic.co/cn/downloads/elasticsearch
* 启动:cmd下进入安装目录中的bin中 输入 elasticsearch.bat 运行
* 浏览器输入:http://127.0.0.1:9200/ 查看是否安装成功,出现下图表示安装成功
*当在服务器安装es时需要在es的配置文件中修改相应内容,如下图:
将ip改为0.0.0.0,方便外网访问:
network.host: 0.0.0.0
将node-2节点删除:
cluster.initial_master_nodes: ["node-1"]
二、安装ik分词器
- 安装:
- 1.下载与elasticsearch同版本号的ik安装包,
- 2.在elasticsearch安装包中的plugins目录新建文件夹ik
- 3.将解压好的文件中的内容复制到elasticsearch安装包中的plugins目录
- 注意:ik分词器的版本要同 elasticsearch 的版本号一致,否则会启动失败
三、安装kibana
- 安装:下载相应安装包解压
- 启动:打开解压文件下的bin目录,单击kibana.bat 运行
- 浏览器输入:http://127.0.0.1:5601/ 查看是否安装成功
- 当在服务器安装kibana时需要在kibana的配置文件中修改相应内容:
四、springboot整合es
1、pom文件引入相关jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
2、yml配置
spring:
elasticsearch:
rest:
uris: localhost:9200
3、代码编写
3.1 封装分页
public class Page<T> extends SimplePage implements java.io.Serializable,
Paginable {
public Page() {
}
public Page(int pageNo, int pageSize, int totalCount) {
super(pageNo, pageSize, totalCount);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Page(int pageNo, int pageSize, int totalCount, List list) {
super(pageNo, pageSize, totalCount);
this.list = list;
}
public int getFirstResult() {
return (pageNo - 1) * pageSize;
}
/**
* 当前页的数据
*/
private List<T> list;
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
/**
* 分页实体
*/
public interface Paginable {
/**
* 总记录数
* @return
*/
public int getTotalCount();
/**
* 总页数
* @return
*/
public int getTotalPage();
/**
* 每页记录数
* @return
*/
public int getPageSize();
/**
* 当前页号
* @return
*/
public int getPageNo();
/**
* 是否第一页
* @return
*/
public boolean isFirstPage();
/**
* 是否最后一页
* @return
*/
public boolean isLastPage();
/**
* 返回下页的页号
*/
public int getNextPage();
/**
* 返回上页的页号
*/
public int getPrePage();
}
public class SimplePage implements Paginable {
private static final long serialVersionUID = 1L;
public static final int DEF_COUNT = 20;
public SimplePage() {
}
public SimplePage(int pageNo, int pageSize, int totalCount) {
if (totalCount <= 0) {
this.totalCount = 0;
} else {
this.totalCount = totalCount;
}
if (pageSize <= 0) {
this.pageSize = DEF_COUNT;
} else {
this.pageSize = pageSize;
}
if (pageNo <= 0) {
this.pageNo = 1;
} else {
this.pageNo = pageNo;
}
if ((this.pageNo - 1) * this.pageSize >= totalCount) {
this.pageNo = totalCount / pageSize;
if(this.pageNo==0){
this.pageNo = 1 ;
}
}
}
/**
* 调整分页参数,使合理化
*/
public void adjustPage() {
if (totalCount <= 0) {
totalCount = 0;
}
if (pageSize <= 0) {
pageSize = DEF_COUNT;
}
if (pageNo <= 0) {
pageNo = 1;
}
if ((pageNo - 1) * pageSize >= totalCount) {
pageNo = totalCount / pageSize;
}
}
public int getPageNo() {
return pageNo;
}
public int getPageSize() {
return pageSize;
}
public int getTotalCount() {
return totalCount;
}
public int getTotalPage() {
int totalPage = totalCount / pageSize;
if (totalCount % pageSize != 0 || totalPage == 0) {
totalPage++;
}
return totalPage;
}
public boolean isFirstPage() {
return pageNo <= 1;
}
public boolean isLastPage() {
return pageNo >= getTotalPage();
}
public int getNextPage() {
if (isLastPage()) {
return pageNo;
} else {
return pageNo + 1;
}
}
public int getPrePage() {
if (isFirstPage()) {
return pageNo;
} else {
return pageNo - 1;
}
}
protected int totalCount = 0;
protected int pageSize = 20;
protected int pageNo = 1;
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public void setPageNo(int pageNo) {
this.pageNo = pageNo;
}
protected int filterNo;
public int getFilterNo() {
return filterNo;
}
public void setFilterNo(int filterNo) {
this.filterNo = filterNo;
}
}
3.2 创建实体类
@Document(indexName = "book")
@Mapping(mappingPath = "productIndex.json") // 解决IK分词不能使用问题
@Data
public class Book implements Serializable {
@Id
private String id;
@Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String bookName;
private String author;
@Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String introduction;
private String createTime;
private String updateTime;
}
productIndex.json 该文件放在resources目录下
{
"properties": {
"createTime": {
"type": "long"
},
"productDesc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"productName": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"updateTime": {
"type": "long"
}
}
}
3.3 创建 BookRespository
package com.cisdi.es.Repository;
import com.cisdi.es.domain.Book;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
@Component
public interface BookRespository extends ElasticsearchRepository<Book,String> {
}
3.4 BookEsSearchService
package com.cisdi.es.service;
import com.cisdi.es.domain.Book;
import java.util.List;
public interface BookEsSearchService extends BookBaseSearchService<Book> {
/**
* 保存
*/
void save(Book... books);
/**
* 删除
* @param id
*/
void delete(String id);
/**
* 清空索引
*/
void deleteAll();
/**
* 根据ID查询
* @param id
* @return
*/
Book getById(String id);
/**
* 查询全部
* @return
*/
List<Book> getAll();
}
3.5 BookBaseSearchService
package com.cisdi.es.service;
import com.cisdi.es.page.Page;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface BookBaseSearchService<T> {
/**
* 搜 索
* @param keyword 关键字
* @param clazz 实体
* @param indexName 索引库名称
* @return
*/
List<T> query(String keyword, String indexName, Class<T> clazz,String... fieldNames) throws IOException;
/**
* 搜索高亮显示
* @param keyword 关键字
* @param clazz 索引库
* @param indexName 索引库名称
* @param startTime 开始时间
* @param endTime 结束时间
* @param fieldNames 搜索的字段
* @return
*/
List<Map<String,Object>> queryHit(String keyword,String indexName, Class<T> clazz, String startTime,String endTime, String ... fieldNames)throws IOException;
/**
* 搜索高亮显示,返回分页
* @param pageNo 当前页
* @param pageSize 每页显示的总条数
* @param keyword 关键字
* @param clazz 索引库
* @param startTime 开始时间
* @param endTime 结束时间
* @param fieldNames 搜索的字段
* @return
*/
Page<Map<String, Object>> queryHitByPage(int pageNo, int pageSize, String keyword,String indexName, Class<T> clazz,String startTime,String endTime, String ... fieldNames)throws IOException;
/**
* 删除索引库
* @param indexName
* @return
*/
void deleteIndex(String indexName)throws IOException;
}
3.6 实现类
package com.cisdi.es.service.impl;
import com.alibaba.fastjson.JSON;
import com.cisdi.es.Repository.BookRespository;
import com.cisdi.es.domain.Book;
import com.cisdi.es.service.BookEsSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class BookEsSearchServiceImpl extends BookBaseSearchServiceImpl<Book> implements BookEsSearchService {
private Logger log = LoggerFactory.getLogger(getClass());
@Resource
private BookRespository bookRespository;
@Override
public void save(Book... books) {
if(books.length>0){
log.info("【保存索引】:{}", JSON.toJSONString(bookRespository.saveAll(Arrays.asList(books))));
}
}
@Override
public void delete(String id) {
bookRespository.deleteById(id);
}
@Override
public void deleteAll() {
bookRespository.deleteAll();
}
@Override
public Book getById(String id) {
return bookRespository.findById(id).get();
}
@Override
public List<Book> getAll() {
List<Book> list = new ArrayList<>();
bookRespository.findAll().forEach(list::add);
return list;
}
}
package com.cisdi.es.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.cisdi.es.page.Page;
import com.cisdi.es.service.BookBaseSearchService;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
public class BookBaseSearchServiceImpl <T> implements BookBaseSearchService<T> {
private Logger log = LoggerFactory.getLogger(getClass());
@Resource
private RestHighLevelClient restHighLevelClient;
@Override
public List<T> query(String keyword,String indexName, Class<T> clazz,String... fieldNames)throws IOException {
SearchRequest searchRequest = new SearchRequest(indexName);//discusspost是索引名,就是表名
//构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
//在discusspost索引的title和content字段中都查询“互联网寒冬”
.query(QueryBuilders.multiMatchQuery(keyword, fieldNames))
// matchQuery是模糊查询,会对key进行分词:searchSourceBuilder.query(QueryBuilders.matchQuery(key,value));
// termQuery是精准查询:searchSourceBuilder.query(QueryBuilders.termQuery(key,value));
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));
//一个可选项,用于控制允许搜索的时间:searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//.from(0)// 指定从哪条开始查询
//.size(10);// 需要查出的总记录条数
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("【检索数据】:{}",JSONObject.toJSON(searchResponse));
List<T> list = new LinkedList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
T discussPost = JSONObject.parseObject(hit.getSourceAsString(), clazz);
list.add(discussPost);
}
return list;
}
@Override
public List<Map<String,Object>> queryHit(String keyword,String indexName, Class<T> clazz, String startTime,String endTime,String... fieldNames)throws IOException {
//discusspost是索引名,就是表名
SearchRequest searchRequest = new SearchRequest(indexName);
HighlightBuilder highlightBuilder =createHighlightBuilder(fieldNames);
SearchSourceBuilder searchSourceBuilder=null;
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//时间范围的设定
RangeQueryBuilder rangequerybuilder = QueryBuilders
.rangeQuery("createTime")
.from(startTime).to(endTime);
if(keyword.equals(null)||keyword.equals("")){
//构建搜索条件
boolQueryBuilder.must(QueryBuilders.matchAllQuery());
boolQueryBuilder.must(rangequerybuilder);
searchSourceBuilder = new SearchSourceBuilder()
.query(boolQueryBuilder)
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.highlighter(highlightBuilder);
}else{
//构建搜索条件
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(keyword, fieldNames));
boolQueryBuilder.must(rangequerybuilder);
searchSourceBuilder = new SearchSourceBuilder()
.query(boolQueryBuilder)
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.highlighter(highlightBuilder);
}
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("【检索数据】:{}",JSONObject.toJSON(searchResponse));
List<Map<String,Object>> lists = getHitList(searchResponse.getHits());
return lists;
}
@Override
public Page<Map<String, Object>> queryHitByPage(int pageNo, int pageSize, String keyword, String indexName,Class<T> clazz, String startTime,String endTime,
String... fieldNames)throws IOException {
Page<Map<String, Object>> page = null;
//discusspost是索引名,就是表名
SearchRequest searchRequest = new SearchRequest(indexName);
HighlightBuilder highlightBuilder =createHighlightBuilder(fieldNames);
SearchSourceBuilder searchSourceBuilder=null;
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//时间范围的设定
RangeQueryBuilder rangequerybuilder = QueryBuilders
.rangeQuery("createTime")
.from(startTime).to(endTime);
if(keyword.equals(null)||keyword.equals("")){
//构建搜索条件
boolQueryBuilder.must(QueryBuilders.matchAllQuery());
boolQueryBuilder.must(rangequerybuilder);
searchSourceBuilder = new SearchSourceBuilder()
.query(boolQueryBuilder)
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.highlighter(highlightBuilder)
.from((pageNo-1)*pageSize)// 指定从哪条开始查询
.size(pageSize);// 需要查出的总记录条数
}else{
//构建搜索条件
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(keyword, fieldNames));
boolQueryBuilder.must(rangequerybuilder);
searchSourceBuilder = new SearchSourceBuilder()
.query(boolQueryBuilder)
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.highlighter(highlightBuilder)
.from((pageNo-1)*pageSize)// 指定从哪条开始查询
.size(pageSize);// 需要查出的总记录条数
}
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("【检索数据】:{}",JSONObject.toJSON(searchResponse));
Long totalCount =searchResponse.getHits().getTotalHits().value;
page = new Page<>(pageNo,pageSize,totalCount.intValue());
page.setList(getHitList(searchResponse.getHits()));
return page;
}
@Override
public void deleteIndex(String indexName) throws IOException{
DeleteRequest deleteRequest = new DeleteRequest(indexName);
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
restHighLevelClient.close();
}
/**
* 构造高亮器
*/
private HighlightBuilder createHighlightBuilder(String... fieldNames){
// 设置高亮,使用默认的highlighter高亮器
HighlightBuilder highlightBuilder = new HighlightBuilder()
// .field("productName")
.preTags("<span style='color:red'>")
.postTags("</span>");
// 设置高亮字段
for (String fieldName: fieldNames) highlightBuilder.field(fieldName);
return highlightBuilder;
}
/**
* 处理高亮结果
*/
private List<Map<String,Object>> getHitList(SearchHits hits){
List<Map<String,Object>> list = new ArrayList<>();
Map<String,Object> map;
for(SearchHit searchHit : hits){
map = new HashMap<>();
// 处理源数据
map.put("source",searchHit.getSourceAsMap());
// 处理高亮数据
Map<String,Object> hitMap = new HashMap<>();
searchHit.getHighlightFields().forEach((k,v) -> {
String hight = "";
for(Text text : v.getFragments()) hight += text.string();
hitMap.put(v.getName(),hight);
});
map.put("highlight",hitMap);
list.add(map);
}
return list;
}
}
3.7 controller接口
package com.cisdi.es.controller;
import com.cisdi.es.domain.Book;
import com.cisdi.es.page.Page;
import com.cisdi.es.service.BookEsSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* elasticsearch 搜索
*/
@RestController
public class EsSearchController {
private Logger log = LoggerFactory.getLogger(getClass());
@Resource
private BookEsSearchService bookEsSearchService;
/**
* 新增 / 修改索引
* @return
*/
@PostMapping("save")
public String add(@RequestBody Book... book) {
bookEsSearchService.save(book);
return "success";
}
/**
*
* 删除索引
* @return
*/
@DeleteMapping("delete/{id}")
public String delete(@PathVariable String id) {
bookEsSearchService.delete(id);
return "success";
}
/**
* 清空索引
* @return
*/
@DeleteMapping("delete_all")
public String deleteAll() {
bookEsSearchService.deleteAll();
return "success";
}
/**
* 根据ID获取
* @return
*/
@GetMapping("get/{id}")
public Book getById(@PathVariable String id){
return bookEsSearchService.getById(id);
}
/**
* 根据获取全部
* @return
*/
@GetMapping("get_all")
public List<Book> getAll(){
return bookEsSearchService.getAll();
}
/**
* 根据关键字不分页查询
* @param keyword
* @param fields 搜索字段名称,多个以“,”分割
* @return
*/
@GetMapping("query")
public List<Book> query(@RequestParam String keyword, @RequestParam String fields) throws IOException {
String indexName = "book";
String[] fieldNames = {};
if(fields.contains(",")) fieldNames = fields.split(",");
else fieldNames[0] = fields;
return bookEsSearchService.query(keyword,indexName,Book.class,fieldNames);
}
/**
* 搜索,命中关键字高亮
* @param keyword 关键字
* @param fields 搜索字段名称,多个以“,”分割
* @return
*/
@GetMapping("query_hit")
public List<Map<String,Object>> queryHit(@RequestParam String keyword, @RequestParam String fields ,@RequestParam String startTime,@RequestParam String endTime)throws IOException {
String indexName = "book";
String[] fieldNames = {};
if(fields.contains(",")) fieldNames = fields.split(",");
else fieldNames[0] = fields;
return bookEsSearchService.queryHit(keyword,indexName,Book.class, startTime, endTime,fieldNames);
}
/**
* 搜索,命中关键字高亮
* @param pageNo 当前页
* @param pageSize 每页显示的数据条数
* @param keyword 关键字
* @param fields 搜索字段名称,多个以“,”分割
* @return
*/
@GetMapping("query_hit_page")
public Page<Map<String, Object>> queryHitByPage(@RequestParam int pageNo, @RequestParam int pageSize
, @RequestParam String keyword, @RequestParam String fields
,@RequestParam String startTime,@RequestParam String endTime)throws IOException{
String indexName = "book";
String[] fieldNames = {};
if(fields.contains(",")) fieldNames = fields.split(",");
else fieldNames[0] = fields;
return bookEsSearchService.queryHitByPage(pageNo,pageSize,keyword,indexName,Book.class,startTime,endTime,fieldNames);
}
/**
* 删除索引库
* @param indexName
* @return
*/
@DeleteMapping("delete_index/{indexName}")
public String deleteIndex(@PathVariable String indexName)throws IOException{
bookEsSearchService.deleteIndex(indexName);
return "success";
}
}