1. 程式人生 > >Elasticsearch全文檢索

Elasticsearch全文檢索

簡介

  Elasticsearch是一個實時分散式搜尋和分析引擎,用於全文搜尋、結構化搜尋、分析以及將這三者混合使用。Elasticsearch實現全文索引,需要使用分詞器,預設使用的standard分詞器。由於本文介紹的是中文的全文檢索,因此我們需要使用第三方外掛的中文分詞器,如ik分詞器、mmsegf分詞器,在此我們主要使用的是ik分詞器。

安裝ik分詞器

1.首先,我們需要根據Elasticsearch的版本來選擇使用哪一個版本的ik分詞器,如下:

IK version ES version
master 2.2.0->master
1.8.0 2.2.0
1.7.0 2.1.1
1.6.1 2.1.0
1.5.0 2.0.0
1.4.1 1.7.2

由於我們的Elasticsearch的版本為1.7.3,因此我們使用ik-1.4.1。

wget https://github.com/medcl/elasticsearch-analysis-ik/archive/v1.4.1.tar.gz
tar -zxvf v1.4.1.tar.gz 
cd elasticsearch-analysis-ik-1.4.1
cp -rf /home/test/ELKstack/elasticsearch-analysis-ik-1.4
.1/config/ik /usr/local/elasticsearch-1.7.3/config #需要安裝maven並配置環境變數 vim /etc/profile export MAVEN_HOME=/usr/local/maven export PATH=${PATH}:${MAVEN_HOME}/bin export M2_REPO=/usr/local/maven/repository #安裝 mvn package #安裝完畢後,將其放到elasticsearch的外掛目錄下 unzip /home/test/ELKstack/elasticsearch-analysis-ik-1.4.1target/releases/elasticsearch-analysis-ik-1.4
.1.zip -d /usr/local/elasticsearch-1.7.3/plugins/ik

2.配置elasticsearch.yml

vim /usr/local/elasticsearch-1.7.3/config/elasticsearch.yml
index:
  analysis:
    analyzer:
      ik:
          alias: [ik_analyzer]
          type: org.elasticsearch.index.analysis.IkAnalyzerProvider
      ik_max_word:
          type: ik
          use_smart: false
      ik_smart:
          type: ik
          use_smart: true
或者
index.analysis.analyzer.ik.type : "ik"

以上兩種配置方式的區別:
(1)、第二種方式,只定義了一個名為 ik 的 analyzer,其 use_smart 採用預設值 false

(2)、第一種方式,定義了三個 analyzer,分別為:ik、ik_max_word、ik_smart,其中 ik_max_word 和 ik_smart 是基於 ik 這個 analyzer 定義的,並各自明確設定了 use_smart 的不同值。

(3)、其實,ik_max_word 等同於 ik。ik_max_word 會將文字做最細粒度的拆分,比如會將“中華人民共和國國歌”拆分為“中華人民共和國,中華人民,中華,華人,人民共和國,人民,人,民,共和國,共和,和,國國,國歌”,會窮盡各的組合;而 ik_smart 會做最粗粒度的拆分,比如會將“中華人民共和國國歌”拆分為“中華人民共和國,國歌”。

因此,建議,在設定 mapping 時,用 ik 這個 analyzer,以儘可能地被搜尋條件匹配到。

3.重啟elasticsearch

/etc/init.d/elasticsearch start

檢查日誌檢視ik外掛是否載入成功。

分詞測試

1.建立索引

curl -XPOST http://10.10.20.16:9200/index

2.建立mapping

curl -XPOST http://10.10.20.16:9200/index/test/_mapping?pretty -d '
{
  "test":{
           "properties":{
              "NewString":{"type":"string","indexAnalyzer":"ik","searchAnalyzer":"ik"},
              "OriginString":{"type":"string","indexAnalyzer":"ik","searchAnalyzer":"ik"},
              "townname":{"type":"string","indexAnalyzer":"ik","searchAnalyzer":"ik"},
              "distname":{"type":"string","indexAnalyzer":"ik","searchAnalyzer":"ik"},
              "haclcode":{"type":"string"},
              "towncode":{"type":"string"},
              "distcode":{"type":"string"},
              "WordClass":{"type":"string"},
              "WordCode":{"type":"string"},
              "x":{"type":"float"},
              "y":{"type":"float"}
          }
    }
}'

若在後期新增新欄位,需要更新mapping

curl -XPUT http://10.10.20.16:9200/index/_mapping/test -d '
 {
    "properties":{
        "OriginString":{
           "type":"string",
           "indexAnalyzer":"ik",
           "searchAnalyzer":"ik"
          }
      }
 }'

3.匯入資料
匯入資料使用bulk api,處理海量資料時會極大的提高效率。
bulk api的請求格式為:

{ action: { metadata }}\n
{ request body        }\n
{ action: { metadata }}\n
{ request body        }\n

這種格式就類似於一個用”\n”字元來連線的單行json一樣。
因此使用bulk api需要將我們的原始資料格式化為上面的json格式。
例如:
我們的原始資料為:

CL029103CHY4064 城陽職教中心    ha      指教中心        CHY     城陽區  null    null    120.38120840204475      36.30916300197122       ed
CL029103CHY4064 城陽職教中心    ha      明陽路職教      CHY     城陽區  null    null    120.38120840204475      36.30916300197122       ed
CL029103CHY4064 城陽職教中心    ha      職教中心        CHY     城陽區  null    null    120.38120840204475      36.30916300197122       ed

通過python進行格式化:

vim format.py 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
names = ['WordCode', 'NewString', 'WordClass', 'OriginString', 'distcode', 'distname', 'towncode', 'townname', 'x', 'y', 'haclcode']

with open('/home/test/text', 'r') as f:
        data = f.readlines()

s = '{"index":{"_index":"index","_type":"test"}}'

for line in data:
        values = line.strip().split()
        dictionary = dict(zip(names, values))
        #格式化為json
        #解決list,dict亂碼
        json_data = json.dumps(dictionary).decode("unicode-escape")
        with open('/home/test/out', 'a') as f1:
                print >> f1, s
        with open('/home/test/out', 'a') as f2:
                print >> f2, json_data.encode("utf-8")

格式化後的out檔案為:

{"haclcode": "ed", "townname": "null", "distname": "城陽區", "towncode": "null", "distcode": "CHY", "WordClass": "ha", "OriginString": "指教中心", "y": "36.30916300197122", "x": "120.38120840204475", "WordCode": "CL029103CHY4064", "NewString": "城陽職教中心"}
{"index":{"_index":"test","_type":"supersearch"}}
{"haclcode": "ed", "townname": "null", "distname": "城陽區", "towncode": "null", "distcode": "CHY", "WordClass": "ha", "OriginString": "明陽路職教", "y": "36.30916300197122", "x": "120.38120840204475", "WordCode": "CL029103CHY4064", "NewString": "城陽職教中心"}
{"index":{"_index":"test","_type":"supersearch"}}
{"haclcode": "ed", "townname": "null", "distname": "城陽區", "towncode": "null", "distcode": "CHY", "WordClass": "ha", "OriginString": "職教中心", "y": "36.30916300197122", "x": "120.38120840204475", "WordCode": "CL029103CHY4064", "NewString": "城陽職教中心"}

匯入資料

curl http://10.10.20.16:9200/index/_bulk?pretty --data-binary @out

注意:out檔案的json格式,每行後面必須跟\n
4.檢索資料

全欄位查詢

curl -XPOST  http://10.10.20.16:9200/index/test/_search?pretty  -d'
{
    "query" : { "match" : { "_all" : "職教" }}
}'

_all欄位在新欄位探索階段比較管用,當你還不清楚文件的最終結構時,可以將任何查詢用於這個欄位。

精確查詢

curl -XPOST http://10.10.20.16:9200/index/test/_search?pretty  -d'
{
    "query" : { "term" : { "OriginString" : "職教" }}
}'
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}

由於是精確匹配,不會進行分詞,因此沒有匹配項。

模糊查詢

curl -XPOST  http://10.10.20.16:9200/index/test/_search?pretty  -d'
{
    "query" : { "match" : { "OriginString" : "職教" }}
}'
{
  "took" : 16,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 10,
    "max_score" : 2.7033195,
    "hits" : [ {
      "_index" : "test",
      "_type" : "supersearch",
      "_id" : "AVOhG4x9J_-fAShDYmCN",
      "_score" : 2.7033195,
      "_source":{"haclcode": "ed", "townname": "null", "distname": "城陽區", "towncode": "null", "distcode": "CHY", "WordClass": "ha", "OriginString": "指教中心", "y": "36.30916300197122", "x": "120.38120840204475", "WordCode": "CL029103CHY4064", "NewString": "城陽職教中心"}
    }, {
      "_index" : "test",
      "_type" : "supersearch",
      "_id" : "AVOhG4x9J_-fAShDYmCM",
      "_score" : 2.5207999,
      "_source":{"haclcode": "ed", "townname": "null", "distname": "城陽區", "towncode": "null", "distcode": "CHY", "WordClass": "ha", "OriginString": "職教中心", "y": "36.30916300197122", "x": "120.38120840204475", "WordCode": "CL029103CHY4064", "NewString": "城陽職教中心"}
    }, {
      "_index" : "test",
      "_type" : "supersearch",
      "_id" : "AVOhG4x9J_-fAShDYmCL",
      "_score" : 2.2980154,
      "_source":{"haclcode": "ed", "townname": "null", "distname": "城陽區", "towncode": "null", "distcode": "CHY", "WordClass": "ha", "OriginString": "明陽路職教", "y": "36.30916300197122", "x": "120.38120840204475", "WordCode": "CL029103CHY4064", "NewString": "城陽職教中心"}
    }]
   }
}

多詞查詢

curl -XPOST  http://10.10.20.16:9200/index/test/_search?pretty  -d'
{
    "query" : { "match" : { "OriginString" : {"query":"城陽 中心","operator":"and" }}}
}'

跨欄位查詢

curl -XPOST  http://10.10.20.16:9200/index/test/_search?pretty  -d'
{
    "query" : { "multi_match" : { "query":"職教中心","fields":["OriginString","NewString"]} }
}'

總結

  Elasticsearch的全文檢索功能我們介紹的只是入門,有好多細節性的東西需要我們在測試或使用過程中慢慢去了解,如結構化查詢、過濾操作,都可以幫助我們有效分析資料檔案。另外,此處我們是將資料庫的匯出到文字檔案,然後自行格式化為bulk適用的json格式,在生產環境中我們也可以藉助相關外掛,直接將資料庫資料匯入到elasticsearch中,大大提高了效率。