spring boot使用elasticsearch分词,排序,分页,高亮简单示例
记,写一个简单的es分词demo,es版本6.8.12
如果使用es7有些方法可能会有所改变,请参考7的文档
1. 创建ES实体
怎么简单怎么来
@Data @Document(indexName = "goods") public class GoodsEsItem implements Serializable { //主键 @Id private Long id; @Field(type = FieldType.Text, analyzer = "ik_max_word") //商品名 private String name; private String category; @Field(type = FieldType.Date) private Date create_time; @Field(type = FieldType.Date) private Date update_time; }
2. 创建查询实体
@Data public class GoodsQueryVo { /** * 关键字 */ private String keyword; /** * 分类 */ private String category; /** * 当前页 */ private Integer current; /** * 每页大小 */ private Integer pageSize; /** * 排序字段 */ private String sfield; /** * 排序格式asc,desc */ private String sm; }
3. 查询方法实现
3.1 核心代码
@Override public MapsearchByKeyword(GoodsQueryVo goodsQueryVo) { //构建查询条件 NativeSearchQueryBuilder queryBuilder = queryBuilder(goodsQueryVo); //添加高亮域 HighlightBuilder.Field field = new HighlightBuilder. Field("name"). //指定的高亮域 preTags(""). //前缀 postTags(""). //后缀 fragmentSize(100); queryBuilder.withHighlightFields(field); //执行搜索。HighlightResultMapper处理高亮文本 AggregatedPage results = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), PblGoodsEsItem.class, new HighlightResultMapper()); //结果集 Map resultMap = new HashMap (); resultMap.put("list", results.getContent()); resultMap.put("total", results.getTotalElements()); return resultMap; }
3.2 构建查询条件
商品名称加了 @Field(type = FieldType.Text, analyzer = “ik_max_word”) 会自动分词
private NativeSearchQueryBuilder queryBuilder(GoodsQueryVo goodsQueryVo) { //关键字分词 EsIkResult esIkResult = keywordToken(goodsQueryVo); Listtokens = esIkResult.getTokens().stream().map(EsIkResult.token::getToken).collect(Collectors.toList()); //QueryBuilder构建 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); //多条件组合查询对象 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); //关键字条件should构建or条件,must构建and条件 BoolQueryBuilder keywordQuery = QueryBuilders.boolQuery(); tokens.forEach(e -> { keywordQuery.should(QueryBuilders.termQuery("name", e)); }); //分类条件 if (!StringUtils.isEmpty(goodsQueryVo.getCategory())) { boolQuery.must(QueryBuilders.termQuery("category",goodsQueryVo.getCategory())); } //这里的关系为(keyword1 or keyword2) and category boolQuery.must(keywordQuery); //分页 queryBuilder.withPageable(PageRequest.of(currentPage(goodsQueryVo), goodsQueryVo.getPageSize())); //排序 String sfield = goodsQueryVo.getSfield(); String sm = goodsQueryVo.getSm(); if (!StringUtils.isEmpty(sfield) && !StringUtils.isEmpty(sm)) { queryBuilder.withSort( SortBuilders.fieldSort(sfield) //排序域 .order(SortOrder.valueOf(sm))); //排序方式 } return queryBuilder.withQuery(boolQuery); }
分页处理
public int currentPage(GoodsQueryVo goodsQueryVo) { try { Object currentPage = goodsQueryVo.getCurrent(); return Integer.parseInt(currentPage.toString()) > 0 ? Integer.parseInt(currentPage.toString()) - 1 : 0; } catch (Exception e) { return 0; } }
3.2.1 关键词分词
当输入衣服鞋子的时候会将关键字分为衣服,鞋子去查询
@Data public class EsIkResult { private Listtokens; @Data public static class token{ private String token; @JSONField(name = "start_offset") private Integer startOffset; @JSONField(name = "end_offset") private Integer endOffset; private String type; private Integer position; } }
HttpUtil是自己封装的http请求类,可以根据自己的方式去发出请求
private EsIkResult keywordToken(GoodsQueryVo goodsQueryVo) { HashMapparams = new HashMap<>(); params.put("analyzer", "ik_max_word"); params.put("text", goodsQueryVo.getKeyword()); return HttpUtil.post("http://localhost:9200/_analyze?pretty", params, EsIkResult.class); }
3.3 高亮处理
高亮处理mapper,处理高亮数据,复制用即可,不需要记
public class HighlightResultMapper extends DefaultResultMapper { /*** * 处理结果集 */ @Override publicAggregatedPage mapResults(SearchResponse response, Class clazz, Pageable pageable) { //所有数据 for (SearchHit hit : response.getHits()) { //当前单条数据 Map sourceMap = hit.getSourceAsMap(); //高亮数据 for (Map.Entry entry : hit.getHighlightFields().entrySet()) { String key = entry.getKey(); if (sourceMap.containsKey(key)) { Text[] fragments = entry.getValue().getFragments(); sourceMap.put(key, transTextArrayToString(fragments)); } } hit.sourceRef(new ByteBufferReference(ByteBuffer.wrap(JSONObject.toJSONString(sourceMap).getBytes()))); } return super.mapResults(response, clazz, pageable); } /*** * 拼接数据碎片 */ private String transTextArrayToString(Text[] fragments) { if (null == fragments) { return ""; } StringBuffer buffer = new StringBuffer(); for (Text fragment : fragments) { buffer.append(fragment.string()); } return buffer.toString(); } }
添加高亮,在上面·searchByKeyword方法有写到
此处会将name字段含有关键字的文本替换成 包含,前端用html回显即可
示例,搜索衣服
//添加高亮域 HighlightBuilder.Field field = new HighlightBuilder. Field("name"). //指定的高亮域 preTags(""). //前缀 postTags(""). //后缀 fragmentSize(100); queryBuilder.withHighlightFields(field); //执行搜索。HighlightResultMapper处理高亮文本 AggregatedPageresults = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), PblGoodsEsItem.class, new HighlightResultMapper());
4.完整查询代码展示
ps:
高亮处理代码上方已给出,此处只给出搜索代码
HttpUtil是自己封装的http请求类,可以根据自己的方式去发出请求
@Service public class GoodsEsServiceImpl implements GoodsEsService { @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; @Override public MapsearchByKeyword(GoodsQueryVo goodsQueryVo) { //构建查询条件 NativeSearchQueryBuilder queryBuilder = queryBuilder(goodsQueryVo); //添加高亮域 HighlightBuilder.Field field = new HighlightBuilder. Field("name"). //指定的高亮域 preTags(""). //前缀 postTags(""). //后缀 fragmentSize(100); queryBuilder.withHighlightFields(field); //执行搜索。HighlightResultMapper处理高亮文本 AggregatedPage results = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), PblGoodsEsItem.class, new HighlightResultMapper()); //结果集 Map resultMap = new HashMap (); resultMap.put("list", results.getContent()); resultMap.put("total", results.getTotalElements()); return resultMap; } private NativeSearchQueryBuilder queryBuilder(GoodsQueryVo goodsQueryVo) { //关键字分词 EsIkResult esIkResult = keywordToken(goodsQueryVo); List tokens = esIkResult.getTokens().stream().map(EsIkResult.token::getToken).collect(Collectors.toList()); //QueryBuilder构建 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); //多条件组合查询对象 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); //关键字条件should构建or条件,must构建and条件 BoolQueryBuilder keywordQuery = QueryBuilders.boolQuery(); tokens.forEach(e -> { keywordQuery.should(QueryBuilders.termQuery("name", e)); }); //分类条件 if (!StringUtils.isEmpty(goodsQueryVo.getCategory())) { boolQuery.must(QueryBuilders.termQuery("category",goodsQueryVo.getCategory())); } //这里的关系为(keyword1 or keyword2) and category boolQuery.must(keywordQuery); //分页 queryBuilder.withPageable(PageRequest.of(currentPage(goodsQueryVo), goodsQueryVo.getPageSize())); //排序 String sfield = goodsQueryVo.getSfield(); String sm = goodsQueryVo.getSm(); if (!StringUtils.isEmpty(sfield) && !StringUtils.isEmpty(sm)) { queryBuilder.withSort( SortBuilders.fieldSort(sfield) //排序域 .order(SortOrder.valueOf(sm))); //排序方式 } return queryBuilder.withQuery(boolQuery); } public int currentPage(GoodsQueryVo goodsQueryVo) { try { Object currentPage = goodsQueryVo.getCurrent(); return Integer.parseInt(currentPage.toString()) > 0 ? Integer.parseInt(currentPage.toString()) - 1 : 0; } catch (Exception e) { return 0; } } private EsIkResult keywordToken(GoodsQueryVo goodsQueryVo) { HashMap params = new HashMap<>(); params.put("analyzer", "ik_max_word"); params.put("text", goodsQueryVo.getKeyword()); return HttpUtil.post("http://localhost:9200/_analyze?pretty", params, EsIkResult.class); } }