Elasticsearch高階檢索之使用單個字母數位進行分詞N-gram tokenizer(不區分大小寫)【實戰篇】

2022-09-15 12:01:39

一、前言

小編最近在做到一個檢索相關的需求,要求按照一個欄位的每個字母或者數位進行檢索,如果是不設定分詞規則的話,英文是按照單詞來進行分詞的。

小編以7.6.0版本做的功能哈,大家可以根據自己的版本去官網看看,應該區別不大

例子:
C6153PE-冬日戀歌,要可以通過任何一個數位和字母進行檢索到,並且不區分大小寫。c6c6等等!

今天官網上有一些例子,覺得和實戰還是有點區別,小編這裡通過了測試抓緊來記錄一下,希望幫助後來人哈!

二、測試分詞策略

我們進入官網找到我們需要的策略:
Elasticsearch策略官網

N-gram 分詞器
每當遇到指定字元列表中的一個時,ngram標記器首先將文字分解為單詞,然後發出 指定長度的每個單詞的N-gram。

N-gram 就像一個在單詞上移動的滑動視窗——一個指定長度的連續字元序列。它們對於查詢不使用空格或複合詞長的語言很有用。

我們去kibana進行測試分詞策略是否符合我們的要求:

POST _analyze
{
  "tokenizer": "ngram",
  "text": "C6153PE-冬日戀歌"
}

分詞分得細,會導致檢索的效率降低,但是需求如此,沒辦法,最重要的是小編這裡的資料量只有1w,其實換了這種分詞,是無感知的!


分詞策略規則:

ngram分詞器接受以下引數:

引數 解釋
min_gram 以 gram 為單位的最小長度。預設為1.
max_gram 以 gram 為單位的最大字元長度。預設為2.
token_chars 應包含在令牌中的字元類,Elasticsearch 將根據不屬於指定類的字元進行拆分。預設為[](保留所有字元)詳細引數見下表
custom_token_chars 應被視為令牌一部分的自定義字元。例如,將此設定為+-_將使標記器將加號、減號和下劃線符號視為標記的一部分。

min_gram將和設定max_gram為相同的值通常是有意義的。長度越小,匹配的檔案越多,但匹配的質量越低。長度越長,匹配越具體。三元組(長度3)是一個很好的起點。官方比較推薦使用3,可能是因為效率分詞粒度兩不誤吧,這裡不符合小編的,小編這裡使用是1,2,也就是預設的值

token_chars引數 解釋例子
letter 字母,例如a, b,ï或京
digit 數位,例如3或7
whitespace 空白,例如" "或"\n"
punctuation 標點,例如!或"
symbol 標記, 例如$或√
custom 自定義,需要使用 custom_token_chars設定設定的自定義字元

custom_token_chars
應被視為令牌一部分的自定義字元。例如,將此設定為+-_將使標記器將加號、減號和下劃線符號視為標記的一部分。

三、在索引欄位中使用

官方是使用一個欄位進行測試的,這裡小編就直接使用公司的索引進行演示了!
這裡是官網的例子:

下面放出來小編實戰後的例子:

總結就是在settings設定分詞策略,在mappings中進行使用即可!!

PUT /product
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "index": {
      "max_result_window": 100000000
    },
    # 這裡使用分詞策略
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          # 這裡分詞指定下面策略的具體設定的名稱
          "tokenizer": "my_tokenizer",
          # 這裡忽略大小寫設定
          "filter": [
            "lowercase"
          ]
        }
      },
      # 具體策略設定
      "tokenizer": {
        "my_tokenizer": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 2,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "@version": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "cargoNo": {
        "type": "text"
      },
      "name": {
        "type": "text"
      },
      "sort": {
        "type": "integer"
      },
      "attribute13": {
        "type": "text",
        # 在需要的欄位指定我們寫的分詞策略
        "analyzer": "my_analyzer"
      },
      "isDeleted": {
        "type": "integer"
      }
    }
  }
}

四、在springboot中實戰

為了公司,小編只貼上部分條件構建規則:

SearchRequest searchRequest = new SearchRequest("product");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder bool = new BoolQueryBuilder();
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.should(QueryBuilders.matchPhraseQuery("name", model))
         .should(QueryBuilders.matchPhraseQuery("cargoNo", model))
         .should(QueryBuilders.wildcardQuery("cargoNo", "*" + model + "*"))
         // 我們分詞規則的欄位查詢
         .should(QueryBuilders.matchPhraseQuery("attribute13", model));
 bool.must(boolQueryBuilder);
 searchSourceBuilder.query(bool);
 searchRequest.source(searchSourceBuilder);

我們拿著頁面感受一下分詞帶來的效果:

效果實現,隨便一個字母都可以查詢出來,這裡只顯示名稱和一個數位,其實是使用attribute13來進行查詢的,是因為attribute13是名稱的第一個-之前的截出來的。

五、總結

這樣我們就完成了一些客製化化的需求,完美交差,還得是看官網啊!!一定要去看官網!搜了好多都沒有這種的教學,寫出來幫助後來人,但是詳細的還得是看官網哈!小編這裡也是把官網的一些概念寫到了部落格里!!

如果對你有幫助還請不要吝嗇你的發財小手給小編來個一鍵三連哦!謝謝大家了!!


有緣人才可以看得到的哦!!!

點選存取!小編自己的網站,裡面也是有很多好的文章哦!