当前位置: 首页 > 图灵资讯 > 技术篇> SpringBoot 整合 ES 进行各种高级查询搜索

SpringBoot 整合 ES 进行各种高级查询搜索

来源:图灵教育
时间:2023-05-31 09:18:12

数据准备

因为这篇文章是数据搜索,我们需要在我们的es服务器中插入一些数据供我们以后使用

esUserService

public interface EsUserService extends ElasticsearchRepository<User, Integer> {}

@RestControllerpublic class EsController {    @Autowired    private ElasticsearchRestTemplate elasticsearchTemplate;    @Autowired    private EsUserService esUserService;    @Autowired    private RestHighLevelClient client;    private String[] names = {诸葛亮, "曹操", "李白", "韩信", "赵云", "小乔", 狄仁杰, "李四", “诸小明”, "王五"};    private String[] infos = {“我来自中国的一个小村庄,位于湖南省”, “我来自中国的一个叫上海的大城市,人们称之为魔都”            , “我来自杭州,这是一座浪漫的城市“};    /**     * 准备数据     *     * @return     */    @GetMapping("prepareDate")    public Object saveUser() {        ///添加索引mapping索引会自动创建,但mapping只使用默认,这将导致分词器无效 因此,我们手动导入mapping        Random random = new Random();        List<User> users = new ArrayList<>();        for (int i = 0; i < 20; i++) {            User user = new User();            user.setId(i);            user.setName(names[random.nextInt(9)]);            user.setAge(random.nextInt(40) + i);            user.setInfo(infos[random.nextInt(2)]);            users.add(user);        }        Iterable<User> users1 = esUserService.saveAll(users);        return users1;    }}

SpringBoot 整合 ES 进行各种高级查询搜索_搜索

以下是我们本文的重点。

准备map转实体类

因为我们需要经常使用map转对象,所以给你一个更好的map转对象方法

import java.beans.BeanInfo;import java.beans.Introspector;import java.beans.PropertyDescriptor;import java.math.BigDecimal;import java.sql.ResultSet;import java.util.Map;import java.util.Objects;/** * 将反射讲数据转化为Objectctt * * @param <T> */public class BeanHandler<T> {    private Class<T> clazz;    public BeanHandler(Class<T> clazz) {        this.clazz = clazz;    }    /**     * 讲sql 查询结果 Resultset转化为对象     *     * @param rs     * @return     * @throws Exception     */    public T handle(ResultSet rs) throws Exception {        ///结果集默认指向第一个数据的前一个        if (rs.next()) {            ///根据传入的字节码创建传入的指定对象            T obj = clazz.newInstance();            ///获取指定字节码信息            BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);            ///获取所有属性描述器            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();            for (PropertyDescriptor pd : pds) {                ///获取结果集中对应字段名的值                Object o = rs.getObject(pd.getName());                ///执行当前方法并输入参数                pd.getWriteMethod().invoke(obj, o);            }            return obj;        }        return null;    }    /**     * 将map 将反射转化为对象     *     * @param map     * @return     * @throws Exception     */    public T handle(Map<String, Object> map) throws Exception {        ///结果集默认指向第一个数据的前一个        ///根据传入的字节码创建传入的指定对象        T obj = clazz.newInstance();        BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();        for (PropertyDescriptor pd : pds) {            Object o = map.get(pd.getName());            if (Objects.nonNull(o)) {                // !!!!这里需要对属性类型进行强制性类型转换,                o = getPropertyTypeObject(pd, o);                // 下面的方法相当于属性 set方法                pd.getWriteMethod().invoke(obj, o);            }        }        return obj;    }    /**     * 将相应的mapvalue对应 强转为实体类对应的类型     *     * @param pd     * @param o     * @return     */    public Object getPropertyTypeObject(PropertyDescriptor pd, Object o) {        ///当前属性类型        String name = pd.getPropertyType().getName();        name = name.substring(name.lastIndexOf(".") + 1);        switch (name) {            case "String":                o = String.valueOf(o);                break;            case "Long":                o = Long.valueOf(String.valueOf(o));                break;            case "Double":                o = Double.valueOf(String.valueOf(o));                break;            case "Integer":                o = Integer.valueOf(String.valueOf(o));                break;            case "BigDecimal":                o = new BigDecimal(String.valueOf(o));                break;        }        return o;    }}

准确查询单个条件

@PostMapping("singleConditionPreciseQuery")public Object singleConditionPreciseQuery(@RequestParam(value = "query") String query) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.matchQuery("name", query));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// List执行请求<User> users = new ArrayList<>();SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

范围查询

/** * 范围查询 * 包括from、to * * @return */@PostMapping(rangesearch1)public Object rangesearch(@RequestParam(value = "from") int from, @RequestParam(value = "to") int to) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.rangeQuery("age").from(from).to(to));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// ////// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}/** * 范围查询 * 不包括from、to * * @return */@PostMapping(rangesearch2)public Object rangeserch2(@RequestParam(value = "from") int from, @RequestParam(value = "to") int to) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.rangeQuery("age").from(from, false).to(to, false));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// ////// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}/** * 范围查询 * lt:小于,gt:大于 * * @return */@PostMapping(rangesearch3)public Object rangeserch3(@RequestParam(value = "from") int from, @RequestParam(value = "to") int to) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.rangeQuery("age").lt(to).gt(from));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// ////// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

SpringBoot 整合 ES 进行各种高级查询搜索_User_02

模糊查询支持通配符

/** * 模糊查询,支持通配符 * * @return */@PostMapping("vagueSearch")public Object vagueSearch(@RequestParam(value = "query") String query) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.wildcardQuery("name", query));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}/** * 不使用通配符的模糊查询,左右匹配 * * @return */@PostMapping(vaguesearch1)public Object vaguesearch(@RequestParam(value = "query") String query) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.queryStringQuery(query).field("name"));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}/** * 多字段模糊查询 * * @return */@PostMapping(vaguesearch2)public Object vagueSearch2(@RequestParam(value = "query") String query) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.multiMatchQuery(query, "name", "info"));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

