在作搜索的時候,下拉聯想詞的搜索確定是最多見的一個場景,用戶在輸入的時候,要自動補全詞幹,說得簡單點,就是以...開頭搜索,若是是數據庫,一句SQL就很容易實現,但在elasticsearch如何實現呢?java
你們可能會立馬想到用elasticsearch自帶的suggest功能,確實,在一些初級應用場景,特別是數據量比較少的狀況下,suggest能夠快速簡易的解決問題。正則表達式
在數據量比較大的時候,性能有待提升,並且遇到複雜場景,suggest就會顯得力不從心,看下面一個需求:spring
一、下拉結果須要根據城市過濾 數據庫
二、下拉結果須要根據拼音搜索、首字母搜索、中文拼音混合搜索等app
若是使用suggest,是否是無從下手?框架
下面我介紹另外二種實現的方式,這二種試更加靈活,以上二個問題皆可解決,因爲篇幅,我將在其餘章節具體講解拼音+混合搜索。curl
1、基於正則表達式搜索elasticsearch
要點:索引的時候,使用"keyword"做爲tokenizer,把整個文本看成一個term。性能
curl -XPUT localhost:9200/search_words_index -d '{ "settings" : { "refresh_interval" : "5s", "number_of_shards" : 1, "number_of_replicas" : 1, "analysis" : { "analyzer": { "myAnalyzer": { "type": "custom", "tokenizer": "keyword", "filter": ["lowercase"] } } } }, "mappings": { "search_words_type": { "properties": { "words": { "type": "string", "index": "analyzed", "indexAnalyzer" : "myAnalyzer" } } } } } }'
搜索的時候,可以使用queryStringQuery或者wildcardQuery實現正則表達式查詢,囉嗦一句,queryStringQuery與wildcardQuery的區別是,wildcardQuery是一種低級查詢,不會進行analyzer的,而queryStringQuery則會,更具體區別的可參考官網資料。ui
下面以queryStringQuery方式爲例進行說明,關鍵代碼:
String reg=/key.*/;
QueryBuilders.queryStringQuery(reg).field("words").analyzer("myAnalyzer"));
這種方式的優勢是簡單,索引空間佔用也不大,效率也還能夠,但我更推薦下面的一種式,性能會更佳。
2、基於edge-ngram分詞法
這種方式是典型的以空間換時間的作法,惟一的缺點是會加大索引開銷,索引數據的時間也會加長,但這種開銷都是在索引階段,並不會影響查詢階段,只要有足夠的磁盤和內存空間,效率仍是很不錯的。
要點:索引階段使用edge-ngram分詞,按金字塔式的分割成獨立的term。以下:
中華人民共和國
中華人民共和
中華人民共
中華人民
中華人
中華
中
索引以下:
curl -XPUT localhost:9200/search_words_index -d '{ "settings" : { "refresh_interval" : "5s", "number_of_shards" : 1, "number_of_replicas" : 1, "analysis" : {
"filter": {
"edge_ngram_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 30
},
"analyzer": { "myAnalyzer": { "type": "custom", "tokenizer": "keyword", "filter": ["edge_ngram_filter","lowercase"] } } } }, "mappings": { "search_words_type": { "properties": { "words": { "type": "string", "index": "analyzed", "indexAnalyzer" : "myAnalyzer" } } } } } }'
搜索的時候,直接使用term查詢,若是比較複雜的狀況下,如要按拼音、中文繁體轉換等,則使用matchQuery,先對關鍵字進行一次分析。
QueryBuilders.termQuery("words", key);//低級查詢,速度快
或者
QueryBuilders.matchQuery("words", key).analyzer("xxx");//可指定分詞器來分析關鍵字
這種搜索結果保證必定是以..開頭,由於在索引階段就已經把term限定了。
注意:以上java代碼示例都是基於spring-data-elasticsearch框架。