1. 程式人生 > >ElasticSearch使用入門及拼音搜尋介紹

ElasticSearch使用入門及拼音搜尋介紹

最近有個專案需要用到拼音搜尋並高亮顯示所匹配的中文,其實拼音搜尋可以通過將中文轉化為拼音儲存在庫表如mysql中,然後通過sql like語句查詢搜尋到對應的中文,在一些併發要求並不高、中文已知的情況下是完全可以做到拼音搜尋。但是由於專案要求不僅能夠搜尋出來對應的中文,但是對於高亮顯示匹配的中文難度成本就比較高了,此時需要通過中文拼音分詞來實現。
經過一番調研,鑑於elasticsearch社群的活躍,及相關拼音分詞外掛也比較豐富,選用ES作為搜尋方案。雖然現在ES版本已經到了6.x了,而且每隔一個月左右都會有新版本釋出,由於機器條件等的限制,ES版本越高越吃記憶體和CPU,所以選擇2.4.2版本,穩定、佔用記憶體小、支援叢集,滿足本次需求,如果空間不夠也可多安裝幾個ES組成叢集。

es安裝

官網選擇2.4.2的linux版本tgz安裝包下載上傳到linux伺服器,解壓後在bin中啟動,兩種啟動方式:
1.前臺啟動./elasticsearch,啟動日誌顯示在前臺console,關閉會話則關閉ES
2.後臺啟動./elasticsearch -d,啟動日誌列印在logs資料夾下。
此時可通過linux的curl命令來請求ES的http restful介面建立索引、type、增刪改查等基本操作,也可通過Post等工具請求ES的Http介面。本文通過head工具進行操作。

安裝plugin-head

使用head外掛是ES必備的工具,除了可以操作請求restful介面操作ES資料庫,還可以用於觀察ES的狀態等。
通過在bin檔案下執行命令./plugin install mobz/elasticsearch-head

,ES則自動到github上下載並安裝。
通過ip:port/_plugin/head即可訪問ES,port預設為9200,如果發現不能訪問,則需要在config檔案下的elasticsearch.yml修改或新增如下網路配置,然後重啟es後再通過ip:port/_plugin/head訪問即可(http.port可配置)。
安裝head後的效果圖1

elasticsearch.yml配置,暫未知es的關閉方法,可通過kill命令關閉es

#網路及埠配置
network.host: 0.0.0.0
http.port: 11192

建立索引index

ES的Restful介面語法規則一般是用json來進行資料儲存或設定,可通過head外掛中的【複合查詢】選單欄進行操作,支援多數HTTP傳輸模式。
head http操作介面

ES中的索引相當於mysql的庫,type相當於mysql中的表。
ES建立索引的方式也很簡單,通過http PUT介面即可建立一個索引,如下:

http://172.168.1.11:11192/es_dev_v1
//通過對以上介面進行PUT操作,即可建立名為 es_dev_v1 的索引

建立完成後如下
index建立

建立type,並建立mapping

ES預設是可以不用設定type的mapping的,可以直接通過http儲存資料,ES會根據儲存的json資料自動建立一個type及對應的欄位mapping,如下

http://172.168.1.11:11192/es_dev_v1/temp/12

//傳輸方式:POST
{
  "name":"test",
  "address":"America",
  "tel":"132134542"
}
//通過POST儲存資料,即在es_dev中的temp type(表)中儲存了一條id=12的資料,如果id不傳,則ES會自動建立一個id用於引用這條資料。

新增後的資料及mapping如下,3個欄位都預設為String型別
新增資料

通過訪問http://172.168.1.11:11192/es_dev_v1
資料mapping

如何修改索引欄位型別(即mapping)

