1. 程式人生 > >Elasticsearch實現類似 like '?%' 搜尋

Elasticsearch實現類似 like '?%' 搜尋

 

在做搜尋的時候,下拉聯想詞的搜尋肯定是最常見的一個場景,使用者在輸入的時候,要自動補全詞幹,說得簡單點,就是以...開頭搜尋,如果是資料庫,一句SQL就很容易實現,但在elasticsearch如何實現呢?

大家可能會立馬想到用elasticsearch自帶的suggest功能,確實,在一些初級應用場景,特別是資料量比較少的情況下,suggest可以快速簡易的解決問題。

在資料量比較大的時候,效能有待提高,而且遇到複雜場景,suggest就會顯得力不從心,看下面一個需求:


1、下拉結果需要根據城市過濾   

2、下拉結果需要根據拼音搜尋、首字母搜尋、中文拼音混合搜尋等

如果使用suggest,是不是無從下手?

下面我介紹另外二種實現的方式,這二種試更加靈活,以上二個問題皆可解決,由於篇幅,我將在其他章節具體講解拼音+混合搜尋。

 

一、基於正則表示式搜尋

要點:索引的時候,使用"keyword"作為tokenizer,把整個文本當作一個term。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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則會,更具體區別的可參考官網資料。

下面以queryStringQuery方式為例進行說明,關鍵程式碼:

String reg=/key.*/;

QueryBuilders.queryStringQuery(reg).field("words").analyzer("myAnalyzer"));

這種方式的優點是簡單,索引空間佔用也不大,效率也還可以,但我更推薦下面的一種式,效能會更佳。

 

二、基於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框架。