Spring Boot 配置 ElasticSearch7
Elasticsearch 版本: 7.6.2.
Spring boot版本: 2.3.0.RELEASE
快速跳转
利用xxxRequest,xxxReponseI进行增删改查
引入POM依赖
方式1:
Spring boot 项目创建时选择NoSQL -- Spring dataElasticSearch
方式2 : 在POM处引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
由于本人没有7.17.4 版本对应的ik分词器和拼音分词器,所以降了spring boot的版本为 2.3.0.RELEASE, 对应的ES版本降为7.6.2
配置yml
server:
port: 8888
spring:
elasticsearch:
uris: http://localhost:9200
username:
password:
创建定义索引的实体类
@Data @Document(indexName = "user") @Setting(settingPath = "/setting/pinyin.analyzer.json") public class User { @Field private String id; @Field(type = FieldType.Text,name = "name") private String name; @Field(name = "desc",type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String desc; @Field(name = "age",type = FieldType.Integer) private Integer age; @Field(name = "phone",type = FieldType.Keyword) private String phone; @Field(name = "like", type = FieldType.Text, analyzer = "pinyin_analyzer_ik_max", searchAnalyzer = "ik_max_word") private String like; }
@Document注解用来定义索引名称、索引分片、索引副本,是否自动创建索引等,这个只需定义一个索引名称,其余均默认即可。
@Setting注解可以引入自定义的index.setting, 如果有自定义的分词器,可以通过此注解引入,如果没有,这个注解可忽略。
@Field注解用来定义字段信息,需重点关注的为name, type, analyzer, search_analyzer这四项。
创建Setting文件
如果有自定义的setting内容,则需在resource文件夹下存放setting.json, 即@Setting注解对应的路径
利用xxxRequestApi + xxxResponseApi进行增删改查
import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; import org.elasticsearch.client.indices.GetIndexRequest; import org.elasticsearch.client.indices.GetIndexResponse; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.ParsedMax; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortOrder; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; import java.util.List; @SpringBootTest @Slf4j public class OriginalApiTest { @Test @SneakyThrows void originalApiTest() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("localhost:9200") .withBasicAuth("elastic_name", "elastic_password") .build(); //连接客户端 RestHighLevelClient esClient = RestClients.create(clientConfiguration).rest(); String indexName = "client-test"; //创建索引 createIndex(esClient,indexName); //查询索引 getIndex(esClient,indexName); //文档操作 documentOptions(esClient,indexName); //文档批量操作 documentBatchOptions(esClient,indexName); //组合查询 complexQuery(esClient,indexName); //删除索引 deleteIndex(esClient,indexName); //关闭索引 esClient.close(); } private static void complexQuery(RestHighLevelClient esClient, String indexName) throws Exception { //全量查询 queryAll(esClient,indexName); //条件查询 queryCondition(esClient,indexName); //分页查询、排序、过滤、高亮 queryPageAndSort(esClient,indexName); //组合查询 queryBool(esClient,indexName); //范围查询 queryRange(esClient,indexName); //聚合查询--最大值 queryAggMax(esClient,indexName); //聚合查询--分组 queryAggTermGroup(esClient,indexName); } private static void queryAggTermGroup(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); AggregationBuilder aggregationBuilder = AggregationBuilders.terms("ageGroup").field("age"); sourceBuilder.aggregation(aggregationBuilder); searchRequest.source(sourceBuilder); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); ParsedLongTerms ageGroup = (ParsedLongTerms)searchResponse.getAggregations().getAsMap().get("ageGroup"); List<? extends Terms.Bucket> buckets = ageGroup.getBuckets(); for (Terms.Bucket item : buckets) { log.info("age:{}, count:{}",item.getKey(),item.getDocCount()); } } private static void queryAggMax(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); AggregationBuilder aggregationBuilder = AggregationBuilders.max("maxAge").field("age"); sourceBuilder.aggregation(aggregationBuilder); searchRequest.source(sourceBuilder); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); List<Aggregation> aggregations = searchResponse.getAggregations().asList(); for (Aggregation agg : aggregations) { ParsedMax ageMax = (ParsedMax)agg; log.info("ageMax:{}",ageMax.getValue()); } } private static void queryRange(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gt(10).lt(19); sourceBuilder.query(rangeQueryBuilder); searchRequest.source(sourceBuilder); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); print("queryRange",searchResponse.getHits()); } private static void queryBool(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(QueryBuilders.matchQuery("age","30"));//should\must\must_not boolQueryBuilder.must(QueryBuilders.matchQuery("sex","男")); sourceBuilder.query(boolQueryBuilder); searchRequest.source(sourceBuilder); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); print("queryBool",searchResponse.getHits()); } private static void queryPageAndSort(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); String[] ec = new String[]{"sex"}; String[] ic = new String[]{"name","age"}; SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) .from(0).size(5) .fetchSource(ic,ec) .highlighter(new HighlightBuilder().field("name")) .sort("age", SortOrder.DESC); searchRequest.source(sourceBuilder); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); print("queryPageAndSort",searchResponse.getHits()); } private static void queryCondition(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.termQuery("age",18))); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); print("queryCondition",searchResponse.getHits()); } private static void queryAll(RestHighLevelClient esClient, String indexName) throws Exception{ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())); SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT); print("queryAll", searchResponse.getHits()); } private static void print(String desc, SearchHits hits) { for (SearchHit hit : hits) { log.info(desc +":{}" , hit.getSourceAsString()); } } private void documentBatchOptions(RestHighLevelClient esClient, String indexName) throws Exception{ //批量新增 bulkAdd(esClient,indexName); //批量删除 bulkDelete(esClient,indexName); } private void bulkDelete(RestHighLevelClient esClient, String indexName) throws Exception{ BulkRequest bulkDeleteRequest = new BulkRequest(); bulkDeleteRequest.add(new DeleteRequest(indexName).id("1001")); bulkDeleteRequest.add(new DeleteRequest(indexName).id("1002")); bulkDeleteRequest.add(new DeleteRequest(indexName).id("1003")); BulkResponse deleteResponse = esClient.bulk(bulkDeleteRequest, RequestOptions.DEFAULT); log.info("批量删除状态为:{}",deleteResponse.status()); //查询 queryAll(esClient,indexName); } private void bulkAdd(RestHighLevelClient esClient, String indexName) throws Exception{ //批量新增 BulkRequest bulkRequest = new BulkRequest(); bulkRequest.add(new IndexRequest(indexName).id("1001").source(XContentType.JSON,"name","张三","age",30)); bulkRequest.add(new IndexRequest(indexName).id("1002").source(XContentType.JSON,"name","李四","age",40)); bulkRequest.add(new IndexRequest(indexName).id("1003").source(XContentType.JSON,"name","王五","age",50)); bulkRequest.add(new IndexRequest(indexName).id("1004").source(XContentType.JSON,"name","张龙","age",18)); bulkRequest.add(new IndexRequest(indexName).id("1005").source(XContentType.JSON,"name","赵虎","age",19)); bulkRequest.add(new IndexRequest(indexName).id("1006").source(XContentType.JSON,"name","王朝","age",18)); bulkRequest.add(new IndexRequest(indexName).id("1007").source(XContentType.JSON,"name","马汉","age",19)); BulkResponse bulkResponse = esClient.bulk(bulkRequest, RequestOptions.DEFAULT); log.info("批量添加结果:{}",bulkResponse.status()); //查询 queryAll(esClient,indexName); } private void documentOptions(RestHighLevelClient esClient, String indexName) throws Exception{ User user = new User(); user.setId(1001); user.setName("唐僧"); user.setAge(18); user.setDesc("三藏,御弟,金蝉子"); user.setLike("西天取经,讲经说法"); user.setPhone("13453345678"); insertDoc(esClient,indexName,user); user.setName("唐僧2"); updateDoc(esClient,indexName,user); searchDoc(esClient, indexName, user.getId().toString()); deleteDoc(esClient,indexName,user.getId().toString()); searchDoc(esClient, indexName, user.getId().toString()); } private void searchDoc(RestHighLevelClient esClient, String indexName, String id) throws Exception{ //查询 GetRequest getRequest = new GetRequest(); getRequest.index(indexName).id(id); GetResponse getResponse = esClient.get(getRequest, RequestOptions.DEFAULT); log.info("查询id为{}的数据,结果为:{}",id,getResponse.getSourceAsString()); } private void deleteDoc(RestHighLevelClient esClient, String indexName, String id) throws Exception{ //删除 DeleteRequest deleteRequest = new DeleteRequest(); deleteRequest.index(indexName).id(id); DeleteResponse delete = esClient.delete(deleteRequest, RequestOptions.DEFAULT); System.out.println(delete.toString()); } private void updateDoc(RestHighLevelClient esClient, String indexName, User user) throws Exception{ //更新 UpdateRequest updateRequest = new UpdateRequest(); updateRequest.index(indexName).id(user.getId().toString()); updateRequest.doc(XContentType.JSON,"sex","女"); UpdateResponse updateResponse = esClient.update(updateRequest, RequestOptions.DEFAULT); log.info("更新文档:{}", updateResponse.getResult()); } private void insertDoc(RestHighLevelClient esClient, String indexName, User user) throws Exception{ //新增 IndexRequest indexRequest = new IndexRequest(); indexRequest.index(indexName).id(user.getId().toString()); //向ES插入数据需转换成json格式 ObjectMapper mapper = new ObjectMapper(); String source = mapper.writeValueAsString(user); indexRequest.source(source, XContentType.JSON); IndexResponse indexReponse = esClient.index(indexRequest, RequestOptions.DEFAULT); log.info("新增文档:{}",indexReponse.getResult()); } private void deleteIndex(RestHighLevelClient esClient, String indexName) throws Exception{ //删除索引 DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName); AcknowledgedResponse deleteRespose = esClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT); log.info("删除索引状态:{}" , deleteRespose); } private void getIndex(RestHighLevelClient esClient, String indexName) throws Exception{ //查询索引 GetIndexRequest getIndexRequest = new GetIndexRequest(indexName); GetIndexResponse getIndexResponse = esClient.indices().get(getIndexRequest, RequestOptions.DEFAULT); log.info("索引别名:{}",getIndexResponse.getAliases()); log.info("索引Mapping:{}",getIndexResponse.getMappings()); log.info("索引Setting:{}",getIndexResponse.getSettings()); } private void createIndex(RestHighLevelClient esClient,String indexName) throws Exception{ //创建索引 CreateIndexRequest request = new CreateIndexRequest(indexName); boolean exists = esClient.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT); if (!exists) { CreateIndexResponse response = esClient.indices().create(request, RequestOptions.DEFAULT); //响应状态 boolean acknowledged = response.isAcknowledged(); log.info("创建索引状态:{}" , acknowledged); } else { log.info("索引{}已存在",indexName); } } }
利用Spring Data 提供的接口进行增删改查
一个比较草率的图
PagingAndSorting中,已经为我们定义好了两个Find方法
@NoRepositoryBean public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> { /** * Returns all entities sorted by the given options. * * @param sort * @return all entities sorted by the given options */ Iterable<T> findAll(Sort sort); /** * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object. * * @param pageable * @return a page of entities */ Page<T> findAll(Pageable pageable); }
在CurdRepository中,为我们定义了一批基础的增删改查方法
@NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { /** * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the * entity instance completely. * * @param entity must not be {@literal null}. * @return the saved entity; will never be {@literal null}. * @throws IllegalArgumentException in case the given {@literal entity} is {@literal null}. */ <S extends T> S save(S entity); /** * Saves all given entities. * * @param entities must not be {@literal null} nor must it contain {@literal null}. * @return the saved entities; will never be {@literal null}. The returned {@literal Iterable} will have the same size * as the {@literal Iterable} passed as an argument. * @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is * {@literal null}. */ <S extends T> Iterable<S> saveAll(Iterable<S> entities); /** * Retrieves an entity by its id. * * @param id must not be {@literal null}. * @return the entity with the given id or {@literal Optional#empty()} if none found. * @throws IllegalArgumentException if {@literal id} is {@literal null}. */ Optional<T> findById(ID id); /** * Returns whether an entity with the given id exists. * * @param id must not be {@literal null}. * @return {@literal true} if an entity with the given id exists, {@literal false} otherwise. * @throws IllegalArgumentException if {@literal id} is {@literal null}. */ boolean existsById(ID id); /** * Returns all instances of the type. * * @return all entities */ Iterable<T> findAll(); /** * Returns all instances of the type {@code T} with the given IDs. * <p> * If some or all ids are not found, no entities are returned for these IDs. * <p> * Note that the order of elements in the result is not guaranteed. * * @param ids must not be {@literal null} nor contain any {@literal null} values. * @return guaranteed to be not {@literal null}. The size can be equal or less than the number of given * {@literal ids}. * @throws IllegalArgumentException in case the given {@link Iterable ids} or one of its items is {@literal null}. */ Iterable<T> findAllById(Iterable<ID> ids); /** * Returns the number of entities available. * * @return the number of entities. */ long count(); /** * Deletes the entity with the given id. * * @param id must not be {@literal null}. * @throws IllegalArgumentException in case the given {@literal id} is {@literal null} */ void deleteById(ID id); /** * Deletes a given entity. * * @param entity must not be {@literal null}. * @throws IllegalArgumentException in case the given entity is {@literal null}. */ void delete(T entity); /** * Deletes the given entities. * * @param entities must not be {@literal null}. Must not contain {@literal null} elements. * @throws IllegalArgumentException in case the given {@literal entities} or one of its entities is {@literal null}. */ void deleteAll(Iterable<? extends T> entities); /** * Deletes all entities managed by the repository. */ void deleteAll(); }
我们的MyUserDao只需要集成PagingAndSortingRepository就可以直接使用上述接口中的方法。
创建UserDao.java
import org.springframework.data.repository.PagingAndSortingRepository; public interface MyUserDao extends PagingAndSortingRepository<User,Integer> { }
测试
import com.wsz.example.elasticsearch.entity.index.User; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; import java.util.ArrayList; import java.util.List; import java.util.Optional; @SpringBootTest @Slf4j class MyUserDaoTest { @Autowired private MyUserDao myUserDao; @Test void getAll(){ Iterable<User> all = myUserDao.findAll(); AggregatedPageImpl<User> page = (AggregatedPageImpl<User>)all; log.info("content:{}",page.getContent()); } @Test void getAllByIds(){ List<Integer> ids = new ArrayList<>(); ids.add(1001); ids.add(1002); ids.add(1003); Iterable<User> all = myUserDao.findAllById(ids); log.info("content:{}",all.toString()); } @Test void count(){ long count = myUserDao.count(); log.info(count+""); } @Test void saveOneEntity(){ User user = new User(); user.setId(1); user.setName("唐僧"); user.setAge(18); user.setDesc("三藏,御弟,金蝉子"); user.setLike("西天取经,讲经说法"); user.setPhone("13453345678"); User save = myUserDao.save(user); System.out.println(save.toString()); } @Test void saveAll(){ User user = new User(); user.setId(1002); user.setName("孙悟空"); user.setAge(1500); user.setDesc("孙行者,猴子,猴哥,斗战胜佛"); user.setLike("吃桃,打妖怪"); user.setPhone("123456789"); User user2 = new User(); user2.setId(1003); user2.setName("猪悟能"); user2.setAge(1500); user2.setDesc("净坛使者,老猪,八戒"); user2.setLike("美女,美食,美酒,分家"); user2.setPhone("123456789"); List<User> list = new ArrayList<>(); list.add(user); list.add(user2); Iterable<User> users = myUserDao.saveAll(list); log.info(users.toString()); } @Test void findById(){ Optional<User> byId = myUserDao.findById(1002); System.out.println(byId.toString()); } @Test void existsById(){ boolean exists = myUserDao.existsById(1002); log.info("1002:{}",exists); boolean exists2 = myUserDao.existsById(1001); log.info("1001:{}",exists2); } @Test void deleteById(){ myUserDao.deleteById(1002); System.out.println("1002"); } @Test void deleteByIds(){ User user2 = new User(); user2.setId(1003); user2.setName("猪悟能"); user2.setAge(1500); user2.setDesc("净坛使者,老猪,八戒"); user2.setLike("美女,美食,美酒,分家"); user2.setPhone("123456789"); List<User> list = new ArrayList<>(); list.add(user2); myUserDao.deleteAll(list); getAll(); } @Test void findAllWithSort(){ Sort sort = Sort.by(Sort.Direction.DESC, "age"); Iterable<User> all = myUserDao.findAll(sort); log.info(all.toString()); } @Test void findAllWithPage(){ Page<User> all = myUserDao.findAll(PageRequest.of(0, 3)); AggregatedPageImpl<User> page = (AggregatedPageImpl<User>)all; log.info(page.getContent().toString()); } }
利用Spring data JPA,根据属性名进行组合检索
显而易见,上述检索接口最多是以ID、排序、分页为参数进行检索,那如何定制化检索呢?
可以通过继承Repository<T,ID> 接口,通过属性名和操作命进行检索
创建MyUserSearchDao.java。
import org.springframework.data.repository.Repository; import java.util.Collection; import java.util.List; public interface MyUserSearchDao extends Repository<User,Integer> { //{"bool" : {"must" : {"field" : {"name" : "?"}}}} List<User> findByName(String name); //{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"age" : "?"}} ]}} List<User> findByNameAndAge(String name); //{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"age" : "?"}} ]}} List<User> findByNameOrAge(String name, Integer age); //{"bool" : {"must_not" : {"field" : {"name" : "?"}}}} List<User> findByNameNot(String name); //{"bool" : {"must" : {"range" : {"age" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} List<User> findByAgeBetween(Object from,Object to); //{"bool" : {"must" : {"range" : {"age" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} List<User> findByAgeLessThanEqual(double lessThan); List<User> findByAgeBefore(double lessThan); //{"bool" : {"must" : {"range" : {"age" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} List<User> findByAgeGreaterThanEqual(double greaterThan); List<User> findByAgeAfter(double greaterThan); //{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} List<User> findByNameLike(String name); //{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} List<User> findByNameStartingWith(String prefix); //{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} List<User> findByNameEndingWith(String suffix); //{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} List<User> findByNameContaining(String name); //{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} List<User> findByNameIn(Collection<String> names); //{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} List<User> findByNameNotIn(Collection<String> names); }
以上各接口均是通过以下组合而来。
自定义检索
如果上述按属性名称进行检索还不能满足需求,可以考虑自定义检索。
关于这个功能,目前还没找到和PagingAndSortingRepository一样,只一个接口继承就可以,不需要再继承PagingAndSortingRepository实现类的方法,如果各位有相关方法,麻烦评论里留个痕迹。
我先把结构晒出来,检索内容根据个人需要自行处理即可。
新建repository包,包中包含检索的接口类和实现类
CustomElasticsearchRepository.java
import com.wsz.example.elasticsearch.entity.query.ComplexQuery; import java.util.List; public interface CustomElasticsearchRepository<T,ID> { List<T> complexPage(ComplexQuery complexQuery); }
这里主要提供检索接口。Complex.java为包含检索参数的类。
@Data public class ComplexQuery { private List<String> queryFields; private List<String> highLightFields; private List<QueryCondition> query; private String sortField; private String orderType; private Integer pageIndex; private Integer pageSize; public Integer getPageIndex() { return (pageIndex-1) * this.pageSize; } }
AbstractCustomElasticsearchRepository.java
这里对检索功能进行具体的实现
import lombok.extern.slf4j.Slf4j; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.repository.support.SimpleElasticsearchRepository; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; @Slf4j public class AbstractCustomElasticsearchRepository<T,ID> implements CustomElasticsearchRepository<T,ID> { protected ElasticsearchOperations operations; protected Class<T> clazz; public AbstractCustomElasticsearchRepository(ElasticsearchOperations operations) { this.operations = operations; } @Override public List<T> complexPage(ComplexQuery complexQuery) { List<T> list = new ArrayList<>(); //todo 检索实现 return list; } //获取T的具体类 protected Class<T> getJavaType() { if (!isEntityClassSet()) { try { this.clazz = resolveReturnedClassFromGenericType(); } catch (Exception e) { throw new InvalidDataAccessApiUsageException("Unable to resolve EntityClass. Please use according setter!", e); } } return clazz; } private boolean isEntityClassSet() { return clazz != null; } @SuppressWarnings("unchecked") private Class<T> resolveReturnedClassFromGenericType() { ParameterizedType parameterizedType = resolveReturnedClassFromGenericType(getClass()); return (Class<T>) parameterizedType.getActualTypeArguments()[0]; } private ParameterizedType resolveReturnedClassFromGenericType(Class<?> clazz) { Object genericSuperclass = clazz.getGenericSuperclass(); if (genericSuperclass instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type rawtype = parameterizedType.getRawType(); if (SimpleElasticsearchRepository.class.equals(rawtype)) { return parameterizedType; } } return resolveReturnedClassFromGenericType(clazz.getSuperclass()); } }
可以通过具体接口和具体实现类的方式来调用。
public interface ICustomUserDao extends CustomElasticsearchRepository<User,Integer> { }
@Repository public class ICustomUserDaoImpl extends AbstractCustomElasticsearchRepository<User, Integer> implements ICustomUserDao { public ICustomUserDaoImpl(ElasticsearchOperations operations) { super(operations); this.clazz = User.class; } }