注意:本文使用 Springboot 2.4.3,、elasticsearchRestTemplate,elasticsearch使用的是 7.9.3
主要是学习使用 ElasticsearchRestTemplate 的 API,termQuery、matchQuery、rangeQuery、fuzzyQuery、matchAllQuery、multiMatchQuery、分页查询、高亮查询、排序等查询方式
/**
* 查询所有文档
* {"from":0,"size":10000,"query":{"match_all":{"boost":1.0}},"version":true}
*
* @return
*/
@GetMapping("/matchAllQuery")
public Object matchAllQuery() {
NativeSearchQuery matchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).build();
SearchHits<Brand> searchHits = elasticsearchRestTemplate.search(matchQuery, Brand.class);
return searchHits;
}
/**
* 根据搜索关键字查询文档,搜索关键字不分词
* {"from":0,"size":10000,"query":{"term":{"name":{"value":"小米","boost":1.0}}},"version":true}
*
* @param keyword
* @return
*/
@GetMapping("/termQuery")
public Object termQuery(String keyword) {
NativeSearchQuery termQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.termQuery("name", keyword)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(termQuery, Brand.class);
return search;
}
/**
* 根据搜索关键字分词后的字段搜索文档
* 关键字会分词,其实该方式等价于matchQuery
* {
* "from": 0,
* "size": 10000,
* "query": {
* "common": {
* "name": {
* "query": "宇华华为",
* "high_freq_operator": "OR",
* "low_freq_operator": "OR",
* "cutoff_frequency": 0.01,
* "boost": 1.0
* }* }
* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/commonTermsQuery")
public Object commonTermsQuery(String keyword) {
NativeSearchQuery commonTermsQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.commonTermsQuery("name", keyword)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(commonTermsQuery, Brand.class);
return search;
}
/**
* 模糊匹配、接近匹配
* 查询包含搜索关键字 分词后的字段 的数据
* 如果在一个精确值的字段上使用它,例如数字、日期、布尔或者一个 not_analyzed 字 符串字段,那么它将会精确匹配给定的值:
* {
* "from": 0,
* "size": 10000,
* "query": {
* "match": {
* "name": {
* "query": "中华华为",
* "operator": "OR",
* "prefix_length": 0,
* "max_expansions": 50,
* "fuzzy_transpositions": true,
* "lenient": false,
* "zero_terms_query": "NONE",
* "auto_generate_synonyms_phrase_query": true,
* "boost": 1.0
* }* }
* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/matchQuery")
public Object matchQuery(String keyword) {
NativeSearchQuery matchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchQuery("name", keyword)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(matchQuery, Brand.class);
return search;
}
/**
* 精确匹配查询,搜索关键字不分词 ,针对的是一个语句
*
* @param keyword
* @return
*/
@GetMapping("/matchPhraseQuery")
public Object matchPhraseQuery(String keyword) {
NativeSearchQuery matchPhraseQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchPhraseQuery("name", keyword)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(matchPhraseQuery, Brand.class);
return search;
}
/**
* 这种精准查询满足的条件有点苛刻,有时我们想要包含 ""广东靓仔靓女"" 的文档也能够匹配 "广东靓女"。这时就要以用到 "slop" 参数来控制查询语句的灵活度。
* slop 表示相隔多远时,还能匹配到。比如 搜索关键字为 广东靓女,设置slop为2,表示中间可以相隔为2,所以广东靓仔靓女这条数据也是满足条件的
* {
* "from": 0,
* "size": 10000,
* "query": {
* "match_phrase": {
* "name": {
* "query": "广东靓女",
* "slop": 2,
* "zero_terms_query": "NONE",
* "boost": 1.0
* }* }
* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/matchPhraseQueryWithSlop")
public Object matchPhraseQueryWithSlop(String keyword) {
NativeSearchQuery matchPhraseQueryWithSlop = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchPhraseQuery("name", keyword).slop(2)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(matchPhraseQueryWithSlop, Brand.class);
return search;
}
/**
* 跟matchPhrase作用大致相同,会先进行一次matchPhrase查询,然后根据分词后的最后一个词汇作为前缀模糊查询
* 举个例子:查询列的值: this is a handsome boy ,查询关键字: this is a hand
* es 先进行一次matchPhrase查询筛选出 含有this is a hand 的文档,然后进一步查询 以 hand 开头的数据,如 this is a handsome boy 会命中
* {
* "from": 0,
* "size": 10000,
* "query": {
* "match_phrase_prefix": {
* "name": {
* "query": "宇华华为",
* "slop": 0,
* "max_expansions": 50,
* "boost": 1.0
* }* }
* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/matchPhrasePrefixQuery")
public Object matchPhrasePrefixQuery(String keyword) {
NativeSearchQuery matchPhrasePrefixQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchPhrasePrefixQuery("name", keyword)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(matchPhrasePrefixQuery, Brand.class);
return search;
}
/**
* 根据搜索关键查询文档
* 搜索关键字不分词,测出仅支持单个字去查询?这个的作用还有待验证
*
* @param keyword
* @return
*/
@GetMapping("/prefixQuery")
public Object prefixQuery(String keyword) {
NativeSearchQuery prefixQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.prefixQuery("name", keyword)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(prefixQuery, Brand.class);
return search;
}
/**
* 从多个列中查询包含搜索关键字分词后的字段的数据:如中华华为分词后的字段大概有:中华、华为、华,会从name、brandName、subTile三个列搜索包含分词后的字段的数据
* {
* "from": 0,
* "size": 10000,
* "query": {
* "multi_match": {
* "query": "中华华为",
* "fields": ["brandName^1.0", "name^1.0", "subTitle^1.0"],
* "type": "best_fields",
* "operator": "OR",
* "slop": 0,
* "prefix_length": 0,
* "max_expansions": 50,
* "zero_terms_query": "NONE",
* "auto_generate_synonyms_phrase_query": true,
* "fuzzy_transpositions": true,
* "boost": 1.0
* }* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/multiMatchQuery")
public Object multiMatchQuery(String keyword) {
NativeSearchQuery multiMatchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery(keyword, "name", "subTitle", "brandName")).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(multiMatchQuery, Brand.class);
return search;
}
/**
* 范围查询
* {
* "from": 0,
* "size": 10000,
* "query": {
* "range": {
* "id": {
* "from": 100,
* "to": null,
* "include_lower": false,
* "include_upper": true,
* "boost": 1.0
* }* }
* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/rangeQuery")
public Object rangeQuery(String keyword) {
NativeSearchQuery rangeQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.rangeQuery("id").gt(100)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(rangeQuery, Brand.class);
return search;
}
/**
* 根据正则表达式查询
*
* @param keyword
* @return
*/
@GetMapping("/regexpQuery")
public Object regexpQuery(String keyword) {
NativeSearchQuery regexpQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.regexpQuery("name", "正则表达式")).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(regexpQuery, Brand.class);
return search;
}
/**
* 对多个查询的结果做去重合并
* <p>
* {
* "from": 0,
* "size": 10000,
* "query": {
* "dis_max": {
* "tie_breaker": 0.0,
* "queries": [{
* "match": {
* "name": {
* "query": "华",
* "operator": "OR",
* "prefix_length": 0,
* "max_expansions": 50,
* "fuzzy_transpositions": true,
* "lenient": false,
* "zero_terms_query": "NONE",
* "auto_generate_synonyms_phrase_query": true,
* "boost": 1.0
* }* }
* }, {
* "term": {
* "name": {
* "value": "华为",
* "boost": 1.0
* }
* }
* }],
* "boost": 0
* }
* },
* "version": true
* }
*
* @param keyword
* @return
*/
@GetMapping("/disMaxQuery")
public Object disMaxQuery(String keyword) {
NativeSearchQuery disMaxQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.disMaxQuery().add(QueryBuilders.matchQuery("name", keyword)).add(QueryBuilders.termQuery("name", "华为"))).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(disMaxQuery, Brand.class);
return search;
}
/**
* 实际的搜索中,我们有时候会打错字,从而导致搜索不到。在Elasticsearch中,我们可以使用fuzziness属性来进行模糊查询,从而达到搜索有错别字的情形。
* match查询具有“fuziness”属性。它可以被设置为“0”, “1”, “2”或“auto”。“auto”是推荐的选项,它会根据查询词的长度定义距离。
* 简单的说,就是搜索关键有错别字,es可以会自动进行纠错,假如查询列的数据是周星驰,但用户搜索的时候输入的是周星迟,fuzzyQuery可以将正确的数据查询出来
*
* 搜索华未会将华为的数据搜索出来
* {
* "from": 0,
* "size": 10000,
* "query": {
* "fuzzy": {
* "name": {
* "value": "华未",
* "fuzziness": "1",
* "prefix_length": 0,
* "max_expansions": 50,
* "transpositions": true,
* "boost": 1.0
* }* }
* },
* "version": true
* }
* @param keyword
* @return
*/
@GetMapping("/fuzzyQuery")
public Object fuzzyQuery(String keyword) {
// 默认是auto,修改为只允许修正一个错别字
NativeSearchQuery fuzzyQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.fuzzyQuery("name", keyword).fuzziness(Fuzziness.ONE)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(fuzzyQuery, Brand.class);
return search;
}
/**
* 轻量级字符串搜索,关键字会分词
* @param keyword
* @return
*/
@GetMapping("/queryStringQuery")
public Object queryStringQuery(String keyword) {
// 默认是auto,修改为只允许修正一个错别字
NativeSearchQuery queryStringQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.queryStringQuery(keyword).fuzziness(Fuzziness.ONE)).build();
SearchHits<Brand> search = elasticsearchRestTemplate.search(queryStringQuery, Brand.class);
return search;
}
高亮查询
一、通过注解 + 查询方法的方式
1、定义一个查询方法,并贴上 @Highligh 注解,设置要高亮的字段即可
public interface SpuRepository extends ElasticsearchRepository<EsSpu,String> {
@Highlight(fields = {
@HighlightField(name = "name"),
@HighlightField(name = "caption")
})
List<SearchHit<EsSpu>> findByNameOrCaption(String name, String caption);
}
2、controller层调用
@GetMapping("/highLightSearchMethod")
public Object highLightSearchMethod(String keyword) {
List<SearchHit<EsSpu>> byNameOrCaption = spuRepository.findByNameOrCaption(keyword, keyword);
EsSpu content = null;
for (SearchHit<EsSpu> esSpuSearchHit : byNameOrCaption) {
// 得到对象
content = esSpuSearchHit.getContent();
// 只有一个高亮字段的情况
List<String> highlightField = esSpuSearchHit.getHighlightField("name");
if (highlightField.size() > 0) {
content.setName(highlightField.get(0));
}
}
return byNameOrCaption;
}
3、响应结果
[
{
"index": "spu",
"id": "2511918707000",
"score": 9.83293,
"sortValues": [],
"content": {
"id": "2511918707000",
"sn": "",
"name": "华为(HUAWEI) 华为nova2S 手机 银钻灰 {版本}",
"caption": "华为新品上市,华为nova4",
"introduction":""
"specItems": "",
"paraItems": "",
"saleNum": 0,
"commentNum": 0,
"isMarketable": "1",
"isEnableSpec": "1",
"isDelete": "0",
"status": "1"
},
"highlightFields": {
"name": [
"<em>华为</em>(HUAWEI) <em>华为</em>nova2S 手机 银钻灰 {版本}"
],
"caption": [
"<em>华为</em>新品上市,<em>华为</em>nova4"
]
},
"innerHits": {},
"nestedMetaData": null
}
]
二、 通过 ElasticsearchTemplate 方式
/**
* 简易版高亮查询,这种方式在es 7.9.3已经过时
*/
@GetMapping("/hightLightSearch")
public Object hightLightSearch() {
// 分页
PageRequest page = PageRequest.of(0, 5);
// 设置高亮属性
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("subTitle").preTags("<em style='color:red'>").postTags("</em>");
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(QueryBuilders.termQuery("subTitle", "小米"))
.withHighlightBuilder(highlightBuilder).withPageable(page).build();
AggregatedPage<EsProduct> aggregatedPage = elasticsearchTemplate.queryForPage(query, EsProduct.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<EsProduct> list = new ArrayList<>();
// hits 才是真正保存用户数据的字段
response.getTook();
response.getSuccessfulShards();
SearchHits hits = response.getHits();
hits.getTotalHits();
hits.getMaxScore();
SearchHit[] hits1 = hits.getHits();
EsProduct product = null;
for (SearchHit hit : hits) {
if (hits.totalHits <= 0) {
return null;
}
// 处理每一个实体类型
product = new EsProduct();
// 这种方式为自己手动获取对象的每一个值
product.setId(Long.valueOf(hit.getId()));
product.setBrandName(String.valueOf(hit.getSourceAsMap().get("brandName")));
product.setSubTitle(String.valueOf(hit.getSourceAsMap().get("subTitle")));
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
// 获取高亮的字段
String subTitle = highlightFields.get("subTitle").fragments()[0].toString();
// 重新赋值
product.setSubTitle(subTitle);
list.add(product);
}
return new AggregatedPageImpl<>((List<T>) list);
}
/**
* Map a single {@link SearchHit} to the given {@link Class type}.
*
* @param searchHit must not be {@literal null}.
* @param type must not be {@literal null}.
* @return can be {@literal null}.
* @since 3.2
*/
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
return null;
}
});
return aggregatedPage;
}
三、通过 ElasticsearchRestTemplate 方式
/**
* @param keyword
* @return
*/
@GetMapping("/highLightSearch")
public Object highLightSearch(String keyword) {
// 分页查询
PageRequest page = PageRequest.of(0, 10);
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 设置高亮属性
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("name");
highlightBuilder.preTags("</em style='red'>");
highlightBuilder.postTags("</em>");
// HighlightBuilder.Field tags = new HighlightBuilder.Field(keyword).preTags("</em style='red'>").postTags("</em>");
NativeSearchQuery searchQuery = queryBuilder.withQuery(QueryBuilders.matchQuery("name", keyword)).withPageable(page).withHighlightBuilder(highlightBuilder).build();
// 第一个参数表示查询对象,第二个参数表示返回值,第三个参数表示要查询的索引
SearchHits<EsSpu> searchHits = elasticsearchRestTemplate.search(searchQuery, EsSpu.class, IndexCoordinates.of("spu"));
// elasticsearchRestTemplate.queryForPage()
return searchHits;
}
以上仅为个人学习总结,如有错误欢迎指出
版权声明:本文为weixin_43549350原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。