ES[7.6.x]學習筆記(十二)高亮 和 搜尋建議

牛初九發表於2020-05-28

ES當中大部分的內容都已經學習完了,今天呢算是對前面內容的查漏補缺,把ES中非常實用的功能整理一下,在以後的專案開發中,這些功能肯定是對你的專案加分的,我們來看看吧。

高亮

高亮在搜尋功能中是十分重要的,我們希望搜尋的內容在搜尋結果中重點突出,讓使用者聚焦在搜尋的內容上。我們看看在ES當中是怎麼實現高亮的,我們還用之前的索引ik_index,前面的章節,我們搜尋過香蕉好吃,但是返回的結果中並沒有高亮,那麼想要在搜尋結果中,對香蕉好吃高亮該怎麼辦呢?我們看看,

POST /ik_index/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "desc": "香蕉好吃"
        }
      }
    }
  },
  "highlight": {
    "fields": {
      "desc": {}
    }
  }
}

我們重點看一下請求體中的highlight部分,這部分就是對返回結果高亮的設定,fields欄位中,指定哪些欄位需要高亮,我們指定了desc欄位,執行一下,看看結果吧。

{
    "took": 73,
    "timed_out": false,
    "_shards": { "total": 1,"successful": 1,"skipped": 0,"failed": 0},
    "hits": {
        "total": {
            "value": 5,
            "relation": "eq"
        },
        "max_score": 1.3948275,
        "hits": [
            {
            "_index": "ik_index",
            "_type": "_doc",
            "_id": "2",
            "_score": 1.3948275,
            "_source": {
                "id": 1,
                "title": "香蕉",
                "desc": "香蕉真好吃"
                },
            "highlight": {
                "desc": [
                        "<em>香蕉</em>真<em>好吃</em>"
                    ]
                }
            }
        ……

我們看到在返回的結果中,增加了highlighthighlight裡有我們指定的高亮欄位desc,它的值是<em>香蕉</em>真<em>好吃</em>,其中“香蕉”和“好吃”欄位在<em>標籤中,前端的小夥伴就可以針對這個<em>標籤寫樣式了。我們再看看程式當中怎麼設定高亮,繼續使用上一節中的搜尋的程式,

    public void searchIndex() throws IOException {
        SearchRequest searchRequest = new SearchRequest("ik_index");
        SearchSourceBuilder ssb = new SearchSourceBuilder();
        QueryBuilder qb = new MatchQueryBuilder("desc","香蕉好吃");
        ssb.query(qb);
        
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("desc");

        ssb.highlighter(highlightBuilder);

        searchRequest.source(ssb);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            String record = hit.getSourceAsString();
            HighlightField highlightField = hit.getHighlightFields().get("desc");

            for (Text fragment : highlightField.getFragments()) {
                System.out.println(fragment.string());
            }
        }
    }

我們重點關注一下HighlightBuilder,我們在傳送請求前,建立HighlightBuilder,並指定高亮欄位為desc。搜尋結束後,我們取結果,從hit當中取出高亮欄位desc,然後列印出fragment,執行一下,看看結果吧,

<em>香蕉</em>真<em>好吃</em>
<em>香蕉</em>真<em>好吃</em>
橘子真<em>好吃</em>
桃子真<em>好吃</em>
蘋果真<em>好吃</em>

完全符合預期,“香蕉好吃”被分詞後,在搜尋結果中都增加了<em>標籤,我們可不可以自定義高亮標籤呢?當然是可以的,我們稍微改一下程式就可以了,

HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("desc");

highlightBuilder.preTags("<b>");
highlightBuilder.postTags("</b>");

ssb.highlighter(highlightBuilder);

HighlightBuilder中,使用preTags新增起始標籤,指定為<b>,用postTags新增閉合標籤,指定為</b>,再執行一下,看看結果,

<b>香蕉</b>真<b>好吃</b>
<b>香蕉</b>真<b>好吃</b>
橘子真<b>好吃</b>
桃子真<b>好吃</b>
蘋果真<b>好吃</b>

結果完全正確,用<b>替換了<em>,是不是很靈活。接下來我們再看看搜尋建議。

搜尋建議

“搜尋建議”這個功能也是相當實用的,當我們在搜尋框中輸入某個字時,與這個字的相關搜尋內容就會羅列在下面,我們選擇其中一個搜尋就可以了,省去了敲其他字的時間。我們看看ES中是怎麼實現“搜尋建議”的。

如果要在ES中使用“搜尋建議”功能,是需要特殊設定的,要設定一個型別為completion的欄位,由於之前的索引中已經有了資料,再新增欄位是會報錯的,索引我們新建一個索引,

PUT /my_suggester
{
    "settings":{
        "analysis":{
            "analyzer":{
                "default":{
                    "type":"ik_max_word"
                }
            }
        }
    },
    "mappings":{
        "dynamic_date_formats": [
             "MM/dd/yyyy",
             "yyyy/MM/dd HH:mm:ss",
             "yyyy-MM-dd",
             "yyyy-MM-dd HH:mm:ss"
         ],
        "properties":{
            "suggest":{
                "type":"completion"
            }
        }
    }
}

這已經成了我們新建索引的一個標配了,指定分詞器為ik中文分詞,動態欄位的時間對映格式,以及搜尋建議欄位,注意suggest欄位的型別為completion。我們再新增欄位的時候,就要為suggest欄位新增值了,如下:

POST /my_suggester/_doc
{
    "title":"天氣",
    "desc":"今天天氣不錯",
    "suggest": {
       "input": "天氣"
    }
}

POST /my_suggester/_doc
{
    "title":"天空",
    "desc":"藍藍的天空,白白的雲",
    "suggest": {
       "input": "天空"
    }
}

我們向索引中新增了兩條資料,大家需要額外注意的是suggest欄位的賦值方法,要使用input,我們看一下資料,

suggest欄位並沒有像其他欄位那樣展示出來,說明它和其他欄位是不一樣的。現在我們如果只輸入一個“天”字,看看搜尋建議能不能給出提示,如下:

POST /my_suggester/_search
{
  "suggest": {
    "s-test": {
      "prefix": "天",
      "completion": {
        "field": "suggest"
      }
    }
  }
}

在請求體中,suggest就是“搜尋建議”的標識,s-test是自定義的一個名稱,prefix是字首,也就是我們輸入的“天”字,completion指定搜尋建議的欄位,我們看看查詢的結果,

……
"suggest": {
    "s-test": [
        {
            "text": "天",
            "offset": 0,
            "length": 1,
            "options": [{
                "text": "天氣",
                "_index": "my_suggester",
                "_type": "_doc",
                "_id": "QtgAWnIBOZNtuLQtJgpt",
                "_score": 1,
                "_source": { "title": "天氣","desc": "今天天氣不錯","suggest": { "input": "天氣"}}
        	}
        	,
            {
                "text": "天空",
                "_index": "my_suggester",
                "_type": "_doc",
                "_id": "T9gAWnIBOZNtuLQtWQoX",
                "_score": 1,
                "_source": { "title": "天空","desc": "藍藍的天空,白白的雲","suggest": { "input": "天空"}}
            }
        ]
        }
    ]
}

s-test.options裡,包含了兩條記錄,text欄位就是我們寫的建議欄位,後面_source裡還包含對應的資料,下面我們再看看程式裡怎麼使用“搜尋建議”,

public void searchSuggest(String prefix) throws IOException {
    SearchRequest searchRequest = new SearchRequest("my_suggester");
    SearchSourceBuilder ssb = new SearchSourceBuilder();

    CompletionSuggestionBuilder suggest = SuggestBuilders
        .completionSuggestion("suggest")
        .prefix(prefix);

    SuggestBuilder suggestBuilder = new SuggestBuilder();
    suggestBuilder.addSuggestion("s-test",suggest);

    ssb.suggest(suggestBuilder);

    searchRequest.source(ssb);
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

    CompletionSuggestion suggestion = response.getSuggest().getSuggestion("s-test");
    for (CompletionSuggestion.Entry.Option option : suggestion.getOptions()) {
        System.out.println(option.getText().string());
    }

}

@Test
public void searchSuggest() throws IOException {
    eService.searchSuggest("天");
}

我們建立了CompletionSuggestionBuilder,通過方法completionSuggestion指定“搜尋建議”欄位suggest,並且指定字首為方法傳入的prefix,我們在測試的時候傳入"天"字。然後,我們自定義“搜尋建議”的名字為s-test,傳入前面構造好的suggest

傳送請求後,在響應中獲取前面自定義的s-test,然後迴圈options,取出text欄位,這就是搜尋建議的欄位,我們執行一下,看看結果,

天氣
天空

完全符合預期,這樣使用者在搜尋的時候,就會給出提示資訊了。

好了,今天這兩個ES的知識點就全部OK了~ 大家有問題在評論區留言。

相關文章