排序

/** * 排序 * * @return */@PostMapping("sortSearch")public Object sortSearch(@RequestParam(value = "query") String query) throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.multiMatchQuery(query, "name", "info")).sort("age", SortOrder.ASC);//按年龄顺序//搜索Searchrequestestesss searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

对文档数量进行精确统计筛选

/** * 对文档数量进行精确统计筛选,降低了查询性能 * * @return */@PostMapping("countSearch")public Object countSearch() throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().trackTotalHits(true);//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// 分析查询结果returnn response;}

设置源字段过滤返回

/** * 设置源字段过度考虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段 *  * @return */@PostMapping("filterSearch")public Object filterSearch() throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().fetchSource(new String[]{"name", "age"}, new String[]{"id", "info"});//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

SpringBoot 整合 ES 进行各种高级查询搜索_搜索_03

我们还可以从上图的结果中看出:

  • 第一个参数结果集包括哪些字段?
  • 第二个参数表示结果集不包括哪些字段

SpringBoot 整合 ES 进行各种高级查询搜索_java_04

根据 id 精确匹配

/** * 根据ID准确查询 * * @return */@PostMapping("searchByIds")public Object searchByIds(@RequestBody Map<String, Object> params) throws Exception {List<Integer> ids = (List<Integer>) params.get("ids");// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.termsQuery("_id", ids));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

matchAllQuery 搜索全部

/** * matchAllQuery 搜索全部 * * @return */@PostMapping("martchAllQuery")public Object martchAllQuery() throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

match 搜索匹配

/** * match 搜索匹配 * * @return */@PostMapping("martch")public Object martch(@RequestBody Map<String, Object> params) throws Exception {List<String> querys = (List<String>) params.get("querys");// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.matchQuery("name", querys));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

bool组合查询

/*** match 搜索匹配** @return*/@PostMapping("martch")public Object martch(@RequestBody Map<String, Object> params) throws Exception {List<String> querys = (List<String>) params.get("querys");// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.matchQuery("name", querys));//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");    /** * bool组合查询 * * @return */@PostMapping("boolSearch")public Object boolSearch(@RequestBody Map<String, Object> params) throws Exception {List<String> queryNames = (List<String>) params.get("names");int maxAge = (int) params.get("maxAge");int minAge = (int) params.get("minAge");// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder();BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();boolQueryBuilder.must(QueryBuilders.termsQuery("name", queryNames));boolQueryBuilder.must(QueryBuilders.rangeQuery("age").lte(maxAge).gte(minAge));builder.query(boolQueryBuilder);//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

SpringBoot 整合 ES 进行各种高级查询搜索_搜索_05

nested类型嵌套查询

有时候,我们需要查询一个对象内部类DSL的值发现通过平时的查询无法查询数据(Domain Specific language,也就是说,特定领域的特殊语言)出现了!

elasticsearch中的内部对象不能按预期工作,这里的问题是elasticsearch(lucene)使用的库没有内部对象的概念,所以内部对象被扁平化为一个简单的字段名称和值列表。

/** * nested类型嵌套查询 * * @return */@PostMapping("nestedSearch")public Object nestedSearch() throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder();//条件查询BoolQueryBuilder mainBool=new BoolQueryBuilder();mainBool.must(QueryBuilders.matchQuery("name", "赵六");//nested类型嵌套查询BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();boolQueryBuilder.must(QueryBuilders.matchQuery("user.name", "A"));boolQueryBuilder.must(QueryBuilders.matchQuery("user.info", “浦东”);NestedQueryBuilder nested = QueryBuilders.nestedQuery("user",boolQueryBuilder, ScoreMode.None);mainBool.must(nested);builder.query(mainBool);//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

多条件查询 + 排序 + 分页

/** * 多条件查询 + 排序 + 分页 * * @return */@PostMapping("multiConditionSearch")public Object multiConditionSearch() throws Exception {// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder();//条件搜索BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();boolQueryBuilder.must(QueryBuilders.matchQuery("name", "张").operator(Operator.AND);//需要 满足所有字段);boolQueryBuilder.must(QueryBuilders.rangeQuery("age").lte(30).gte(20));builder.query(boolQueryBuilder);//结果集成分页builder.from(0).size(2);//排序builder.sort("age",SortOrder.ASC);//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// List执行请求<User> users = new ArrayList<>();for (SearchHit searchHit : response.getHits().getHits()) {Map<String, Object> map = searchHit.getSourceAsMap();BeanHandler<User> beanHandler = new BeanHandler<>(User.class);User user = beanHandler.handle(map);users.add(user);}// 分析查询结果returnn users;}

聚合查询

/** * 求和 * * @return */@PostMapping("sumSearch")public Object sumSearch() throws Exception {Map<String, Object> result = new HashMap<>();// SearchSourcebuilder创建请求 builder = new SearchSourceBuilder().query(QueryBuilders.termsQuery("_id", new int[]{1, 2, 3}));//条件搜索//结果集合分页builder.from(0).size(2);builder.query(QueryBuilders.matchAllQuery());//聚合查询AggregationBuilder aggregation = AggregationBuilders.sum("sum_age").field("age");builder.aggregation(aggregation);//搜索Searchrequestest searchRequest = new SearchRequest();searchRequest.indices("user");searchRequest.types("_doc");searchRequest.source(builder);// Searchresponse执行请求 response = client.search(searchRequest, RequestOptions.DEFAULT);// 分析查询结果returnn response;}

SpringBoot 整合 ES 进行各种高级查询搜索_User_06

SpringBoot 整合 ES 进行各种高级查询搜索_搜索_07

值得注意的是,在聚合操作的fild上,如果将该字段设置为key或text,则会出现以下错误

SpringBoot 整合 ES 进行各种高级查询搜索_java_08

原因是:

文本字段没有优化每个文档字段数据(如聚合和排序)的操作,因此默认情况下禁止这些操作。

我们需要使用关键字段。或者,在设置text或key的字段上设置fieldatata=true,通过取消反转索引来加载字段数据。请注意,这可能会占用大量内存

参考文章并稍作修改,https://blog.csdn.net/zhiyikeji/article/details/128939901