这是RestHighLevelClient的一些使用方式和笔记;
一、版本选择
版本的选择一定要慎!Es的版本,ik的版本,以及高级客户端的包的版本一定要统一,不得不说兼容上还是做得很差的。版本的API差异还是很大。会导致一些问题例如NoSuchMethodInvoce的异常等等。
附上maven
<!--elasticSearch-->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.2.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.2.0</version>
</dependency>
二、创建客户端
官方文档一定是最有用的东西,附上地址 https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-create-index.html。 (注:以下方式都是采用官方文档的方式,版本可根据自己使用来选择)
properties配置
#elasticsearch配置
elastic-search.clusterName=
elastic-search.hostName=宿主机地址
elastic-search.port=宿主机端口
elastic-search.indices=
elastic-search.types=
ElasticSearchConfig 代码
@Data
@ConfigurationProperties(prefix = "elastic-search")
@Configuration
public class ElasticSearchConfig {
//ES集群名,默认值
private String clusterName;
//ES集群中节点的域名或IP
private String hostName;
//ES连接端口号
private Integer port;
//ES查询的索引名称
private String indices;
//ES查询的Types
private String types;
}
注入客户端
@Configuration
public class ElasticsearchBean {
private final ElasticSearchConfig esConfig;
@Autowired
public ElasticsearchBean(ElasticSearchConfig esConfig) {
this.esConfig = esConfig;
}
/**
* @return 封装 RestClient
*/
@Bean(destroyMethod = "close")
public RestHighLevelClient restHighLevelClient(){
return new RestHighLevelClient(RestClient.builder(new HttpHost(esConfig.getHostName(), esConfig.getPort(), "http")));
}
}
@Bean(destroyMethod = “close”) 及时关闭资源非常重要,曾经遇到过不关闭资源导致整个服务器崩溃的问题
三、创建索引
有三种创建索引的方法:
这是ES使用HTTP的方式创建的列子
curl -XPOST http://localhost:9200/index/_mapping -H 'Content-Type:application/json' -d'
{
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}'
首先第一种以Map创建的方式
//CreateIndexRequest 实例, 需要注意包的版本 我这里用的7.2的版本 org.elasticsearch.client.indices;
CreateIndexRequest request = new CreateIndexRequest(indexName);
//封装属性 类似于json格式
Map<String, Object> jsonMap = new HashMap<>();
Map<String, Object> properties = new HashMap<>();
Map<String, Object> content = new HashMap<>();
content.put("type", "integer");
Map<String, Object>account = new HashMap<>();
content .put("type", "text");
content .put("analyzer", "ik_max_word");
properties.put("id", content);
properties.put("account", account);
jsonMap.put("properties", properties);
//设置分片
request.settings(Settings.builder()
.put("index.number_of_shards", 3)
.put("index.number_of_replicas", 2)
);
request.mapping(jsonMap);
//我使用的同步的方式 异步请参考官方文档
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
if (!createIndexResponse.isAcknowledged()){
throw new BusinessException("创建索引失败");
}
第二种使用Builder的方式
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject()
.startObject("mappings")
.startObject("properties")
.startObject("id")
.field("type", "integer")
.endObject()
.startObject("account")
.field("type", "text")
.field("analyzer","ik_max_word")
.field("search_analyzer","ik_smart")
.endObject()
.endObject()
.endObject()
.startObject("settings")
.field("number_of_shards",3)
.field("number_of_replicas",1)
.endObject()
.endObject();
request.mapping(builder); //版本不一样注意这个方法不一样,有些版本是source方法
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
if (!createIndexResponse.isAcknowledged()){
throw new BusinessException("创建索引失败");
}
第三种以json的方式
request.mapping(
"{\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" }\n" +
"}",
XContentType.JSON);
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
if (!createIndexResponse.isAcknowledged()){
throw new BusinessException("创建索引失败");
}
五、数据操作
需要注意的是,从Elasticsearch5.x开始就提出了弱化索引类型-type的使用,起初,我们说"索引"和关系数据库的“库”是相似的,“类型”和“表”是对等的。这是一个不正确的对比,导致了不正确的假设。在关系型数据库里,"表"是相互独立的,一个“表”里的列和另外一个“表”的同名列没有关系,互不影响。但在类型里字段不是这样的。
举个例子,两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。
所以在Elasticsearch7.x就完全去除了type。每个索引的type默认且只有为_doc。
首先是新增
public void save(User user) throws Exception{
IndexRequest request = new IndexRequest(INDEX_NAME);
request.id(user.getId()); //ID也可使用内部自动生成的 不过希望和数据库统一唯一业务ID
request.source(JSON.toJSONString(user), XContentType.JSON);
restHighLevelClient.index(request, RequestOptions.DEFAULT);
}
获取
public Object get(String id) {
GetRequest getRequest = new GetRequest(INDEX_NAME, id);
GetResponse getResponse = restHighLevelClient.get(getRequest,RequestOptions.DEFAULT);
if (getResponse.isExists()) {
String sourceAsString = getResponse.getSourceAsString();
return sourceAsString;
}
return null;
}
删除
public void delete(String id) {
DeleteRequest request = new DeleteRequest(INDEX_NAME, String.valueOf(id));
DeleteResponse deleteResponse = restHighLevelClient.delete(request,RequestOptions.DEFAULT);
if (deleteResponse.status() == RestStatus.OK) {
logger.info("删除成功!id: {}", id);
}
}
增删改都非常的简单
基本分页查询
public Page<User> listPage(Pageable pageable, String coutent) throws Exception{
//创建检索请求
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
//创建搜索构建者
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//设置构建搜索属性
sourceBuilder.from(pageable.getPageNumber() * pageable.getPageSize()); // 需要注意这里是从多少条开始
sourceBuilder.size(pageable.getPageSize()); //共返回多少条数据
sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.ASC)); //根据自己的需求排序
if (!StringUtils.isEmpty(coutent)){
//自定义组合查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
TermQueryBuilder termQuery = QueryBuilders.termQuery(STATUS, STATUS);
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("coutent",coutent)
.fuzziness(Fuzziness.AUTO); //模糊匹配
boolQueryBuilder.must(termQuery).must(queryBuilder);
sourceBuilder.query(boolQueryBuilder);
}
//传入构建进行搜索
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//处理结果
RestStatus restStatus = searchResponse.status();
if (restStatus != RestStatus.OK){
throw new BusinessException("搜索错误");
}
List<User> list = new ArrayList<>();
SearchHits hits = searchResponse.getHits();
hits.forEach(item -> list.add(JSON.parseObject(item.getSourceAsString(), User.class)));
return new PageImpl<>(list, pageable, hits.getTotalHits().value);
}
使用QueryBuilder的一些含义
QueryBuilders.termQuery("key", obj) 完全匹配
QueryBuilders.termsQuery("key", obj1, obj2..) 一次匹配多个值
QueryBuilders.matchQuery("key", Obj) 单个匹配, field不支持通配符, 前缀具高级特性
QueryBuilders.multiMatchQuery("text", "field1", "field2"..); 匹配多个字段, field有通配符忒行
QueryBuilders.matchAllQuery(); 匹配所有文件
Bool Query 用于组合多个叶子或复合查询子句的默认查询
must 相当于 与 & =
must not 相当于 非 ~ !=
should 相当于 或 | or
filter 过滤
QueryBuilders.boolQuery()
.must(termQuery("content", "test1"))
.must(termQuery("content", "test4"))
.mustNot(termQuery("content", "test2"))
.should(termQuery("content", "test3"))
.filter(termQuery("content", "test5"));
高亮查询
/**
* 搜索入口 搜索结果处理
*/
public Page<User> searchContent(Pageable pageable, String content) throws IOException{
SearchResponse searchResponse = searchHighlight(pageable.getPageSize() * pageable.getPageNumber(), pageable.getPageSize(), content, "account", "name");
List<User> list = Arrays.stream(searchResponse.getHits().getHits()).map(this::toHighLightUser).collect(Collectors.toList());
return new PageImpl<>(list, pageable, searchResponse.getHits().getTotalHits().value);
}
/**
* SearchHit 转换为高亮字段对象
*/
private User toHighLightUser(SearchHit searchHit){
User user = JSON.parseObject(searchHit.getSourceAsString(), User.class);
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
if (!highlightFields.isEmpty()){
HighlightField account = highlightFields.get("account");
if (!StringUtils.isEmpty(account)){
user.setAccount(Arrays.stream(account.getFragments()).map(Text::string).collect(Collectors.joining("\n")));
}
HighlightField name = highlightFields.get("name");
if (!StringUtils.isEmpty(name)){
user.setName(Arrays.stream(name.getFragments()).map(Text::string).collect(Collectors.joining("\n")));
}
}
return user;
}
/**
* 构建高亮字段 进行 多字段模糊查询
*/
private SearchResponse searchHighlight(int from, int size, String content, String ... fields) throws IOException{
HighlightBuilder highlightBuilder = new HighlightBuilder();
for (String field : fields){
highlightBuilder.field(makeHighlightContent(field));
}
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//自定义条件
boolQueryBuilder.should(QueryBuilders.multiMatchQuery(content, fields).fuzziness(Fuzziness.AUTO));
boolQueryBuilder.should(QueryBuilders.wildcardQuery("account", "*" + content + "*"));
boolQueryBuilder.should(QueryBuilders.wildcardQuery("name", "*" + content + "*"));
return searchDocument(highlightBuilder, boolQueryBuilder , from , size);
}
/**
* 构建SearchRequest, SearchSourceBuilder , 执行search方法
*/
private SearchResponse searchDocument(HighlightBuilder highlightBuilder, QueryBuilder queryBuilder, int from, int size) throws IOException{
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
if (!ObjectUtils.isEmpty(highlightBuilder)){
searchSourceBuilder.highlighter(highlightBuilder);
}
searchSourceBuilder.query(queryBuilder);
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
searchRequest.source(searchSourceBuilder);
return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
}
/**
* 封装高亮查询字段
*/
private HighlightBuilder.Field makeHighlightContent(String fieldName){
HighlightBuilder.Field highlightContent = new HighlightBuilder.Field(fieldName);
highlightContent.preTags("<span style=\"color:red\">");
highlightContent.postTags("</span>");
return highlightContent;
}
(后续慢慢更新。。。。。)