ES Suggester 搜索自動補全


需求

將商品表數據全量更新至ES索引
商品索引支持Suggester自動補全,支持過濾商品enable和delete_status狀態,只篩選啟用且未刪除的商品
Suggester與普通搜索區別:ES將Suggest機器依賴的字段放在堆內存,實現近實時的搜索提示功能

es安裝ik分詞插件

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.7.0/elasticsearch-analysis-ik-7.7.0.zip

配置logstash配置文件

input {
    stdin {
    }
    jdbc {
      # 連接的數據庫地址和哪一個數據庫,指定編碼格式,禁用SSL協議,設定自動重連
      jdbc_connection_string => "jdbc:mysql://{你的MySQL}:3306/shop-server?characterEncoding=UTF-8&useSSL=false&autoReconnect=true"
      # 你的賬戶密碼
      jdbc_user => "{你的用戶}"
      jdbc_password => "{你的密碼}"
      # 連接數據庫的驅動包,建議使用絕對地址
      jdbc_driver_library => "/data/logstash-6.4.0/bin/mysql/mysql-connector-java-5.1.22-bin.jar"
      # 這是不用動就好
      jdbc_driver_class => "com.mysql.jdbc.Driver"
      jdbc_paging_enabled => "true"
      jdbc_page_size => "2000"

          #處理中文亂碼問題
      codec => plain { charset => "UTF-8"}

      #使用其它字段追蹤,而不是用時間
      use_column_value => true
      #追蹤的字段
      tracking_column => app_goods_id
      record_last_run => true
        statement => "SELECT
        g.app_goods_id,
        g.goods_id,
        g.goods_name,
        g.goods_name AS suggest,
        g.collect_count,
        g.sale,
        g.alone_price,
        g.enable,
        g.delete_status,
         (SELECT GROUP_CONCAT( goods_type_id ) FROM db_goods_type_link WHERE goods_id = g.goods_Id ) AS goods_type_id,
        CASE
                WHEN g.alone_price > 301 THEN
                100
                WHEN g.alone_price > 101 THEN
                300
                WHEN g.alone_price > 51 THEN
                500
                WHEN g.alone_price > 0 THEN
                400 ELSE 0
        END price_score,
        CASE
                b.brand_level
                WHEN 1 THEN
                500
                WHEN 2 THEN
                300
                WHEN 3 THEN
                100
                WHEN 4 THEN
                0 ELSE 0
        END brand_score
        FROM
        db_app_goods g
        LEFT JOIN db_brand b ON g.brand_id = b.brand_id
        LIMIT 2000"

      #上一個sql_last_value值的存放文件路徑, 必須要在文件中指定字段的初始值
      last_run_metadata_path => "/data/logstash-6.4.0/bin/mysql/goods.log"

      jdbc_default_timezone => "Asia/Shanghai"

      #statement_filepath => "mysql/jdbc.sql"


      #是否清除 last_run_metadata_path 的記錄,如果為真那么每次都相當於從頭開始查詢所有的數據庫記錄
      clean_run => false

      # 這是控制定時的,重復執行導入任務的時間間隔,第一位是分鍾
      schedule => "* */1 * * *"
      type => "jdbc"
    }
}


filter {
    json {
        source => "message"
        remove_field => ["message"]
    }
}


output {
    elasticsearch {
        # 要導入到的Elasticsearch所在的主機
        hosts => "127.0.0.1:9200"
        # 要導入到的Elasticsearch的索引的名稱
        index => "goods"
        # 類型名稱(類似數據庫表名)
        #document_type => "appgood"
        # 主鍵名稱(類似數據庫主鍵)
        document_id => "%{app_goods_id}"
        # es 賬號
        user => {你的ES用戶}
        password => {你的ES密碼}
        # 這里配置為當前logstash的相對路徑,該文件配置了輸出的Mapping
        template => "mysql/goods_mapping.json"
        template_name => "goods"
        template_overwrite => true

    }

    stdout {
        # JSON格式輸出
        codec => json_lines
    }
}