ES索引一旦建立,不支援修改索引的mapping,如修改某個欄位的型別、分詞、搜尋規則等,ES的規則是欄位型別只能是建立索引的時候就設定好或者由ES自動設定,如果要修改只能重新建立一個索引index,然後重新設定mapping.當然線上生產環境是不允許這麼做的。
可以借用別名來切換ES庫表,建立一個索引es_dev_v1時,指定它的別名為es_dev,當需要修改mapping時,再建立一個索引es_dev_v2設定新的mapping,然後將資料刷入es_dev_v2,刪除es_deves_dev_v1的別名關係,重新建立es_deves_dev_v2的關聯。此時使用es_dev即可訪問索引庫。
參考:https://blog.csdn.net/lengfeng92/article/details/38230521

使用搜索

網上百度或部落格上對於Http或Java客戶端搜尋有很多的文章講解得很清楚,本文就不再贅述,再下節拼音搜尋會簡單搜尋。

中文拼音分詞工具使用

下載對應版本的拼音搜尋外掛,解壓copy至elasticsearch-2.4.2/plugins/pinyin資料夾下面,其中包括elasticsearch-analysis-pinyin-1.8.2.jarnlp-lang-1.7.jarplugin-descriptor.properties檔案,重啟es服務,如下圖
拼音外掛安裝

  • 建立索引新增拼音分詞
    給索引es_dev_v3新增拼音分析工具
http://172.168.1.11:11192/es_dev_v3   PUT

{
    "index" : {
        "analysis" : {
            "analyzer" : {
                "pinyin_analyzer" : {
                    "tokenizer" : "my_pinyin"
                    }
            },
            "tokenizer" : {
                "my_pinyin" : {
                    "type" : "pinyin",
                    "keep_separate_first_letter" : false,
                    "keep_full_pinyin" : true,
                    "keep_original" : true,
                    "limit_first_letter_length" : 10,
                    "lowercase" : true,
                    "remove_duplicated_term" : true
                }
            }
        }
    }
}
  • 測試分詞工具

訪問http://172.168.1.11:11192/es_dev_v3/_analyze?text=劉德華&analyzer=pinyin_analyzer,返回拼音分詞結果如下,可看出elasticsearch-analysis-pinyin支援拼音分詞和首字母縮寫

{
    "tokens": [
        {
            "token": "liu",
            "start_offset": 0,
            "end_offset": 1,
            "type": "word",
            "position": 0
        },
        {
            "token": "de",
            "start_offset": 1,
            "end_offset": 2,
            "type": "word",
            "position": 1
        },
        {
            "token": "hua",
            "start_offset": 2,
            "end_offset": 3,
            "type": "word",
            "position": 2
        },
        {
            "token": "劉德華",
            "start_offset": 0,
            "end_offset": 3,
            "type": "word",
            "position": 3
        },
        {
            "token": "ldh",
            "start_offset": 0,
            "end_offset": 3,
            "type": "word",
            "position": 4
        }
    ]
}
  • 配置mapping
    mapping新增如下設定,表示user表中的userName欄位採用拼音分詞搜尋功能,而userPhone欄位則不進行分詞。ES預設對所有String型別的欄位進行分詞,意味著搜尋是一個一個詞去匹配搜尋的,如果不需要分詞,則設定為not_analyzed即可,此時則只支援精確匹配該欄位搜尋。
http://172.168.1.11:11192/es_dev_v3/user/_mapping   POST

{
  "user": {
    "properties": {
      "userName": {
        "type": "string",
        "fields": {
          "pinyin": {
            "type": "string",
            "store": false,
            "term_vector": "with_offsets",
            "analyzer": "pinyin_analyzer",
            "boost": 10
          }
        }
      },
      "userAddress": {
        "type":"string",
        "similarity": "classic"
      },
      "userPhone": {
        "type":"string",
        "index": "not_analyzed"
      }
    }
  }
}
  • 搜尋
    以下均用match_phrase短語搜尋匹配,其它搜尋如termmatch_all等感興趣的同學可以在網上搜下其它技術部落格研究下。
    1.通過POST介面上傳demo資料
    部分資料

2.userName搜尋

POST請求介面 http://172.168.1.11:11192/es_dev_v3/user/_search
{
  "query": {
    "match_phrase": {
      "userName": "張"
    }
  },
  "highlight": {
    "fields": {
      "userName": {}
    }
  }
}

返回結果
{
    "took": 126,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 0.19178301,
        "hits": [
            {
                "_index": "es_dev_v3",
                "_type": "user",
                "_id": "1",
                "_score": 0.19178301,
                "_source": {
                    "userName": "張三",
                    "userAddress": "深圳南山",
                    "userPhone": "1387897454"
                },
                "highlight": {
                    "userName": [
                        "<em>張</em>三"
                    ]
                }
            }
        ]
    }
}

3.userName拼音搜尋

POST請求 http://172.168.1.11:11192/es_dev_v3/user/_search
{
  "query": {
    "match_phrase": {
      "userName.pinyin": "zhang"
    }
  },
  "highlight": {
    "fields": {
      "userName.pinyin": {}
    }
  }
}

返回結果
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 1.5342641,
        "hits": [
            {
                "_index": "es_dev_v3",
                "_type": "user",
                "_id": "1",
                "_score": 1.5342641,
                "_source": {
                    "userName": "張三",
                    "userAddress": "深圳南山",
                    "userPhone": "1387897454"
                },
                "highlight": {
                    "userName.pinyin": [
                        "<em>張</em>三"
                    ]
                }
            }
        ]
    }
}
  • 如何修改index設定
http://172.168.1.11:11192/es_im_dev/_close   POST  關閉索引
http://172.168.1.11:11192/es_im_dev/_settings   PUT

{
  "index": {
    "analysis": {
      "analyzer": {
        "pinyin_analyzer": {
          "tokenizer": "my_pinyin"
        }
      },
      "tokenizer": {
        "my_pinyin": {
          "type": "pinyin",
          "keep_separate_first_letter": true,
          "keep_full_pinyin": true,
          "keep_joined_full_pinyin": false,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "lowercase": true,
          "remove_duplicated_term": true
        }
      }
    }
  }
}

http://172.168.1.11:11192/es_im_dev/_open   POST  開啟索引

ES其它設定

  • ES的記憶體設定
    通過/elasticsearch-2.4.2/bin/elasticsearch.in.sh檔案可修改ES的記憶體。
# check in case a user was using this mechanism
if [ "x$ES_CLASSPATH" != "x" ]; then
    cat >&2 << EOF
Error: Don't modify the classpath with ES_CLASSPATH. Best is to add
additional elements via the plugin mechanism, or if code must really be
added to the main classpath, add jars to lib/ (unsupported).
EOF
    exit 1
fi

ES_CLASSPATH="$ES_HOME/lib/elasticsearch-2.4.2.jar:$ES_HOME/lib/*"
# 記憶體大小設定
if [ "x$ES_MIN_MEM" = "x" ]; then
    ES_MIN_MEM=256m
fi
if [ "x$ES_MAX_MEM" = "x" ]; then
    ES_MAX_MEM=1g
fi
if [ "x$ES_HEAP_SIZE" != "x" ]; then
    ES_MIN_MEM=$ES_HEAP_SIZE
    ES_MAX_MEM=$ES_HEAP_SIZE
fi

# min and max heap sizes should be set to the same value to avoid
# stop-the-world GC pauses during resize, and so that we can lock the
# heap in memory on startup to prevent any of it from being swapped
  • ES日誌設定
    ES的日誌遵循log4j的設定模式,有多種型別可選擇。ES預設日誌是每天滾動記錄,預設型別(type)為dailyRollingFile的方式,通過修改elasticsearch-2.4.2/config/logging.yml檔案可設定日誌記錄方式為rollingFile,並設定日誌記錄數量maxBackupIndex及大小maxFileSize即可有效減小日誌大小、防止吃滿硬碟。
appender:
  console:
    type: console
    layout:
      type: consolePattern
      conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n"

  file:
    type: rollingFile
    file: ${path.logs}/${cluster.name}.log
    maxFileSize: 10000000
    maxBackupIndex: 5
    layout:
      type: pattern
      conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %.10000m%n"