SpringBoot集成ElasticSearch
1. ElasticSearch
1.1 ElasticSearch的简单介绍
ElasticSearch 是开源搜索平台领域的一个新成员。 ElasticSearch(简称 ES) 是一个基于 Lucene 构建的开源,分布式,RESTful 搜索引擎。 具有搜索实时、稳定、可靠和快速的特点,并且安装使用方便。 支持通过 HTTP 请求,使用 JSON 进行数据索引。
1.2 ElasticSearch的一些概念的介绍
(1). 索引: 一个索引就是一个拥有几分相似特征的文档的集合。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。索引名称必须要全部小写,也不能以下划线开头,不能包含逗号。类似于关系型数据库中的数据库;
(2). 类型:在一个索引中,可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,通常,会为具有一组共同字段的文档定义一个类型,每一个类型都拥有自己的映射(mapping)或者结构定义,它们定义了当前类型下的数据结构,类似于数据库表中的列。划分时需要注意一些限制条件,例如不同的文档类型对同一字段不能设置为不同的字段类型;类似于关系型数据库中的的表;
(3). 文档:一个文档是一个可被索引的基础信息单元。文档以JSON(JavascriptObject Notation)格式来表示,一个文档不只包含了数据。它还包含了元数据(metadata) —— 关于文档的信息。有三个元数据元素是必须存在的,它们是:id是一个字符串, _index 和 _type ,他们可以组合起来锁定Elasticsearch中一个特定的文档。
1.3 ElasticSearch与关系型数据库的对照关系
DB -> Databases -> Tables -> Rows -> Columns
ES -> Indices -> Types -> Documents -> Fields
3.4 ElasticSearch中对索引的一些常用操作
1. 获取Elasticsearch中所有的index:
curl -XGET 'localhost:9200/_cat/indices?v&pretty'
2. 获取某索引下所有的type:
获取elasticsearch索引下所有的type定义
curl -XGET 'localhost:9200/elasticsearch/_mapping'
3. 删除索引:
删除elasticsearch1索引
curl -XDELETE 'localhost:9200/elasticsearch1?pretty'
4. 删除某索引下的某type数据:
删除索引elasticsearch下的article类型
curl -XDELETE 'http://localhost:9200/elasticsearch/article/'
5. 查看某索引的某type下所有的数据:
查找elasticsearch索引中所有的类型为Company的数据
http://localhost:9200/elasticsearch/Company/_search
3.5 ElasticSearch所遇到过的问题
1 当查询的时候如果没有设置from和size,那么默认只会查出10条,不是我们想要的果
解决办法: 通过先查出总的条数,再将其值设置为size;
2 查询多条数据时遇到过的问题:
错误提示信息:Result window is too large
解决方案: 主要是修改index.max_result_window参数,默认为10000
命令:curl -XPUT http://127.0.0.1:9200/cmdb-now/_settings -d '{ "index" : { "max_result_window" : 100000000}}'
3 各种版本与api不匹配的问题
解决方式:上网找,以及测试api与版本
1.6 ElasticSearch架构及其支持的客户端连接方式
Es架构图方式一:REST API ,端口 9200:这种连接方式对应于架构图中的RESTful style API这一层,这种客户端的连接方式是RESTful风格的,使用http的方式进行连接
方式二:Transport ,端口 9300:这种连接方式对应于架构图中的Transport这一层,这种客户端连接方式是直接连接ES的节点,使用TCP的方式进行连接;(ES7.0将会关闭Transport,8.0将完全删除,取而代之的是High Level REST Client)
ElasticSearch提供了两个JAVA REST client 版本:
Java Low Level REST Client: 低级别的REST客户端,通过http与集群交互,需自己编组请求JSON串,及解析响应JSON串。兼容所有ES版本。Java High Level REST Client: 高级别的REST客户端,基于低级别的REST客户端,增加了编组请求JSON串、解析响应JSON串等相关api。使用的版本需要保持和ES服务端的版本一致,否则会有版本问题。
1.7 代码示例
/**
* @description: es工具类
* @author: Guimu
* @create: 2018/07/31 11:47:55
**/
@Component
public class EsUtils {
@Autowired
private RestHighLevelClient client;
private static final String CURRENT_MODEL_PACKAGE_NAME = "com.yy.elasticsearch.model.";
private static final String DEFAULT_INDEX = "elasticsearch";
/**
* @Description: 根据Base 子类数据产生一个IndexRequest数据
* @Param: [source]
* @Return: org.elasticsearch.action.index.IndexRequest
* @Author: Guimu
* @Date: 2018/7/31 下午5:30
*/
private IndexRequest indexRequestGenerater(Base source) {
IndexRequest indexRequest = null;
if (StringUtils.isEmpty(source.getIndex())) {
source.setIndex(DEFAULT_INDEX);
}
try {
String[] tempArr = source.getClass().getName().split("\\.");
source.setType(tempArr[tempArr.length - 1]);
indexRequest = new IndexRequest(source.getIndex(), source.getType());
indexRequest.source(JacksonUtil.getString(source), XContentType.JSON);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return indexRequest;
}
/**
* @Description: 批量存储接口, boolean 表示保存成功与否
* @Param: [bases]
* @Return: boolean
* @Author: Guimu
* @Date: 2018/7/31 下午5:32
*/
public boolean batchSave(List<? extends Base> bases) {
BulkRequest bulkRequest = new BulkRequest();
bases.forEach(el -> bulkRequest.add(indexRequestGenerater(el)));
boolean flag = false;
try {
BulkResponse bulkItemResponses = client.bulk(bulkRequest);
flag = "created".equalsIgnoreCase(bulkItemResponses.getItems()[0].getResponse().getResult().name());
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* @Description: 保存单个索引文档数据
* @Param: [source]
* @Return: boolean
* @Author: Guimu
* @Date: 2018/7/31 下午2:05
*/
public boolean singleSave(Base source) {
IndexRequest singleRequest;
boolean flag = false;
try {
singleRequest = indexRequestGenerater(source);
IndexResponse indexResponse = client.index(singleRequest);
flag = "created".equalsIgnoreCase(indexResponse.getResult().name());
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* @Description: 模糊匹配名字, 精确匹配corpId
* @Param: [name, corpId]
* @Return: java.util.List<com.yy.elasticsearch.model.Base>
* @Author: Guimu
* @Date: 2018/7/31 下午2:21
*/
public List<Base> queryLikeNameAndCorpId(String name, String type, Long corpId) {
MatchPhraseQueryBuilder mb1 = QueryBuilders.matchPhraseQuery("corpId", corpId);
MatchPhraseQueryBuilder mb2 = QueryBuilders.matchPhraseQuery("name", "*" + name + "*");
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(mb1).must(mb2);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(queryBuilder);
SearchRequest searchRequest = new SearchRequest(DEFAULT_INDEX);
searchRequest.types(type);
searchRequest.source(searchSourceBuilder);
return baseQuery(searchRequest);
}
/**
* @Description: 查找指定corpId和type的所有数据
* @Param: [name, corpId]
* @Return: java.util.List<com.yy.elasticsearch.model.Base>
* @Author: Guimu
* @Date: 2018/7/31 下午2:21
*/
public List<Base> queryByCorpId(String type, Long corpId) {
MatchPhraseQueryBuilder mb = QueryBuilders.matchPhraseQuery("corpId", corpId);
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(mb);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(queryBuilder);
SearchRequest searchRequest = new SearchRequest(DEFAULT_INDEX);
searchRequest.types(type);
searchRequest.source(searchSourceBuilder);
return baseQuery(searchRequest);
}
/**
* @Description: 查询该索引中, 指定type的所有数据
* @Param: [index, type]
* @Return: java.util.List<com.yy.elasticsearch.model.Base>
* @Author: Guimu
* @Date: 2018/7/31 下午6:06
*/
public List<Base> findAll(String index, String type) {
SearchRequest searchRequest = new SearchRequest(index);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(0);
searchSourceBuilder.size(this.getCount(searchRequest).intValue());
searchRequest.source(searchSourceBuilder);
//查询全部xxxxx
searchRequest.types(type);
return baseQuery(searchRequest);
}
/**
* @Description: 获取该查询请求的总条数total
* @Param: [searchRequest]
* @Return: java.lang.Long
* @Author: Guimu
* @Date: 2018/7/31 下午4:58
*/
private Long getCount(SearchRequest searchRequest) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(0);
searchSourceBuilder.size(1);
searchRequest.source(searchSourceBuilder);
try {
SearchResponse response = client.search(searchRequest);
return response.getHits().getTotalHits();
} catch (IOException e) {
e.printStackTrace();
}
return -1L;
}
/**
* @Description: 私有的基础查询, 提高代码复用性
* @Param: [searchRequest]
* @Return: java.util.List<com.yy.elasticsearch.model.Base>
* @Author: Guimu
* @Date: 2018/7/31 下午6:15
*/
private List<Base> baseQuery(SearchRequest searchRequest) {
try {
SearchResponse response = client.search(searchRequest);
return Arrays.asList(response.getHits().getHits()).stream().map(el -> {
Map<String, Object> map = el.getSource();
Class clazz;
Base base = null;
try {
map.put("id", el.getId());
clazz = Class.forName(CURRENT_MODEL_PACKAGE_NAME + map.get("type").toString());
base = (Base) JacksonUtil.getObject(JacksonUtil.getString(map), clazz);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return base;
}).collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* @Description: 基础删除接口, 支持Base的所有子类
* @Param: [base]
* @Return: boolean
* @Author: Guimu
* @Date: 2018/7/31 下午6:32
*/
public boolean deleteBase(Base base) {
DeleteRequest deleteRequest = new DeleteRequest(base.getIndex(), base.getType(), base.getId());
boolean flag = false;
try {
DeleteResponse deleteResponse = client.delete(deleteRequest);
flag = "deleted".equalsIgnoreCase(deleteResponse.getResult().name());
System.out.println(deleteResponse);
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* @Description: 此处传入的base的所有值都是全的,没有null值
* @Param: [base]
* @Return: boolean
* @Author: Guimu
* @Date: 2018/8/1 上午10:02
*/
public boolean updateBase(Base base) {
UpdateRequest updateRequest = new UpdateRequest(base.getIndex(), base.getType(), base.getId());
boolean flag = false;
try {
updateRequest.doc(JacksonUtil.getString(base), XContentType.JSON);
UpdateResponse updateResponse = client.update(updateRequest);
flag = "updated".equalsIgnoreCase(updateResponse.getResult().name());
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* @Description: 根据名字, type, corpId 进行精确查询Base数据,没找到则返回 null
* @Param: [name, type, corpId]
* @Return: com.yy.elasticsearch.model.Base
* @Author: Guimu
* @Date: 2018/8/1 上午9:35
*/
public <T extends Base> T queryOneBase(String name, String type, Long corpId) {
MatchPhraseQueryBuilder mb = QueryBuilders.matchPhraseQuery("corpId", corpId);
MatchPhraseQueryBuilder mb1 = QueryBuilders.matchPhraseQuery("name", name);
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(mb).must(mb1);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(queryBuilder);
SearchRequest searchRequest = new SearchRequest(DEFAULT_INDEX);
searchRequest.types(type);
searchRequest.source(searchSourceBuilder);
Base rebase = null;
try {
rebase = baseQuery(searchRequest).get(0);
} catch (NullPointerException e) {
e.printStackTrace();
}
Class<T> aClass = null;
try {
aClass = (Class<T>) Class.forName(CURRENT_MODEL_PACKAGE_NAME + type);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null == rebase ? null : aClass.cast(rebase);
}
public static String getDefaultIndex() {
return DEFAULT_INDEX;
}
}