創建商品索引映射goods_mapping.json

{
        "template": "goods",
        "settings": {
                "index.refresh_interval": "1s"
        },
        "index_patterns": ["goods"],
        "mappings": {
                "properties": {
                        "suggest": {
                                "type": "completion",
                                "analyzer": "ik_smart",
                                "search_analyzer": "ik_smart",
                                "contexts": [{
                                                "name": "enable_cat",
                                                "type": "category",
                                                "path": "enable"
                                        },
                                        {
                                                "name": "delete_status_cat",
                                                "type": "category",
                                                "path": "delete_status"
                                        }
                                ]
                        },
                        "goods_name": {
                                "type": "text",
                                "analyzer": "ik_max_word",
                                "search_analyzer": "ik_smart"
                        },
                        "goods_type_id": {
                                "type": "keyword"
                        },
                        "app_goods_id": {
                                "type": "long"
                        },
                        "goods_id": {
                                "type": "long"
                        },
                        "collect_count": {
                                "type": "integer"
                        },
                        "sale": {
                                "type": "integer"
                        },
                        "alone_price": {
                                "type": "double"
                        },
                        "brand_score": {
                                "type": "integer"
                        },
                        "enable": {
                                "type": "keyword"
                        },
                        "delete_status" :{
                                "type": "keyword"
                        }
                }
        }
}

使用logstash -f {goods配置文件}啟動logstash,logstash將自動從數據庫查詢並以Mapping創建索引

查看索引是否映射成功
GET goods_dev/_mapping
結果:

{
  "goods_dev" : {
    "mappings" : {
      "properties" : {
        "@timestamp" : {
          "type" : "date"
        },
        "@version" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "alone_price" : {
          "type" : "double"
        },
        "app_goods_id" : {
          "type" : "long"
        },
        "brand_score" : {
          "type" : "integer"
        },
        "collect_count" : {
          "type" : "integer"
        },
        "delete_status" : {
          "type" : "keyword"
        },
        "enable" : {
          "type" : "keyword"
        },
        "goods_id" : {
          "type" : "long"
        },
        "goods_name" : {
          "type" : "text",
          "analyzer" : "ik_max_word",
          "search_analyzer" : "ik_smart"
        },
        "goods_type_id" : {
          "type" : "keyword"
        },
        "price_score" : {
          "type" : "long"
        },
        "sale" : {
          "type" : "integer"
        },
        "suggest" : {
          "type" : "completion",
          "analyzer" : "ik_smart",
          "preserve_separators" : true,
          "preserve_position_increments" : true,
          "max_input_length" : 50,
          "contexts" : [
            {
              "name" : "enable_cat",
              "type" : "CATEGORY",
              "path" : "enable"
            },
            {
              "name" : "delete_status_cat",
              "type" : "CATEGORY",
              "path" : "delete_status"
            }
          ]
        },
        "type" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

使用前綴ContextComplitationSuggester進行聯想詞查詢

POST goods_dev/_search?pretty
{
  "_source": "suggest",
  "suggest": {
    "my-suggest":{
      "prefix":"肌膚",
      "completion":{
        "field":"suggest",
        "skip_duplicates":true,
        "size":10,
        "contexts":{
          "enable_cat":{
             "context":1
          },
          "delete_status_cat":{
            "context":0
          }
        }
      }
    }
  }
}

結果:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "suggest" : {
    "my-suggest" : [
      {
        "text" : "肌膚",
        "offset" : 0,
        "length" : 2,
        "options" : [
          {
            "text" : "“肌膚吸塵器123”香港CHICELAN槿念煙酰胺磨砂沐浴露溫和雙效去角質一瓶=沐浴露+磨砂膏",
            "_index" : "goods_dev",
            "_type" : "_doc",
            "_id" : "1323938082604585019",
            "_score" : 1.0,
            "_source" : {
              "suggest" : "“肌膚吸塵器123”香港CHICELAN槿念煙酰胺磨砂沐浴露溫和雙效去角質一瓶=沐浴露+磨砂膏"
            },
            "contexts" : {
              "enable_cat" : [
                "1"
              ]
            }
          },
          {
            "text" : "“肌膚吸塵器”香港CHICELAN槿念煙酰胺磨砂沐浴露溫和雙效去角質一瓶=沐浴露+磨砂膏",
            "_index" : "goods_dev",
            "_type" : "_doc",
            "_id" : "1323894411586834447",
            "_score" : 1.0,
            "_source" : {
              "suggest" : "“肌膚吸塵器”香港CHICELAN槿念煙酰胺磨砂沐浴露溫和雙效去角質一瓶=沐浴露+磨砂膏"
            },
            "contexts" : {
              "delete_status_cat" : [
                "0"
              ]
            }
          }
        ]
      }
    ]
  }
}

使用SpringDataElasticsearch進行JavaAPI查詢

新建一個固定suggest查詢對象,該對象構建一個查詢context,類似於上面的

"contexts":{
          "enable_cat":{
             "context":1
          },
          "delete_status_cat":{
            "context":0
          }
        }
/**
     * 固定的suggest查詢對象
     */
    private static  Map<String, List<? extends ToXContent>> SUGGESTION_CONTEXT = null;
    static {
        CategoryQueryContext enableCat = CategoryQueryContext.builder().setCategory("1").build();
        CategoryQueryContext deleteStatusCat = CategoryQueryContext.builder().setCategory("0").build();
        Map<String, List<? extends ToXContent>> contexts = new HashMap<>();
        List<CategoryQueryContext> list = new ArrayList<>(1);
        list.add(enableCat);
        contexts.put("enable_cat", list);
        List<CategoryQueryContext> list2 = new ArrayList<>(1);
        list2.add(deleteStatusCat);
        contexts.put("delete_status_cat",list2);
        SUGGESTION_CONTEXT = contexts;
    }

根據關鍵字聯想查詢方法

    @Override
    public List<String> associate(String keyword) {
        //使用suggest進行標題聯想
        CompletionSuggestionBuilder suggest = SuggestBuilders.completionSuggestion("suggest").prefix(keyword).skipDuplicates(true).size(10).contexts(SUGGESTION_CONTEXT);
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        suggestBuilder.addSuggestion("goodsNameSuggest",suggest);

        //查詢
        SearchResponse goodsNameSuggestResp = elasticsearchRestTemplate.suggest(suggestBuilder, goodsIndexName);
        Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> goodsNameSuggest = goodsNameSuggestResp
                .getSuggest().getSuggestion("goodsNameSuggest");
        //處理返回
        List<String> collect = goodsNameSuggest.getEntries().stream().map(x -> x.getOptions().stream().map(y->y.getText().toString()).collect(Collectors.toList())).findFirst().get();
        return CollectionUtils.isEmpty(collect)?Collections.emptyList():collect;
    }

接口測試

接口傳遞keyword關鍵字"韓版",返回

{
  "code": 200,
  "msg": "操作成功",
  "timestamp": "1605173186443",
  "data": [
    "韓版時尚中長款襯衫 Because-t2041",
    "韓版淑女純色休閑褲 Holicholic-sp26265",
    "韓版簡約時尚連衣裙 Happy10-ds1009573",
    "韓版簡約純色襯衫 Holicholic-t26070",
    "韓版簡約經典半身裙 Maybe-baby-sp30591",
    "韓版簡約經典女褲套裝 Holicholic-ds26260",
    "韓版純色休閑T恤 Holicholic-t26074"
  ]
}

 

作者:老王_KICHUN
鏈接:https://www.jianshu.com/p/c78011dd9028
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM