赞
踩
参考文章:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-document-index.html
该文章,主要是介绍elasticsearch7.x的rest java客户端。
定义一个request
IndexRequest request = new IndexRequest(索引名称);
request.id(文档id);
String jsonString = "{" +
"\"user\":\"kimchy\"," +
"\"postDate\":\"2013-01-30\"," +
"\"message\":\"trying out Elasticsearch\"" +
"}";
request.source(jsonString, XContentType.JSON);
12345678
执行request命令,分同步异步,一般使用同步
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
1
定义一个request
根据文档id进行删除
DeleteRequest request = new DeleteRequest(
索引名称,
文档id);
123
执行request命令
DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);
1
定义一个request
UpdateRequest request = new UpdateRequest(索引名称, 文档id);
String jsonString = "{" +
"\"updated\":\"2017-01-01\"," +
"\"reason\":\"daily update\"" +
"}";
request.doc(jsonString, XContentType.JSON);
123456
执行request命令
UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
1
查有非常多方式,elasticsearch作为一个开源搜索引擎,全文检索,结构化检索,数据分析,所以有很强大的搜索功能
定义一个request
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder);
1234
同样具有分页功能
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy"));
sourceBuilder.from(0);
sourceBuilder.size(5);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
12345
执行request命令
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
1
elasticsearch的版本要与客户端的版本一致
按照es官方文档描述elasticsearch-rest-client还需要依赖
The High Level Java REST Client depends on the following artifacts and their transitive dependencies: org.elasticsearch.client:elasticsearch-rest-client org.elasticsearch:elasticsearch 1234 <properties> <java.version>1.8</java.version> <elasticsearch>7.5.1</elasticsearch> </properties> <dependencies> <!--SpringBoot整合Spring Data Elasticsearch的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>${elasticsearch}</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>${elasticsearch}</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>${elasticsearch}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
实际上官方文档说 只引入一个依赖就可,实测可以。
注意看一下版本号下面的依赖,我用的最新的4.1.1
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.1.1</version>
</dependency>
下图整理出部分搜索相关的
参考大神文章:https://blog.csdn.net/weixin_42408648/article/details/108199320
单个匹配termQuery
//不分词查询 参数1: 字段名,参数2:字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词.
QueryBuilder queryBuilder=QueryBuilders.termQuery("fieldName", "fieldlValue");
//分词查询,采用默认的分词器
QueryBuilder queryBuilder2 = QueryBuilders.matchQuery("fieldName", "fieldlValue");
多个匹配
//不分词查询,参数1: 字段名,参数2:多个字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词.
QueryBuilder queryBuilder=QueryBuilders.termsQuery("fieldName", "fieldlValue1","fieldlValue2...");
//分词查询,采用默认的分词器
QueryBuilder queryBuilder= QueryBuilders.multiMatchQuery("fieldlValue", "fieldName1", "fieldName2", "fieldName3");
//匹配所有文件,相当于就没有设置查询条件
QueryBuilder queryBuilder=QueryBuilders.matchAllQuery();
1234567891011121314
//模糊查询常见的5个方法如下
//1.常用的字符串查询
QueryBuilders.queryStringQuery("fieldValue").field("fieldName");//左右模糊
//2.常用的用于推荐相似内容的查询
QueryBuilders.moreLikeThisQuery(new String[] {"fieldName"}).addLikeText("pipeidhua");//如果不指定filedName,则默认全部,常用在相似内容的推荐上
//3.前缀查询 如果字段没分词,就匹配整个字段前缀
QueryBuilders.prefixQuery("fieldName","fieldValue");
//4.fuzzy query:分词模糊查询,通过增加fuzziness模糊属性来查询,如能够匹配hotelName为tel前或后加一个字母的文档,fuzziness 的含义是检索的term 前后增加或减少n个单词的匹配查询
QueryBuilders.fuzzyQuery("hotelName", "tel").fuzziness(Fuzziness.ONE);
//5.wildcard query:通配符查询,支持* 任意字符串;?任意一个字符
QueryBuilders.wildcardQuery("fieldName","ctr*");//前面是fieldname,后面是带匹配字符的字符串
QueryBuilders.wildcardQuery("fieldName","c?r?");
123456789101112
//闭区间查询
QueryBuilder queryBuilder0 = QueryBuilders.rangeQuery("fieldName").from("fieldValue1").to("fieldValue2");
//开区间查询
QueryBuilder queryBuilder1 = QueryBuilders.rangeQuery("fieldName").from("fieldValue1").to("fieldValue2").includeUpper(false).includeLower(false);//默认是true,也就是包含
//大于
QueryBuilder queryBuilder2 = QueryBuilders.rangeQuery("fieldName").gt("fieldValue");
//大于等于
QueryBuilder queryBuilder3 = QueryBuilders.rangeQuery("fieldName").gte("fieldValue");
//小于
QueryBuilder queryBuilder4 = QueryBuilders.rangeQuery("fieldName").lt("fieldValue");
//小于等于
QueryBuilder queryBuilder5 = QueryBuilders.rangeQuery("fieldName").lte("fieldValue");
123456789101112
QueryBuilders.boolQuery()
QueryBuilders.boolQuery().must();//文档必须完全匹配条件,相当于and
QueryBuilders.boolQuery().mustNot();//文档必须不匹配条件,相当于not
QueryBuilders.boolQuery().should();//至少满足一个条件,这个文档就符合should,相当于or
1234
采用spring对RestHighLevelClient进行初始化
@Configuration
public class ElasticsearchConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client=new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
return client;
}
}
123456789
/** * es 的工具类 * * @author czchen * @version 1.0 * @date 2020/8/25 14:37 */ @Slf4j @Component public class EsUtil { @Autowired private RestHighLevelClient restHighLevelClient; /** * 关键字 */ public static final String KEYWORD = ".keyword"; /** * 创建索引 * * @param index 索引 * @return */ public boolean createIndex(String index) throws IOException { if(isIndexExist(index)){ log.error("Index is exits!"); return false; } //1.创建索引请求 CreateIndexRequest request = new CreateIndexRequest(index); //2.执行客户端请求 CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT); log.info("创建索引{}成功",index); return response.isAcknowledged(); } /** * 删除索引 * * @param index * @return */ public boolean deleteIndex(String index) throws IOException { if(!isIndexExist(index)) { log.error("Index is not exits!"); return false; } //删除索引请求 DeleteIndexRequest request = new DeleteIndexRequest(index); //执行客户端请求 AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT); log.info("删除索引{}成功",index); return delete.isAcknowledged(); } /** * 判断索引是否存在 * * @param index * @return */ public boolean isIndexExist(String index) throws IOException { GetIndexRequest request = new GetIndexRequest(index); boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT); return exists; } /** * 数据添加,正定ID * * @param jsonObject 要增加的数据 * @param index 索引,类似数据库 * @param id 数据ID, 为null时es随机生成 * @return */ public String addData(JSONObject jsonObject, String index, String id) throws IOException { //创建请求 IndexRequest request = new IndexRequest(index); //规则 put /test_index/_doc/1 request.id(id); request.timeout(TimeValue.timeValueSeconds(1)); //将数据放入请求 json IndexRequest source = request.source(jsonObject, XContentType.JSON); //客户端发送请求 IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT); log.info("添加数据成功 索引为: {}, response 状态: {}, id为: {}",index,response.status().getStatus(), response.getId()); return response.getId(); } /** * 数据添加 随机id * * @param jsonObject 要增加的数据 * @param index 索引,类似数据库 * @return */ public String addData(JSONObject jsonObject, String index) throws IOException { return addData(jsonObject, index, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase()); } /** * 通过ID删除数据 * * @param index 索引,类似数据库 * @param id 数据ID */ public void deleteDataById(String index, String id) throws IOException { //删除请求 DeleteRequest request = new DeleteRequest(index, id); //执行客户端请求 DeleteResponse delete = restHighLevelClient.delete(request, RequestOptions.DEFAULT); log.info("索引为: {}, id为: {}删除数据成功",index, id); } /** * 通过ID 更新数据 * * @param object 要增加的数据 * @param index 索引,类似数据库 * @param id 数据ID * @return */ public void updateDataById(Object object, String index, String id) throws IOException { //更新请求 UpdateRequest update = new UpdateRequest(index, id); //保证数据实时更新 //update.setRefreshPolicy("wait_for"); update.timeout("1s"); update.doc(JSON.toJSONString(object), XContentType.JSON); //执行更新请求 UpdateResponse update1 = restHighLevelClient.update(update, RequestOptions.DEFAULT); log.info("索引为: {}, id为: {}, 更新数据成功",index, id); } /** * 通过ID 更新数据,保证实时性 * * @param object 要增加的数据 * @param index 索引,类似数据库 * @param id 数据ID * @return */ public void updateDataByIdNoRealTime(Object object, String index, String id) throws IOException { //更新请求 UpdateRequest update = new UpdateRequest(index, id); //保证数据实时更新 update.setRefreshPolicy("wait_for"); update.timeout("1s"); update.doc(JSON.toJSONString(object), XContentType.JSON); //执行更新请求 UpdateResponse update1 = restHighLevelClient.update(update, RequestOptions.DEFAULT); log.info("索引为: {}, id为: {}, 更新数据成功",index, id); } /** * 通过ID获取数据 * * @param index 索引,类似数据库 * @param id 数据ID * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) * @return */ public Map<String,Object> searchDataById(String index, String id, String fields) throws IOException { GetRequest request = new GetRequest(index, id); if (StringUtils.isNotEmpty(fields)){ //只查询特定字段。如果需要查询所有字段则不设置该项。 request.fetchSourceContext(new FetchSourceContext(true,fields.split(","), Strings.EMPTY_ARRAY)); } GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT); Map<String, Object> map = response.getSource(); //为返回的数据添加id map.put("id",response.getId()); return map; } /** * 通过ID判断文档是否存在 * @param index 索引,类似数据库 * @param id 数据ID * @return */ public boolean existsById(String index,String id) throws IOException { GetRequest request = new GetRequest(index, id); //不获取返回的_source的上下文 request.fetchSourceContext(new FetchSourceContext(false)); request.storedFields("_none_"); return restHighLevelClient.exists(request, RequestOptions.DEFAULT); } /** * 获取低水平客户端 * @return */ public RestClient getLowLevelClient() { return restHighLevelClient.getLowLevelClient(); } /** * 高亮结果集 特殊处理 * map转对象 JSONObject.parseObject(JSONObject.toJSONString(map), Content.class) * @param searchResponse * @param highlightField */ public List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) { //解析结果 ArrayList<Map<String,Object>> list = new ArrayList<>(); for (SearchHit hit : searchResponse.getHits().getHits()) { Map<String, HighlightField> high = hit.getHighlightFields(); HighlightField title = high.get(highlightField); hit.getSourceAsMap().put("id", hit.getId()); Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果 //解析高亮字段,将原来的字段换为高亮字段 if (title!=null){ Text[] texts = title.fragments(); String nTitle=""; for (Text text : texts) { nTitle+=text; } //替换 sourceAsMap.put(highlightField,nTitle); } list.add(sourceAsMap); } return list; } /** * 查询并分页 * @param index 索引名称 * @param query 查询条件 * @param size 文档大小限制 * @param from 从第几页开始 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) * @param sortField 排序字段 * @param highlightField 高亮字段 * @return */ public List<Map<String, Object>> searchListData(String index, SearchSourceBuilder query, Integer size, Integer from, String fields, String sortField, String highlightField) throws IOException { SearchRequest request = new SearchRequest(index); SearchSourceBuilder builder = query; if (StringUtils.isNotEmpty(fields)){ //只查询特定字段。如果需要查询所有字段则不设置该项。 builder.fetchSource(new FetchSourceContext(true,fields.split(","),Strings.EMPTY_ARRAY)); } from = from <= 0 ? 0 : from*size; //设置确定结果要从哪个索引开始搜索的from选项,默认为0 builder.from(from); builder.size(size); if (StringUtils.isNotEmpty(sortField)){ //排序字段,注意如果proposal_no是text类型会默认带有keyword性质,需要拼接.keyword builder.sort(sortField+".keyword", SortOrder.ASC); } //高亮 HighlightBuilder highlight = new HighlightBuilder(); highlight.field(highlightField); //关闭多个高亮 highlight.requireFieldMatch(false); highlight.preTags("<span style='color:red'>"); highlight.postTags("</span>"); builder.highlighter(highlight); //不返回源数据。只有条数之类的数据。 //builder.fetchSource(false); request.source(builder); SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); log.error("=="+response.getHits().getTotalHits()); if (response.status().getStatus() == 200) { // 解析对象 return setSearchResponse(response, highlightField); } return null; } }
package cn.stylefeng.guns.modular.es.entity; import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.stylefeng.guns.GunsApplication; import lombok.extern.slf4j.Slf4j; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentType; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.io.IOException; import java.util.Map; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = GunsApplication.class) public class UserTest { @Resource private ElasticsearchRestTemplate template; @Resource private RestHighLevelClient client; @Test public void testCreateIndexAndDoc() throws IOException { //1.创建索引请求 CreateIndexRequest request = new CreateIndexRequest("user"); //2.执行客户端请求 CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT); log.info("创建索引{}成功", response); System.out.println("response.isAcknowledged() = " + response.isAcknowledged()); } @Test public void saveDoc() throws IOException { String index = "1"; User user = new User(); user.setId(1L); user.setAge(20); user.setName("狗蛋"); user.setPassword("23243424"); //创建请求 IndexRequest request = new IndexRequest("user"); //规则 put /test_index/_doc/1 request.id(index); request.timeout(TimeValue.timeValueSeconds(1)); //将数据放入请求 json request.source(user, XContentType.JSON); //客户端发送请求 IndexResponse response = client.index(request, RequestOptions.DEFAULT); log.info("添加数据成功 索引为: {}, response 状态: {}, id为: {}",index,response.status().getStatus(), response.getId()); log.info("=========={}",response.getId()); } @Test public void saveDoc2() throws IOException { IndexRequest request = new IndexRequest("user"); User user = new User(); user.setId(1L); user.setAge(20); user.setName("狗蛋"); user.setPassword("23243424"); // String s = JSONUtil.pa request.id("1"); String jsonString = "{" + "\"id\":\"kimchy\"," + "\"postDate\":\"2013-01-30\"," + "\"message\":\"trying out Elasticsearch\"" + "}"; // request.source(jsonString, XContentType.JSON); Map<String, Object> stringObjectMap = BeanUtil.beanToMap(user); request.source(stringObjectMap); // request.source() //客户端发送请求 IndexResponse response = client.index(request, RequestOptions.DEFAULT); // IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); log.info("添加数据成功 索引为: {}, response 状态: {}, id为: {}","user",response.status().getStatus(), response.getId()); log.info("=========={}",response.getId()); } @Test public void testStrToBean() { User user = new User(); user.setId(1L); user.setAge(20); user.setName("狗蛋"); user.setPassword("23243424"); JSONObject jsonObject = JSONUtil.parseObj(user); System.out.println(jsonObject.toString()); } @Test public void saveDocRandomId() throws IOException { IndexRequest request = new IndexRequest("user"); User user = new User(); user.setId(2L); user.setAge(30); user.setName("狗蛋333"); user.setPassword("23dsfasf243424"); // request.id("1"); Map<String, Object> stringObjectMap = BeanUtil.beanToMap(user); request.source(stringObjectMap); // request.source() //客户端发送请求 IndexResponse response = client.index(request, RequestOptions.DEFAULT); // IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); log.info("添加数据成功 索引为: {}, response 状态: {}, id为: {}","user",response.status().getStatus(), response.getId()); log.info("=========={}",response.getId()); } }
因为是基于springboot,那就不再使用kibana进行建立索引,而是用springboot来创建.创建索引的api:
ElasticsearchRestTemplate.createIndex(实体类.class)
如,现在创建一个名为esBean的实体类
相当于建库:
@Document用于指定索引
@Field(type = FieldType.Text,analyzer = “ik_max_word”)用于指定类型
@Data @NoArgsConstructor @Document(indexName = "test") public class esBean { @Id private int id; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String name; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String tags; public esBean(int id,String name,String tags){ this.id=id; this.name=name; this.tags=tags; } } 12345678910111213141516171819
service层:
public class esService implements IesService {
@Autowired
private ElasticsearchRestTemplate template;
@Override
public void create(){
template.createIndex(esBean.class);
}
}
123456789
像这样,就会自动建立一个名为test的索引,表结果就是esBean的成员
存储数据
需要一个持久化层的接口去继承ElasticsearchRepository,接着在service层中,依赖注入,直接调用其saveAll()方法即可,如下:
持久层:(可以在这里做一些搜索查询)
@Service
public interface esMapper extends ElasticsearchRepository<esBean,Long> {
}
12345
service层:
可以看到,saveAll,findAll这些方法是原来就有的,无需自己定义
public class esService implements IesService { @Autowired private ElasticsearchRestTemplate template; @Autowired esMapper mapper; @Override public Iterator<esBean> findAll() { return mapper.findAll().iterator(); } public void create(){ template.createIndex(esBean.class); } @Override public void saveAll(List<esBean> list) { mapper.saveAll(list); } } 12345678910111213141516171819
controller层:
@Autowired IesService iesService; @GetMapping("/init1") public String init1(){ iesService.create(); // id name tags List<esBean>list=new ArrayList<>(); list.add(new esBean(1,"张三锕爱","很帅,有很多人喜欢他")); list.add(new esBean(2,"李四酷狗","很帅,但是讨厌他")); list.add(new esBean(3,"王五王二爷","很丑,有是喜欢他")); list.add(new esBean(4,"张三王二婆","很帅,有没人喜欢他")); iesService.saveAll(list); return "success"; } 123456789101112131415
ElasticsearchRestTemplate的基本api
SearchQuery 总的查询
BoolQueryBuilder bool查询,可在后面加上must,mustNot,should等等
MatchQueryBuilder 匹配查询
TermQueryBuilder 倒排索引查询
HighlightBuilder 高亮查询,用于设置要高亮的field
SearchHit 查询结果
实体类:
@Data @NoArgsConstructor @Document(indexName = "test5") public class esBean { @Id private int id; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String name; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String tags; public esBean(int id,String name,String tags){ this.id=id; this.name=name; this.tags=tags; } } 1234567891011121314151617
持久层
@Service
public interface esMapper extends ElasticsearchRepository<esBean,Long> {
}
123456
service:
public interface IesService { public Iterator<esBean> findAll(); public void saveAll(List<esBean> list); public void create(); public AggregatedPage testForHigh() throws IOException; } 123456 @Service public class esService implements IesService { @Autowired private ElasticsearchRestTemplate template; @Autowired esMapper mapper; @Override public Iterator<esBean> findAll() { return mapper.findAll().iterator(); } public void create(){ template.createIndex(esBean.class); } @Override public void saveAll(List<esBean> list) { mapper.saveAll(list); } public AggregatedPage testForHigh() throws IOException { String preTag = "<font color='#dd4b39'>";//google的色值 String postTag = "</font>"; BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder() .must(new MatchQueryBuilder("tags","很帅")); SearchQuery searchQuery=new NativeSearchQueryBuilder(). //总的查询 withQuery(boolQueryBuilder). //设置bool查询 withHighlightFields(new HighlightBuilder.Field("name").preTags(preTag).postTags(postTag)).//设置高亮效果 withHighlightFields(new HighlightBuilder.Field("tags").preTags(preTag).postTags(postTag)).build(); AggregatedPage<esBean> ideas=template.queryForPage(searchQuery, esBean.class, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) { List<esBean> list = new ArrayList<>(); for(SearchHit hit:searchResponse.getHits()){//获取遍历查询结果 if(searchResponse.getHits().getHits().length<=0)return null; esBean bean=new esBean(); Map map=hit.getSourceAsMap(); System.out.println(map); bean.setId((Integer)map.get("id")); bean.setName((String)map.get("name")); HighlightField name=hit.getHighlightFields().get("name"); if(name!=null){ bean.setName(name.fragments()[0].toString()); //得到高亮的结果 } HighlightField tags=hit.getHighlightFields().get("tags"); if(tags!=null){ bean.setTags(tags.fragments()[0].toString()); } list.add(bean); } if(list.size()>0)return new AggregatedPageImpl<>((List<T>)list); return null; } @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) { return null; } }); ideas.get().forEach(model->{ System.out.println(model); }); return ideas; } @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) { return null; } }); ideas.get().forEach(model->{ System.out.println(model); }); return ideas; } }
controller:
@RestController @RequestMapping("/elastic") public class ElasticController { @Autowired IesService iesService; @GetMapping("/init1") public String init1(){ iesService.create(); // id name tags List<esBean>list=new ArrayList<>(); list.add(new esBean(1,"张三锕爱","很帅,有很多人喜欢他")); list.add(new esBean(2,"李四酷狗","很帅,但是讨厌他")); list.add(new esBean(3,"王五王二爷","很丑,有是喜欢他")); list.add(new esBean(4,"张三王二婆","很帅,有没人喜欢他")); iesService.saveAll(list); return "success"; } @GetMapping("/get1") public AggregatedPage get1() throws IOException { return iesService.testForHigh(); } }
postman访问:
Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D7M8jsnj-1606058387968)(/Users/allen/Documents/TyporaFiles/gulimall/resource/%25E8%25B5%2584%25E6%2596%2599%25E6%2596%2587%25E6%25A1%25A3/%25E9%2598%25B6%25E6%25AE%25B5md/assets/1575806287671.png)]
其中ElasticsearchRepository接口功能最强大。该接口的方法包括:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DV78cXpV-1606058387976)(/Users/allen/Documents/TyporaFiles/gulimall/resource/%25E8%25B5%2584%25E6%2596%2599%25E6%2596%2587%25E6%25A1%25A3/%25E9%2598%25B6%25E6%25AE%25B5md/assets/1575806405547.png)]
@Autowired
UserRepository userRepository;
@Test
void testAdd(){
this.userRepository.save(new User(1l, "zhang3", 20, "123456"));
}
修改和新增是同一个接口,区分的依据就是id,这一点跟我们在页面发起PUT请求是类似的。
@Test
void testDelete(){
this.userRepository.deleteById(1l);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l4Bx6eRd-1606058387984)(/Users/allen/Documents/TyporaFiles/gulimall/resource/%25E8%25B5%2584%25E6%2596%2599%25E6%2596%2587%25E6%25A1%25A3/%25E9%2598%25B6%25E6%25AE%25B5md/assets/1575848896764.png)]
查询一个:
@Test
void testFind(){
System.out.println(this.userRepository.findById(1l).get());
}
Spring Data 的另一个强大功能,是根据方法名称自动实现功能。
比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。
当然,方法名称要符合一定的约定:
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And | findByNameAndPrice | {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or | findByNameOrPrice | {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is | findByName | {"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not | findByNameNot | {"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between | findByPriceBetween | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
LessThanEqual | findByPriceLessThan | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
GreaterThanEqual | findByPriceGreaterThan | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Before | findByPriceBefore | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After | findByPriceAfter | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like | findByNameLike | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith | findByNameStartingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith | findByNameEndingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
Contains/Containing | findByNameContaining | {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} |
In | findByNameIn(Collection<String>names) | {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn | findByNameNotIn(Collection<String>names) | {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
Near | findByStoreNear | Not Supported Yet ! |
True | findByAvailableTrue | {"bool" : {"must" : {"field" : {"available" : true}}}} |
False | findByAvailableFalse | {"bool" : {"must" : {"field" : {"available" : false}}}} |
OrderBy | findByAvailableTrueOrderByNameDesc | {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
准备一组数据:
@Test
void testAddAll(){
List<User> users = new ArrayList<>();
users.add(new User(1l, "柳岩", 18, "123456"));
users.add(new User(2l, "范冰冰", 19, "123456"));
users.add(new User(3l, "李冰冰", 20, "123456"));
users.add(new User(4l, "锋哥", 21, "123456"));
users.add(new User(5l, "小鹿", 22, "123456"));
users.add(new User(6l, "韩红", 23, "123456"));
this.userRepository.saveAll(users);
}
在UserRepository中定义一个方法:
第一种写法:
public interface UserRepository extends ElasticsearchRepository<User, Long> {
/**
* 根据年龄区间查询
* @param age1
* @param age2
* @return
*/
List<User> findByAgeBetween(Integer age1, Integer age2);
}
测试:
@Test
void testFindByAgeBetween(){
System.out.println(this.userRepository.findByAgeBetween(20, 30));
}
第二种写法:
这个就牛逼了,可以实现复杂查询
@Query("{\n" +
" \"range\": {\n" +
" \"age\": {\n" +
" \"gte\": \"?0\",\n" +
" \"lte\": \"?1\"\n" +
" }\n" +
" }\n" +
" }")
List<User> findByQuery(Integer age1, Integer age2);
测试:
@Test
void testFindByQuery(){
System.out.println(this.userRepository.findByQuery(20, 30));
}
@Test void testNative(){ // 初始化自定义查询对象 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 构建查询 queryBuilder.withQuery(QueryBuilders.matchQuery("name", "冰冰")); // 排序 queryBuilder.withSort(SortBuilders.fieldSort("age").order(SortOrder.ASC)); // 分页 queryBuilder.withPageable(PageRequest.of(0, 2)); // 高亮 queryBuilder.withHighlightBuilder(new HighlightBuilder().field("name").preTags("<em>").postTags("</em>")); // 执行查询,获取分页结果集 Page<User> userPage = this.userRepository.search(queryBuilder.build()); // 总页数 System.out.println(userPage.getTotalPages()); // 总记录数 System.out.println(userPage.getTotalElements()); // 当前页数据 System.out.println(userPage.getContent()); }
NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体
Page<item>
:默认是分页查询,因此返回的是一个分页的结果对象,包含属性:
搜索address中包含mill的所有人的年龄分布以及平均年龄,平均薪资
GET bank/_search { "query": { "match": { "address": "Mill" } }, "aggs": { "ageAgg": { "terms": { "field": "age", "size": 10 } }, "ageAvg": { "avg": { "field": "age" } }, "balanceAvg": { "avg": { "field": "balance" } } } }
java实现
/** * 复杂检索:在bank中搜索address中包含mill的所有人的年龄分布以及平均年龄,平均薪资 * @throws IOException */ @Test public void searchData() throws IOException { //1. 创建检索请求 SearchRequest searchRequest = new SearchRequest(); //1.1)指定索引 searchRequest.indices("bank"); //1.2)构造检索条件 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.query(QueryBuilders.matchQuery("address","Mill")); //1.2.1)按照年龄分布进行聚合 TermsAggregationBuilder ageAgg=AggregationBuilders.terms("ageAgg").field("age").size(10); sourceBuilder.aggregation(ageAgg); //1.2.2)计算平均年龄 AvgAggregationBuilder ageAvg = AggregationBuilders.avg("ageAvg").field("age"); sourceBuilder.aggregation(ageAvg); //1.2.3)计算平均薪资 AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance"); sourceBuilder.aggregation(balanceAvg); System.out.println("检索条件:"+sourceBuilder); searchRequest.source(sourceBuilder); //2. 执行检索 SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); System.out.println("检索结果:"+searchResponse); //3. 将检索结果封装为Bean SearchHits hits = searchResponse.getHits(); SearchHit[] searchHits = hits.getHits(); for (SearchHit searchHit : searchHits) { String sourceAsString = searchHit.getSourceAsString(); Account account = JSON.parseObject(sourceAsString, Account.class); System.out.println(account); } //4. 获取聚合信息 Aggregations aggregations = searchResponse.getAggregations(); Terms ageAgg1 = aggregations.get("ageAgg"); for (Terms.Bucket bucket : ageAgg1.getBuckets()) { String keyAsString = bucket.getKeyAsString(); System.out.println("年龄:"+keyAsString+" ==> "+bucket.getDocCount()); } Avg ageAvg1 = aggregations.get("ageAvg"); System.out.println("平均年龄:"+ageAvg1.getValue()); Avg balanceAvg1 = aggregations.get("balanceAvg"); System.out.println("平均薪资:"+balanceAvg1.getValue()); }
可以尝试对比打印的条件和执行结果,和前面的ElasticSearch的检索语句和检索结果进行比较;
据说ElasticsearchRepository并不好用,我还没深度使用,盲猜是复杂查询的时候嵌套和平行关系有点乱哈哈,那么问题来了,如果javaAPI里面能使用原生restAPI查询就好了,借助kibana直接写完就可以放到代码中执行,美滋滋。有空继续研究一下。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。