帐号售卖网站建设,军事新闻最新消息报道,阿里云服务器免费,网站建设连接Spring boot 集成 ElasticSearch 文章目录 Spring boot 集成 ElasticSearch一#xff1a;前置工作1#xff1a;项目搭建和依赖导入2#xff1a;客户端连接相关构建3#xff1a;实体类相关注解配置说明 二#xff1a;客户端client相关操作说明1#xff1a;检索流程1.1前置工作1项目搭建和依赖导入2客户端连接相关构建3实体类相关注解配置说明 二客户端client相关操作说明1检索流程1.1构建请求request和源source1.2查询建造器queryBuilder构建1.3请求建造器加入到请求源请求源加入到请求中1.4结果hits和highlight分析 2各种查询构造器说明2.1MatchQueryBuilder2.2RegexQueryBuilder2.3IdsQueryBuilder2.4MatchPhraseQueryBuilder2.5MatchPhrasePrefixQueryBuilder2.6MultiMatchQueryBuilder2.7TermQueryBuilder2.8FuzzyQueryBuilder2.9RangeQueryBuilder2.10WildcardQueryBuilder2.11BoolQueryBuilder2.12其他的QueryBuilder 3聚合建造器说明3.1构造聚合条件3.2将聚合条件交给builderbuilder交给源封装request3.3结果解析和处理 三repo template1ElasticsearchRepository1.1repo介绍1.2自定义方法命名规范 2ElasticsearchRestTemplate2.1template介绍2.2template查询操作2.2.1入参特殊情况处理【包括异常入参】2.2.2构建查询条件Criteria2.2.3构建高亮条件2.2.4查询也可能是其他的方法不一定是search)2.2.5结果封装 2.3创建索引和删除索引 3使用实例 Elasticsearch是面向文档型数据库一条数据在这里就是一个文档用
JSON作为文档序列化的格式 {name : John,sex : Male,age : 25,birthDate: 1990/05/01,about : I love to go rock climbing,interests: [ sports, music ]
}ES和传统的关系型数据库相关术语对比如下
关系型数据库ES数据库(Database)索引(index)表(table)类型(Type)[es6.0.0废弃]行(row)文档(document)列(column)字段(field)表结构(schema)映射(mapping)索引反向索引SQL查询DSLSelect * from tableGet http://…update table set…Put http://…deleteDelete http://…
一前置工作
1项目搭建和依赖导入 spring data - spring-boot-starter-data-elasticsearch - repo templatetransport elasticsearch-rest-high-level-client - client dependencies!-- spring boot启动器 --!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependency!-- spring boot web启动器 --!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- spring boot 测试启动器 --!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- lombok --dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency!-- commons-lang3工具类 --dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-lang3/artifactId/dependency!-- spring data elasticsearch --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-elasticsearch/artifactId/dependency!-- es 高阶客户端 --dependencygroupIdorg.elasticsearch.client/groupIdartifactIdelasticsearch-rest-high-level-client/artifactId/dependency!-- es 低阶客户端 --dependencygroupIdorg.elasticsearch.client/groupIdartifactIdtransport/artifactId/dependency/dependenciesspring:elasticsearch:rest:uris: http://localhost:9200,http://localhost:9201,http://localhost:9202 # 集群地址connection-timeout: 5s # 连接超时时间read-timeout: 30s # 读取超时时间max-connections: 100 # 最大连接数max-connections-per-route: 20 # 每个路由的最大连接数password: es_password2客户端连接相关构建
package com.cui.es_demo.config;import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;import java.util.Arrays;/*** author cui haida* 2025/1/29*/Configuration
ConfigurationProperties(prefix spring.elasticsearch.rest)
public class ElasticSearchClientConfig {/*** 集群地址*/private String uris;/*** 用户名和密码*/private String username;private String password;Beanpublic RestHighLevelClient restHighLevelClient() {// 创建连接, 指定url和用户名密码ClientConfiguration build ClientConfiguration.builder().connectedTo(uris).withBasicAuth(username, password).build();return RestClients.create(build).rest();}
}3实体类相关注解配置说明
package com.cui.es_demo.entity;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.time.LocalDateTime;/*** 实体类相关注解介绍* - Document指定索引库的名称以及分片数、副本数、刷新间隔、是否允许创建索引* - Id指定主键* - Field指定字段的名称以及字段的分词器以及字段的存储类型以及字段的分词器* - JsonFormat指定日期格式注意日期类型字段不要用java.util.Date类型要用java.time.LocalDate或java.time.LocalDateTime类型* author cui haida* 2025/1/30*/
NoArgsConstructor
AllArgsConstructor
Data
// 指定对应的索引名称, 主分片数 3 副本数 1, 刷新间隔 1s, 允许创建索引
Document(indexName person, shards 3, replicas 1, refreshInterval 1s, createIndex true)
public class Person {Idprivate String id;Field(type FieldType.Keyword)private String name;Field(type FieldType.Keyword)private String age;// text类型并使用IK最粗粒度的分词器// 检索时的分词器采用的是最细粒度的IK分词器Field(type FieldType.Text, analyzer ik_smart, searchAnalyzer ik_max_word)private String address;// 注意日期格式的特殊处理// 指定格式化和时区Field(type FieldType.Date, format DateFormat.custom, pattern yyyy-MM-dd HH:mm:ss)JsonFormat(shape JsonFormat.Shape.STRING, pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)private LocalDateTime createTime;
}二客户端client相关操作说明 对索引相关的操作都不推荐在这里执行建议直接使用Kibana 文档的更新和插入操作建议使用repo template方式 这里只介绍查询相关操作 1检索流程
1.1构建请求request和源source
// 声明查询请求对象
SearchRequest client new SearchRequest();
// 指定使用的索引数据库
client.indices(index);// 声明searchSourceBuilder源对象
SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();// 源对象其他常见设置
// 1. 排序和浅显分页
// from size 默认都是-1也就是有多少显示多少
// from-size浅分页适合数据量不大的情况官网推荐是数据少于10000条可以跳码进行查询
searchSourceBuilder.from(0);
searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序
searchSourceBuilder.sort(age, SortOrder.DESC).sort(name, SortOrder.ASC);1.2查询建造器queryBuilder构建
// 以boolean为例
BoolQueryBuilder boolQueryBuilder QueryBuilders.boolQuery().must(QueryBuilders.termQuery(name.keyword, 王五)).mustNot(QueryBuilders.idsQuery().addIds(1, 2, 3))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery(name, 张三))).boost(2.0f);
// 加入对应的builder到searchSourceBuilder中
searchSourceBuilder.query(boolQueryBuilder);// 如果有高亮设置
HighlightBuilder highlightBuilder new HighlightBuilder();
highlightBuilder.preTags(font stylecolor: red);
highlightBuilder.postTags(/font);
highlightBuilder.field(name).field(age);
//同一字段中存在多个高亮值 设置都高亮
highlightBuilder.requireFieldMatch(true);// 高亮属性中加入高亮配置器
searchSourceBuilder.highlighter(highlightBuilder);1.3请求建造器加入到请求源请求源加入到请求中
searchSourceBuilder.query(xxxQueryBuilder);
// 封装request指定要request的索引index指定source - searchSourceBuilder
SearchRequest request new SearchRequest(index).source(searchSourceBuilder);
// 通过client.search获取响应
SearchResponse response client.search(request, RequestOptions.DEFAULT);1.4结果hits和highlight分析
ListMapString, Object list new ArrayList();
SearchResponse searchResponse this.client.search(client, RequestOptions.DEFAULT);
if (searchResponse null) {log.warn(没有返回值);return;
}// todo: 然后从这个searchResponse解析各种的值 - 根据返回结构
int failedShards searchResponse.getFailedShards();
System.out.println(失败的分片数 failedShards);
int successfulShards searchResponse.getSuccessfulShards();
System.out.println(成功的分片数 successfulShards);
RestStatus status searchResponse.status();
System.out.println(状态 status);//解析高亮数据
SearchHits hits searchResponse.getHits();
System.out.println(hits.getMaxScore());
for (SearchHit hit : hits) {System.out.println(fields: hit.getFields());System.out.println(index is hit.getIndex());System.out.println(document field is: hit.getDocumentFields());System.out.println(metadata field is: hit.getMetadataFields());System.out.println(score is: hit.getScore());System.out.println(-----------------);//原始数据不包含高亮的数据MapString, Object sourceMap hit.getSourceAsMap();//高亮数据拿到高亮字段nameMapString, HighlightField highlightFields hit.getHighlightFields();HighlightField highlightTitle highlightFields.get(name);//将原始数据的name替换if (highlightTitle ! null) {Text[] fragments highlightTitle.getFragments();if (fragments ! null fragments.length 0) {sourceMap.replace(name, fragments[0].toString());}}list.add(sourceMap);//循环将数据添加入列表
}
list.forEach(System.out::println);package com.cui.es_demo.client;import lombok.extern.slf4j.Slf4j;
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.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
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.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 客户端相关操作** author cui haida* 2025/1/30*/
Service
Slf4j
public class ClientDemo {// private final RestHighLevelClient client;public ClientDemo(RestHighLevelClient restHighLevelClient) {this.client restHighLevelClient;}/*** 主要介绍客户端查询操作*/public void searchTest(String[] args) {// 声明查询请求同时声明作用的索引数据库SearchRequest request new SearchRequest();request.indices(person);// 声明searchSourceBuilder源对象SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();// 源对象其他常见设置 // 1. 排序和浅显分页// from size 默认都是-1也就是有多少显示多少// from-size浅分页适合数据量不大的情况官网推荐是数据少于10000条可以跳码进行查询searchSourceBuilder.from(0);searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序searchSourceBuilder.sort(age, SortOrder.DESC).sort(name, SortOrder.ASC);// 构造查询建造器根据不同的业务需求进行不同的查询// 以boolean为例 BoolQueryBuilder boolQueryBuilder QueryBuilders.boolQuery().must(QueryBuilders.termQuery(name.keyword, 王五)).mustNot(QueryBuilders.idsQuery().addIds(1, 2, 3))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery(name, 张三))).boost(2.0f);// 请求构造器加入源中searchSourceBuilder.query(boolQueryBuilder);// 如果有高亮设置 HighlightBuilder highlightBuilder new HighlightBuilder();highlightBuilder.preTags(font stylecolor: red);highlightBuilder.postTags(/font);highlightBuilder.field(name).field(age);//同一字段中存在多个高亮值 设置都高亮highlightBuilder.requireFieldMatch(true);// 高亮属性中加入源中searchSourceBuilder.highlighter(highlightBuilder);// 进行查询// 1. 设置查询源(source - request)request.source(searchSourceBuilder);// 2. 通过search方法进行查询ListMapString, Object list new ArrayList();try {SearchResponse response client.search(request, RequestOptions.DEFAULT);// 结果处理System.out.println(response.getHits().getTotalHits() response.getHits().getTotalHits());// 然后从这个searchResponse解析各种的值 - 根据返回结构int failedShards response.getFailedShards();System.out.println(失败的分片数 failedShards);int successfulShards response.getSuccessfulShards();System.out.println(成功的分片数 successfulShards);RestStatus status response.status();System.out.println(状态 status);// 解析高亮数据SearchHits hits response.getHits();System.out.println(hits.getMaxScore());for (SearchHit hit : hits) {System.out.println(fields: hit.getFields());System.out.println(index is hit.getIndex());System.out.println(document field is: hit.getDocumentFields());System.out.println(metadata field is: hit.getMetadataFields());System.out.println(score is: hit.getScore());System.out.println(-----------------);// 原始数据不包含高亮的数据MapString, Object sourceMap hit.getSourceAsMap();// 高亮数据拿到高亮字段nameMapString, org.elasticsearch.search.fetch.subphase.highlight.HighlightField highlightFields hit.getHighlightFields();HighlightField highlightTitle highlightFields.get(name);// 将原始数据的name替换if (highlightTitle ! null) {Text[] fragments highlightTitle.getFragments();if (fragments ! null fragments.length 0) {sourceMap.replace(name, fragments[0].toString());}}// 循环将数据添加入列表list.add(sourceMap);}list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();}}
}2各种查询构造器说明
2.1MatchQueryBuilder
MatchQueryBuilder matchQueryBuilder QueryBuilders.matchQuery(name, 李四)// 李和四必须都出现在name字段中才可以, operator可以是OR.operator(Operator.AND)// 匹配度必须是 75%才行.minimumShouldMatch(75%)// 是否忽略数据类型转换异常.lenient(true);2.2RegexQueryBuilder
// 张姓开头的doc
RegexpQueryBuilder regexpQueryBuilder QueryBuilders.regexpQuery(name, 张*).caseInsensitive(true);2.3IdsQueryBuilder
String[] ids new String[]{1, 2, 3};
IdsQueryBuilder idsQueryBuilder QueryBuilders.idsQuery().addIds(ids);2.4MatchPhraseQueryBuilder
MatchPhraseQueryBuilder match QueryBuilders.matchPhraseQuery(name, 李四).slop(2);2.5MatchPhrasePrefixQueryBuilder
MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder QueryBuilders.matchPhrasePrefixQuery(name, 张).maxExpansions(1);2.6MultiMatchQueryBuilder
String[] fieldNames new String[]{name, age};
String searchText 老坛;
MultiMatchQueryBuilder multiMatchQueryBuilder QueryBuilders.multiMatchQuery(searchText, fieldNames)// 最多数量匹配.type(MultiMatchQueryBuilder.Type.MOST_FIELDS)// 使用and操作符和minimum_should_match参数来减少相关度低的文档数量.operator(Operator.AND);2.7TermQueryBuilder
TermQueryBuilder termQueryBuilder QueryBuilders.termQuery(name.keyword, 李四);
TermsQueryBuilder termsQueryBuilder QueryBuilders.termsQuery(name.keyword, 李四光, 李四);2.8FuzzyQueryBuilder
FuzzyQueryBuilder fuzzyQueryBuilder QueryBuilders.fuzzyQuery(name, 李四);2.9RangeQueryBuilder
RangeQueryBuilder ageRangeFilter QueryBuilders.rangeQuery(age)// greater than 12.gt(12)// less than 17.lt(17);// 默认是true包含头尾设置false去掉头尾
RangeQueryBuilder ageRangeFilter2 QueryBuilders.rangeQuery(age).from(12).to(17)// 不包含最后一个元素.includeLower(false)// 包含第一个元素.includeUpper(true);2.10WildcardQueryBuilder
// wildcard 通配符查询, 支持*匹配任何字符序列, 包括空避免*
String queryString Lc*dd;
WildcardQueryBuilder wildcardQueryBuilder QueryBuilders.wildcardQuery(name, queryString);2.11BoolQueryBuilder
BoolQueryBuilder boolQueryBuilder QueryBuilders.boolQuery().must(QueryBuilders.termQuery(name.keyword, 王五)).mustNot(QueryBuilders.idsQuery().addIds(1, 2, 3))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery(name, 张三))).boost(2.0f);
searchSourceBuilder.query(boolQueryBuilder);2.12其他的QueryBuilder
// 16: 其他查询
// moreLikeThisQuery: 实现基于内容推荐, 支持实现一句话相似文章查询
// percent_terms_to_match匹配项term的百分比默认是0.3
// min_term_freq一篇文档中一个词语至少出现次数小于这个值的词将被忽略默认是2
// max_query_terms一条查询语句中允许最多查询词语的个数默认是25
// stop_words设置停止词匹配时会忽略停止词
// min_doc_freq一个词语最少在多少篇文档中出现小于这个值的词会将被忽略默认是无限制
// max_doc_freq一个词语最多在多少篇文档中出现大于这个值的词会将被忽略默认是无限制
// min_word_len最小的词语长度默认是0
// max_word_len最多的词语长度默认无限制
// boost_terms设置词语权重默认是1
// boost设置查询权重默认是1
// analyzer设置使用的分词器默认是使用该字段指定的分词器
QueryBuilder queryBuilder QueryBuilders.moreLikeThisQuery(new String[]{王})// 一篇文档中一个词语至少出现次数小于这个值的词将被忽略默认是2.minTermFreq(1)// 一条查询语句中允许最多查询词语的个数默认是25.maxQueryTerms(3);// 查询条件
searchSourceBuilder.query(matchQueryBuilder)// 后置过滤器id, exists, term, range.postFilter(QueryBuilders.existsQuery(tag));3聚合建造器说明
3.1构造聚合条件
TermsAggregationBuilder aggregation AggregationBuilders.terms(等于的值).field(字段名称) // 进行分桶操作.subAggregation(AggregationBuilders.avg(桶的名字随便起的).field(根据xx字段分桶)) // 对每一个分桶进行子聚合3.2将聚合条件交给builderbuilder交给源封装request
// 声明源
SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();// 将查询建造器放入源将高亮信息放入源将聚合信息放入源
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.highlighter(highlightBuilder);
searchSourceBuilder.aggregation(aggregation);// 将源放入请求request
SearchRequest request new SearchRequest(index).source(searchSourceBuilder);// 进行查询
SearchResponse response client.search(request, RequestOptions.DEFAULT);3.3结果解析和处理
// 结果解析
ListObject ans handleResult(response);
// 结果处理
for (Object o : ans) {System.out.println(o.toString());
}
// todo: 结果解析封装/*** 聚合查询*/
public void aggTest() {// 声明查询请求同时声明作用的索引数据库SearchRequest request new SearchRequest();request.indices(person);// 声明searchSourceBuilder源对象SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();// 源对象其他常见设置 // 1. 排序和浅显分页// from size 默认都是-1也就是有多少显示多少// from-size浅分页适合数据量不大的情况官网推荐是数据少于10000条可以跳码进行查询searchSourceBuilder.from(0);searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序searchSourceBuilder.sort(age, SortOrder.DESC).sort(name, SortOrder.ASC);// 构造查询建造器根据不同的业务需求进行不同的查询// 以boolean为例 BoolQueryBuilder boolQueryBuilder QueryBuilders.boolQuery().must(QueryBuilders.termQuery(name.keyword, 王五)).mustNot(QueryBuilders.idsQuery().addIds(1, 2, 3))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery(name, 张三))).boost(2.0f);// 请求构造器加入源中searchSourceBuilder.query(boolQueryBuilder);// 如果有高亮设置 HighlightBuilder highlightBuilder new HighlightBuilder();highlightBuilder.preTags(font stylecolor: red);highlightBuilder.postTags(/font);highlightBuilder.field(name).field(age);//同一字段中存在多个高亮值 设置都高亮highlightBuilder.requireFieldMatch(true);// 高亮属性中加入源中searchSourceBuilder.highlighter(highlightBuilder);TermsAggregationBuilder aggregation // 进行分桶操作AggregationBuilders.terms(Jone).field(name)// 对每一个分桶进行子聚合.subAggregation(AggregationBuilders.avg(age_avg).field(age));// 将聚合结果放入源中searchSourceBuilder.aggregation(aggregation);// 进行查询// 1. 设置查询源(source - request)request.source(searchSourceBuilder);// 2. 通过search方法进行查询ListMapString, Object list new ArrayList();try {SearchResponse response client.search(request, RequestOptions.DEFAULT);// 结果处理System.out.println(response.getHits().getTotalHits() response.getHits().getTotalHits());// 然后从这个searchResponse解析各种的值 - 根据返回结构int failedShards response.getFailedShards();System.out.println(失败的分片数 failedShards);int successfulShards response.getSuccessfulShards();System.out.println(成功的分片数 successfulShards);RestStatus status response.status();System.out.println(状态 status);// 解析高亮数据SearchHits hits response.getHits();System.out.println(hits.getMaxScore());for (SearchHit hit : hits) {System.out.println(fields: hit.getFields());System.out.println(index is hit.getIndex());System.out.println(document field is: hit.getDocumentFields());System.out.println(metadata field is: hit.getMetadataFields());System.out.println(score is: hit.getScore());System.out.println(-----------------);// 原始数据不包含高亮的数据MapString, Object sourceMap hit.getSourceAsMap();// 高亮数据拿到高亮字段nameMapString, org.elasticsearch.search.fetch.subphase.highlight.HighlightField highlightFields hit.getHighlightFields();HighlightField highlightTitle highlightFields.get(name);// 将原始数据的name替换if (highlightTitle ! null) {Text[] fragments highlightTitle.getFragments();if (fragments ! null fragments.length 0) {sourceMap.replace(name, fragments[0].toString());}}// 循环将数据添加入列表list.add(sourceMap);}list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();}
}三repo template
1ElasticsearchRepository
1.1repo介绍
ElasticsearchRepository接口封装了Document的CRUD操作我们直接定义接口继承它即可。 ElasticsearchRepository接口的源码 package org.springframework.data.elasticsearch.repository;import java.io.Serializable;import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.repository.NoRepositoryBean;NoRepositoryBean
public interface ElasticsearchRepositoryT, ID extends Serializable extends ElasticsearchCrudRepositoryT, ID {S extends T S index(S entity);IterableT search(QueryBuilder query);PageT search(QueryBuilder query, Pageable pageable);PageT search(SearchQuery searchQuery);PageT searchSimilar(T entity, String[] fields, Pageable pageable);void refresh();ClassT getEntityClass();
}CrudRepository源码 package org.springframework.data.repository;import java.util.Optional;/*** Interface for generic CRUD operations on a repository for a specific type.** author Oliver Gierke* author Eberhard Wolff*/
NoRepositoryBean
public interface CrudRepositoryT, ID extends RepositoryT, ID {// 保存相关S extends T S save(S entity);S extends T IterableS saveAll(IterableS entities);// 查找相关OptionalT findById(ID id);boolean existsById(ID id);IterableT findAll();IterableT findAllById(IterableID ids);// 计数long count();// delete相关void deleteById(ID id);void delete(T entity);void deleteAll(Iterable? extends T entities);void deleteAll();
}PagingAndSortingRepository源码 NoRepositoryBean
public interface PagingAndSortingRepositoryT, ID extends RepositoryT, ID {IterableT findAll(Sort sort);PageT findAll(Pageable pageable);
}可见ElasticsearchRepository为我们封装好了很多常用的方法我们可以在使用的时候直接调用这些方法进行操作
1.2自定义方法命名规范
和所有的Spring Data一样都支持指定格式的函数声明对于这些函数不用写具体的实现而是可以直接调用
SpringData会通过动态代理的方式帮我们生成基础的CRUD方法 自定义方法命名规范 findBy[fieldName]根据指定的单个条件进行等值查询findBy[fieldName]And[fieldName]And[...]根据指定的多条件进行and查询findBy[fieldName]Or[fieldName]Or[...]根据指定的多条件进行or查询findBy[fieldName]Equals根据指定的单个条件进行等值查询findBy[fieldName]In对指定的单个字段进行in查询入参为一个列表findBy[fieldName]Like对指定的单个字段进行like模糊查询findBy[fieldName]NotNull查询指定字段不为空的数据findBy[fieldName]GreaterThan对指定的单个字段进行]范围查询findBy[fieldName]GreaterThanEqual对指定的单个字段进行]范围查询findBy[fieldName]LessThan对指定的单个字段进行[范围查询findBy[fieldName]LessThanEqual对指定的单个字段进行[范围查询Page[...] findBy[...]根据指定的条件进行分页查询countBy[fieldName]根据指定的条件字段进行计数统计findTop[n]By[fieldName]根据指定字段做等值查询并返回前n条数据findBy[fieldName]Between根据指定字段进行between范围查询findDistinctBy[fieldName]根据指定的单个条件进行去重查询findFirstBy[fieldName]根据指定的单个条件进行等值查询只返回满足条件的第一个数据findBy[fieldName1]OrderBy[fieldName2]根据第一个字段做等值查询并根据第二个字段做排序 各种方法名开头含义 以get、find、read、query、stream开头代表是查询数据的方法以count开头代表是计数统计的方法以delete、remove开头代表是删除数据的方法以exists开头代表是判断是否存在的方法以search开头代表是全文搜索的方法以update开头代表是修改数据的方法 方法名开头后面跟的关键字含义以find开头的方法为例 By表示当前方法生成的查询语句会根据By后面的逻辑来组成FirstBy表示当前方法生成的语句只会返回符合条件的第一条数据DistinctBy表示当前方法生成的语句会对符合条件的数据去重TopBy表示当前方法生成的语句只会返回符合条件的前N条数据[实体类名称]By表示当前方法生成的语句只会返回一条数据[实体类名称]sBy表示当前方法生成的语句会返回多条数据AllBy表示当前方法生成的语句会返回多条或所有数据DistinctFirstBy表示当前方法生成的语句只会返回去重后的第一条数据DistinctTopBy表示当前方法生成的语句只会返回去重后的前N条数据 方法名开头跟的关键字之后跟的是字段名[fieldName], 字段名称后面可以接的关键字如下 在这些关键字之后都是跟具体的字段名实体类的属性名字段名称后面可以接的关键字如下同样以find为例
Or表示当前查询方法有多个条件多个条件之间为“或者”关系And表示当前查询方法有多个条件多个条件之间为“并且”关系OrderBy表示当前查询会涉及到排序后面需要跟一个排序字段Between表示当前方法为between范围查询GreaterThan表示当前方法为查询GreaterThanEqual表示当前方法为查询LessThan表示当前方法为 查询LessThanEqual表示当前方法为查询After和GreaterThan差不多相当于查询指定数值之后的数据Before和LessThan差不多查询指定条件之前的数据Containing查询某字段中包含指定字符的数据Empty表示当前方法会查询指定字段为空的数据与之含义类似的还有Null、ExistsEquals表示当前方法会根据指定字段做等值查询Is和Equals差不多In表示当前方法为in多值匹配查询Like表示当前方法为like模糊查询Not可以和上述大多数关键字组合带有Not的则含义相反如NotEmpty表示不为空 2ElasticsearchRestTemplate
2.1template介绍 template提供了众多模板方法只要我们编写好对应的条件然后调用模板方法即可 2.2template查询操作
下面是操作流程【查询为例】
2.2.1入参特殊情况处理【包括异常入参】
2.2.2构建查询条件Criteria
// 1构建查询条件
Criteria criteria new Criteria()// 条件一xxxx.and(new Criteria(字段名称).contains(条件中的内容))// 条件二xxx.and(new Criteria(字段名称).is(条件中的内容));
// 2封装进入到CriteriaQuery
CriteriaQuery filter new CriteriaQuery(criteria);
// 3设置分页信息【可能没有看业务】
CriteriaQuery criteriaQuery filter.setPageable(PageRequest.of(pageIndex, pageSize));2.2.3构建高亮条件
// 1声明高亮构造器对象
HighlightBuilder highlightBuilder new HighlightBuilder();
// todo: 设置高亮领域
// todo: 前置后置标签// 2构建高亮query
HighlightQuery highlightQuery new HighlightQuery(highlightBuilder);
// 3将高亮query封装进criteriaQuery中
criteriaQuery.setHighlightQuery(highlightQuery);2.2.4查询也可能是其他的方法不一定是search)
elasticsearchRestTemplate.search(查询条件返回信息的实体.class);2.2.5结果封装
// 对结果进行封装
2.3创建索引和删除索引
对于索引的创建和删除建议还是直接使用Kibana进行相关的操作如果要使用template可以输入对应的索引名称进行创建
template会到实体中找到Document(indexName xxx)对应的信息根据这个注解的配置拿到settings对应的信息配置
然后根据实体类标注的字段类型和分析器类型创建对应的mappings对应的信息配置
package com.example.es_demo.pojo;import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.time.LocalDateTime;/*** p* 功能描述实体类* /p** author cui haida* date 2024/01/28/8:20*/
NoArgsConstructor
AllArgsConstructor
Data
Document(indexName mytest) // 指定对应的索引名称
public class MyTest {IdJSONField(serialize false)private String id;Field(type FieldType.Keyword)private String name;Field(type FieldType.Keyword)private String age;// text类型并使用IK最粗粒度的分词器检索时的分词器采用的是最细粒度的IK分词器Field(type FieldType.Text, analyzer ik_smart, searchAnalyzer ik_max_word)private String address;// 注意日期格式的特殊处理Field(type FieldType.Date, format DateFormat.custom, pattern yyyy-MM-dd HH:mm:ss)JsonFormat(shape JsonFormat.Shape.STRING, pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)private LocalDateTime createTime;
}public String create(String indexName) {IndexOperations indexOperations elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName));if (indexOperations.exists()) {return 索引已存在;}indexOperations.create();return 索引创建成功;
}public String delete(String indexName) {IndexOperations indexOperations elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName));indexOperations.delete();return 索引删除成功;
}3使用实例
package com.cui.es_demo.client;import com.cui.es_demo.client.repo.PersonRepository;
import com.cui.es_demo.entity.PageResponse;
import com.cui.es_demo.entity.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** author cui haida* 2025/1/30*/
Service
Slf4j
public class TemplateDemo {// es repo - 这里提供了Spring Data的基本方法可以直接使用AutowiredPersonRepository personRepository;// templateAutowiredElasticsearchRestTemplate elasticsearchRestTemplate;// repo测试 // 就是直接调用对应的方法只要语法满足上面说的Spring Data语法就可以public void saveAll(ListPerson orders) {personRepository.saveAll(orders);}public void deleteById(Integer id) {personRepository.deleteById(id);}public void updateById(Person person) {personRepository.save(person);}public Person findById(Integer id) {return (Person) personRepository.findById(id).orElse(null);}public PageResponsePerson findAll(Integer pageIndex, Integer pageSize) {PagePerson page personRepository.findAll(PageRequest.of(pageIndex, pageSize));PageResponsePerson pageResponse new PageResponsePerson();pageResponse.setTotal(page.getTotalElements());pageResponse.setResult(page.getContent());return pageResponse;}// template测试 // 1构造查询条件// 2调用template.xxx(条件)// 3结果处理封装public PageResponsePerson findList(Person person, Integer pageIndex, Integer pageSize) {// 1构建查询条件Criteria criteria new Criteria()// 条件一文档中的orderDesc字段要包含查询条件的orderDesc字段.and(new Criteria(orderDesc).contains(person.getAddress()))// 条件二文档中的orderNo字段要等于查询条件的orderNo字段.and(new Criteria(orderNo).is(person.getId()));// 封装进入到CriteriaQueryCriteriaQuery filter new CriteriaQuery(criteria);// 设置分页信息CriteriaQuery criteriaQuery filter.setPageable(PageRequest.of(pageIndex, pageSize));// 2查询 - elasticsearchRestTemplate.search(查询条件返回信息的实体.class)SearchHitsPerson searchHits elasticsearchRestTemplate.search(criteriaQuery, Person.class);// 3结果封装ListPerson result searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());PageResponsePerson pageResponse new PageResponse();pageResponse.setTotal(searchHits.getTotalHits());pageResponse.setResult(result);return pageResponse;}public PageResponsePerson findHighlight(Person person, Integer pageIndex, Integer pageSize) {// 0特殊情况的处理if (person null) {PageResponsePerson pageResponse new PageResponsePerson();pageResponse.setTotal(0L);pageResponse.setResult(new ArrayList());return pageResponse;}// 1构建查询条件Criteria criteria new Criteria()// 条件一文档中的orderDesc字段要包含查询条件的orderDesc字段.and(new Criteria(id).contains(person.getId()))// 条件二文档中的orderNo字段要等于查询条件的orderNo字段.and(new Criteria(address).is(person.getAddress()));// 封装进入到CriteriaQueryCriteriaQuery filter new CriteriaQuery(criteria);// 设置分页信息CriteriaQuery criteriaQuery filter.setPageable(PageRequest.of(pageIndex, pageSize));// 3查询 - elasticsearchRestTemplate.search(查询条件返回信息的实体.class)SearchHitsPerson searchHits elasticsearchRestTemplate.search(criteriaQuery, Person.class);// 4结果封装// 对每一个都设置对应的高亮字段信息ListPerson result searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());// 封装页码信息PageResponsePerson pageResponse new PageResponse();pageResponse.setTotal(searchHits.getTotalHits());pageResponse.setResult(result);return pageResponse;}
}