Spring boot项目配置Elasticsearch 引入POM依赖、配置yml、创建定义索引的实体类、创建Setting文件、利用Elasticsearch提供的API进行增删改查、根据属性名进行组合检索、自定义检索
Elasticsearch 版本: 7.6.2.
Spring boot版本: 2.3.0.RELEASE
快速跳转
引入POM依赖
配置yml
创建定义索引的实体类
创建Setting文件
利用xxxRequest,xxxReponseI进行增删改查
利用Spring Data 提供的接口进行增删改查
利用Spring data JPA,根据属性名进行组合检索
自定义检索
引入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);
}
}
}
RequestAndResponseApiTest
利用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);
}
PagingAndSortingRepository
在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();
}
CrudRepository
我们的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());
}
}
MyUserDaoTest
利用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;
}
}
ComplexQuery
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;
}
}