目录
- 1. 创建ES实体
- 2. 创建查询实体
- 3. 查询方法实现
- 3.1 核心代码
- 3.2 构建查询条件
- 3.2.1 关键词分词
- 3.3 高亮处理
- 4.完整查询代码展示
记,写一个简单的es分词demo,es版本6.8.12
如果使用es7有些方法可能会有所改变,请参考7的文档
es安装教程:http://t.csdn.cn/BSh12
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 Map<String, Object> searchByKeyword(GoodsQueryVo goodsQueryVo) {
//构建查询条件
NativeSearchQueryBuilder queryBuilder = queryBuilder(goodsQueryVo);
//添加高亮域
HighlightBuilder.Field field = new HighlightBuilder.
Field("name"). //指定的高亮域
preTags("<span style=\"color:red\">"). //前缀
postTags("</span>"). //后缀
fragmentSize(100);
queryBuilder.withHighlightFields(field);
//执行搜索。HighlightResultMapper处理高亮文本
AggregatedPage<PblGoodsEsItem> results = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), PblGoodsEsItem.class, new HighlightResultMapper());
//结果集
Map<String, Object> resultMap = new HashMap<String, Object>();
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);
List<String> 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;
}
}
3.2.1 关键词分词
当输入衣服鞋子的时候会将关键字分为衣服,鞋子去查询
@Data
public class EsIkResult {
private List<token> tokens;
@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) {
HashMap<String, Object> 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);
}
3.3 高亮处理
高亮处理mapper,处理高亮数据,复制用即可,不需要记
public class HighlightResultMapper extends DefaultResultMapper {
/***
* 处理结果集
*/
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
//所有数据
for (SearchHit hit : response.getHits()) {
//当前单条数据
Map<String, Object> sourceMap = hit.getSourceAsMap();
//高亮数据
for (Map.Entry<String, HighlightField> 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字段含有关键字的文本替换成<span style=“color:red”> 包含,前端用html回显即可
示例,搜索衣服
//添加高亮域
HighlightBuilder.Field field = new HighlightBuilder.
Field("name"). //指定的高亮域
preTags("<span style=\"color:red\">"). //前缀
postTags("</span>"). //后缀
fragmentSize(100);
queryBuilder.withHighlightFields(field);
//执行搜索。HighlightResultMapper处理高亮文本
AggregatedPage<PblGoodsEsItem> results = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), PblGoodsEsItem.class, new HighlightResultMapper());
4.完整查询代码展示
ps:
高亮处理代码上方已给出,此处只给出搜索代码
HttpUtil是自己封装的http请求类,可以根据自己的方式去发出请求
@Service
public class GoodsEsServiceImpl implements GoodsEsService {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Override
public Map<String, Object> searchByKeyword(GoodsQueryVo goodsQueryVo) {
//构建查询条件
NativeSearchQueryBuilder queryBuilder = queryBuilder(goodsQueryVo);
//添加高亮域
HighlightBuilder.Field field = new HighlightBuilder.
Field("name"). //指定的高亮域
preTags("<span style=\"color:red\">"). //前缀
postTags("</span>"). //后缀
fragmentSize(100);
queryBuilder.withHighlightFields(field);
//执行搜索。HighlightResultMapper处理高亮文本
AggregatedPage<PblGoodsEsItem> results = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), PblGoodsEsItem.class, new HighlightResultMapper());
//结果集
Map<String, Object> resultMap = new HashMap<String, Object>();
resultMap.put("list", results.getContent());
resultMap.put("total", results.getTotalElements());
return resultMap;
}
private NativeSearchQueryBuilder queryBuilder(GoodsQueryVo goodsQueryVo) {
//关键字分词
EsIkResult esIkResult = keywordToken(goodsQueryVo);
List<String> 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<String, Object> 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);
}
}