基於elasticSearch實現自動補全
-
爲什麼要用es來實現?
因爲能共用一個搜索服務,並且穩定,能利用已有的分詞器。
-
有多少種實現方法?本文用的是哪一種?
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html
本文用的
completion
suggester 來實現的。 -
已經有那麼多文章了,爲何還要寫?
- 很多文章沒有及時更新
- 記錄自己的理解
整體流程
- 添加索引,並單獨寫一個字節點用來實現自動補全,並設置類型爲completion
PUT search_index
{
"mappings": {
"properties": {
"fileName": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"suggest": {
"type": "completion",
"analyzer": "ik_max_word"
}
}
},
"path": {
"type": "keyword",
"index": false
}
}
}
}
- 插入數據
POST /search_index/_doc
{
"fileName":"四川成都市龍泉驛生態環境局龍泉驛區3D氣溶膠雷達監控採購項目的中標公告"
}
POST /search_index/_doc
{
"fileName":"氣溶膠雷達租賃服務報價清單"
}
POST /search_index/_doc
{
"fileName":"四川省遂寧市安居生態環境局臭氧在線監測預警系統採購項目競爭性磋商終止(廢標)公告"
}
- 實現搜索補全
POST /search_index/_search
{
"suggest": {
"my_suggest_document": {
"prefix": "四",
"completion": {
"field": "fileName.suggest"
}
}
}
}
JAVA基於RestHighLevelClient客戶端的實現
-
構建配置客戶端連接
@Configuration @EnableElasticsearchRepositories public class ElasticsearchConfiguration extends AbstractElasticsearchConfiguration { @Override @Bean public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("192.168.15.207:9200") //.withConnectTimeout(Duration.ofSeconds(5)) //.withSocketTimeout(Duration.ofSeconds(3)) //.useSsl() //.withDefaultHeaders(defaultHeaders) //.withBasicAuth(username, password) // ... other options .build(); return RestClients.create(clientConfiguration).rest(); } // @Bean // public ElasticsearchRestTemplate restTemplate() throws Exception { // return new ElasticsearchRestTemplate(elasticsearchClient()); // } }
-
配置Java bean
@Data @AllArgsConstructor @NoArgsConstructor @Document(indexName = "search_index", type = "_doc", shards = 5, replicas = 1) public class DocumentPo implements Serializable { private static final long serialVersionUID = 3433260756083989671L; @Id public String id; @Field(type = FieldType.Keyword) // @Field(type = FieldType.Text, analyzer = "ik_max_word") public String fileName; @Field(index = false, type = FieldType.Keyword) public String compassPath; }
-
添加數據
public interface DocumentRepository extends ElasticsearchRepository<DocumentPo,Long> { } //添加 @Autowired DocumentRepository documentRepository; documentRepository.save(documentPo);
-
實現搜索
@Autowired private RestHighLevelClient restHighLevelClient; @RequestMapping(value = "/by_contact",method = RequestMethod.GET) public Object getSearchSuggest(HttpServletRequest request, @RequestParam(value = "keyWord")String keyWord) { //指定在哪個字段搜索 String suggestField = "fileName.suggest"; SearchRequest searchRequest = new SearchRequest("search_index"); searchRequest.types("_doc"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder qb = QueryBuilders.boolQuery(); SuggestionBuilder termSuggestionBuilder =SuggestBuilders.completionSuggestion(suggestField).prefix(keyWord).skipDuplicates(true).size(10); SuggestBuilder suggestBuilder = new SuggestBuilder(); suggestBuilder.addSuggestion("my_suggest_document", termSuggestionBuilder ); searchSourceBuilder.suggest(suggestBuilder); searchRequest.source(searchSourceBuilder); SearchResponse response = null; try { response = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT); } catch (Exception e) { System.out.println(e.getMessage()); } Suggest suggest = response.getSuggest(); List<String> keywords = null; if (suggest != null) { keywords = new ArrayList<>(); List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> entries = suggest.getSuggestion("my_suggest_document").getEntries(); for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> entry: entries) { for (Suggest.Suggestion.Entry.Option option: entry.getOptions()) { String keyword = option.getText().string(); if (!StringUtils.isEmpty(keyword)) { if (keywords.contains(keyword)) { continue; } keywords.add(keyword); if (keywords.size() >= 9) { break; } } } } } return keywords; }
多個條件的搜索
public SearchResponse autosuggestSearch() throws IOException {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder qb = QueryBuilders.boolQuery();
PrefixQueryBuilder namePQBuilder = QueryBuilders.prefixQuery("address", "usa");
PrefixQueryBuilder addressPQBuilder = QueryBuilders.prefixQuery("address", "usa");
qb.should(namePQBuilder);
qb.should(addressPQBuilder); //Similarly add more fields prefix queries.
sourceBuilder.query(qb);
SearchRequest searchRequest = new SearchRequest("employee").source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("Search JSON query \n" + searchRequest.source().toString()); //Generated ES search JSON.
return searchResponse;
}
參考網址
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html