一、功能需求

当我们用ES查询到结果后,想对命中关键词的实体的多个字段自动高亮,应该如何实现呢?

需要实现这个功能,大概分为以下几个步骤:

二、实现步骤

  1. 构造查询方法,设置高亮(SearchHits方法)

    
        /**
         * 查询条件构造方法
         *
         * @param indices 索引名称(需要从那个索引当中查询)
         * @param req     查询条件
         * @param fields  查询的文档(对应数据库的字段)
         * @return SearchHit
         */
        public SearchHits queryBuilder(String indices, SearchREQ req, String[] fields) throws IOException {
            SearchSourceBuilder builder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            HighlightBuilder highlightBuilder = new HighlightBuilder();
    
            //设置分页
            if (req.getCurrent() > 0) {
                builder.from((int) req.getCurrent());
            }
            if (req.getSize() > 0) {
                builder.size((int) req.getSize());
            }
    
            //设置高亮
            highlightBuilder.field("*"); //所有的字段都高亮
            highlightBuilder.requireFieldMatch(false);//如果要多个字段高亮,这项要为false
            highlightBuilder.preTags("<font color='red'>").postTags("</font'>")
                            .fragmentSize(800000)//下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等;  最大高亮分片数
                            .numOfFragments(0);//从第一个分片获取高亮片段
            builder.highlighter(highlightBuilder);
    
    		//构建查询条件
            if (StringUtils.isNotEmpty(req.getUserId())) {
                boolQueryBuilder.must(QueryBuilders.matchQuery("userId", req.getUserId()));
            }
    		//模糊查询
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery("*" + req.getText() + "*", fields));
            //对查询结果进行排序
            builder.query(boolQueryBuilder).sort("createDate", SortOrder.DESC);
    
            SearchRequest searchRequest = new SearchRequest(indices);
            searchRequest.source(builder);
            SearchResponse searchResponse = levelClientTests.search(searchRequest, RequestOptions.DEFAULT);
            return searchResponse.getHits();
        }
    
    
  2. 调用查询方法,查询结果(findObject方法)

    
       public Result findObject(SearchREQ req) throws IOException, 
              NoSuchFieldException {
            if (StringUtils.isEmpty(req.getText())) {
                return Result.error("查找内容不能为空");
            }
            List<Object> resultList = null;
            Map<Object, Object> resultMap = new HashMap<>();
            SearchHits hits;
            String indices = "mcb_article";
            String[] fields = {"summary", "title", "mdContent"};
            hits = queryBuilder(indices, req, fields);
            Article article = null;
            resultList = new ArrayList<>();
            //遍历查询结果并接收
             for (SearchHit searchHit : hits) {
                    article = (Article) replaceAttr(searchHit, fields, indices);
                    if (article != null) {
                        resultList.add(article);
                    }
                }
        }
    
  3. 使用工具替换实体的属性值(replaceAttr方法)

    
       /**
         * 替换高亮属性
         *
         * @param searchHit 查询结果
         * @param attrs 字段名
         * @return
         * @throws JsonProcessingException
         * @throws NoSuchFieldException
         */
        private Object replaceAttr(SearchHit searchHit, String[] attrs) throws JsonProcessingException, NoSuchFieldException {
            
            ObjectMapper objectMapper = new ObjectMapper();
            EntityUtil util = new EntityUtil();
            Object o = null;
            Article article = new Article();
    
            for (String field : attrs) {
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField hField = highlightFields.get(field);
                if (hField != null) {
                    //替换高亮字段
                    Text[] fragments = hField.fragments();
                    StringBuilder text = new StringBuilder();
                    for (Text textGet : fragments) {
                        text.append(textGet);
                    }
                        //对象的值只能读一次,否则会被覆盖
                    if (article.getId() == null) {
                        article = objectMapper.readValue(searchHit.
                                  getSourceAsString(), article.getClass());
                    }
                      //设置对象的属性值
                   util.setValue(article, Article.class,field,
                         Article.class.getDeclaredField(field).getType(),text.toString());
                        o = article;
                        continue;
                    }
            }
           return o;
        }
    
  4. 设置对象的属性值()

    
    /**
     * 实体工具
     * @author MyLover
     * @date 2022.2.6
     * @version 1.0.0
     */
    public class EntityUtil {
    
        /**
         * 设置对象对应的属性值
         * @param obj 传入的对象
         * @param clazz 传入对象的类
         * @param filedName 需要设置的字段名
         * @param typeClass 字段的类型
         * @param value 需要设置的值
         *  实例:  设置Article的id属性值为1
         *  Article article = new Article();
         *  String field = "id";
         *  setValue(article_es1,Article_ES.class,field,
         *  Article.class.getDeclaredField(field).getType(),"1");
         */
        public void setValue(Object obj, Class<?> clazz, String filedName, Class<?> typeClass, Object value) {
            String methodName = "set" + filedName.substring(0, 1)
                .toUpperCase() + filedName.substring(1);
            try {
                Method method = clazz.getDeclaredMethod(methodName, typeClass);
                method.invoke(obj, getClassTypeValue(typeClass, value));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        /**
         * 根据属性,获取对象的属性值, 例如: getGetMethod(Article,id)
         * @param ob   对象
         * @param name 属性名
         */
        public  Object getGetMethod(Object ob, String name) throws Exception {
            Method[] m = ob.getClass().getMethods();
            for (Method method : m) {
                if (("get" + name).toLowerCase().equals(method.getName().toLowerCase())) {
                    return method.invoke(ob);
                }
            }
            return null;
        }
    
        private  Object getClassTypeValue(Class<?> typeClass, Object value) {
            if (typeClass == int.class || value instanceof Integer) {
                if (null == value) {
                    return 0;
                }
                return value;
            } else if (typeClass == short.class) {
                if (null == value) {
                    return 0;
                }
                return value;
            } else if (typeClass == byte.class) {
                if (null == value) {
                    return 0;
                }
                return value;
            } else if (typeClass == double.class) {
                if (null == value) {
                    return 0;
                }
                return value;
            } else if (typeClass == long.class) {
                if (null == value) {
                    return 0;
                }
                return value;
            } else if (typeClass == String.class) {
                if (null == value) {
                    return "";
                }
                return value;
            } else if (typeClass == boolean.class) {
                if (null == value) {
                    return true;
                }
                return value;
            } else if (typeClass == BigDecimal.class) {
                if (null == value) {
                    return new BigDecimal(0);
                }
                return new BigDecimal(value + "");
            } else {
                return typeClass.cast(value);
            }
        }
    }
    
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