全文檢索工具elasticsearch:第一章:理論知識

搜索

什么是搜索, 計算機(jī)根據(jù)用戶輸入的關(guān)鍵詞進(jìn)行匹配,從已有的數(shù)據(jù)庫中摘錄出相關(guān)的記錄反饋給用戶。

常見的全網(wǎng)搜索引擎,像百度、谷歌這樣的。但是除此以外,搜索技術(shù)在垂直領(lǐng)域也有廣泛的使用,比如淘寶、京東搜索商品,萬芳、知網(wǎng)搜索期刊,csdn中搜索問題貼。也都是基于海量數(shù)據(jù)的搜索。
如何處理搜索
用傳統(tǒng)關(guān)系性數(shù)據(jù)庫
在這里插入圖片描述









弊端:

1、 對于傳統(tǒng)的關(guān)系性數(shù)據(jù)庫對于關(guān)鍵詞的查詢,只能逐字逐行的匹配,性能非常差。

2、匹配方式不合理,比如搜索“小密手機(jī)” ,如果用like進(jìn)行匹配, 根本匹配不到。但是考慮使用者的用戶體驗的話,除了完全匹配的記錄,還應(yīng)該顯示一部分近似匹配的記錄,至少應(yīng)該匹配到“手機(jī)”。
專業(yè)全文索引是怎么處理的

 全文搜索引擎目前主流的索引技術(shù)就是倒排索引的方式。

傳統(tǒng)的保存數(shù)據(jù)的方式都是

  記錄→單詞

而倒排索引的保存數(shù)據(jù)的方式是

單詞→記錄

例如

搜索“紅海行動”

但是數(shù)據(jù)庫中保存的數(shù)據(jù)如圖:
在這里插入圖片描述










那么搜索引擎是如何能將兩者匹配上的呢?

基于分詞技術(shù)構(gòu)建倒排索引:

首先每個記錄保存數(shù)據(jù)時,都不會直接存入數(shù)據(jù)庫。系統(tǒng)先會對數(shù)據(jù)進(jìn)行分詞,然后以倒排索引結(jié)構(gòu)保存。如下:
在這里插入圖片描述











然后等到用戶搜索的時候,會把搜索的關(guān)鍵詞也進(jìn)行分詞,會把“紅海行動”分詞分成:紅海和行動兩個詞。

這樣的話,先用紅海進(jìn)行匹配,得到id=1和id=2的記錄編號,再用行動匹配可以迅速定位id為1,3的記錄。

那么全文索引通常,還會根據(jù)匹配程度進(jìn)行打分,顯然1號記錄能匹配的次數(shù)更多。所以顯示的時候以評分進(jìn)行排序的話,1號記錄會排到最前面。而2、3號記錄也可以匹配到。
全文檢索工具elasticsearch
lucene與elasticsearch

咱們之前講的處理分詞,構(gòu)建倒排索引,等等,都是這個叫l(wèi)ucene的做的。那么能不能說這個lucene就是搜索引擎呢?

還不能。lucene只是一個提供全文搜索功能類庫的核心工具包,而真正使用它還需要一個完善的服務(wù)框架搭建起來的應(yīng)用。

好比lucene是類似于jdk,而搜索引擎軟件就是tomcat 的。

目前市面上流行的搜索引擎軟件,主流的就兩款,elasticsearch和solr,這兩款都是基于lucene的搭建的,可以獨立部署啟動的搜索引擎服務(wù)軟件。由于內(nèi)核相同,所以兩者除了服務(wù)器安裝、部署、管理、集群以外,對于數(shù)據(jù)的操作,修改、添加、保存、查詢等等都十分類似。就好像都是支持sql語言的兩種數(shù)據(jù)庫軟件。只要學(xué)會其中一個另一個很容易上手。

從實際企業(yè)使用情況來看,elasticSearch的市場份額逐步在取代solr,國內(nèi)百度、京東、新浪都是基于elasticSearch實現(xiàn)的搜索功能。國外就更多了 像維基百科、GitHub、Stack Overflow等等也都是基于ES的

elasticSearch的使用場景

為用戶提供按關(guān)鍵字查詢的全文搜索功能。
著名的ELK框架(ElasticSearch,Logstash,Kibana),實現(xiàn)企業(yè)海量日志的處理分析的解決方案。大數(shù)據(jù)領(lǐng)域的重要一份子。

elasticSearch的安裝

全文檢索工具elasticsearch:第二章:安裝配置
elasticsearch的基本概念
在這里插入圖片描述















利用kibana學(xué)習(xí) elasticsearch restful api (DSL)

執(zhí)行bin目錄下的kibana程序:

cd /opt/kibana-5.6.4-linux-x86_64/bin

./kibana
es中保存的數(shù)據(jù)結(jié)構(gòu)

public class Movie {

String id;

 String name;

 Double doubanScore;

 List<Actor> actorList;

}

public class Actor{

String id;

String name;

}

這兩個對象如果放在關(guān)系型數(shù)據(jù)庫保存,會被拆成2張表,但是elasticsearch是用一個json來表示一個document。

所以他保存到es中應(yīng)該是:

{

“id”:”1”,

“name”:”operation red sea”,

“doubanScore”:”8.5”,

“actorList”:[

{“id”:”1”,”name”:”zhangyi”},

{“id”:”2”,”name”:”haiqing”},

{“id”:”3”,”name”:”zhanghanyu”}

]

}

對數(shù)據(jù)的操作增刪改查

查看es中有哪些索引

GET /_cat/indices?v

es 中會默認(rèn)存在一個名為.kibana的索引

health status index   uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .kibana sBDZ-v6YQMWx9GaQOmSQQg   1   1          1            0      3.2kb          3.2kb

表頭的含義
在這里插入圖片描述

















增加一個索引

PUT /movie_index

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "movie_index"
}

刪除一個索引

  ES 是不刪除也不修改任何數(shù)據(jù)

DELETE /movie_index

{
  "acknowledged": true
}

新增文檔

格式 :PUT /index/type/id

PUT /movie_index/movie/1

{ “id”:1,

“name”:“operation red sea”,

“doubanScore”:8.5,

“actorList”:[

{“id”:1,“name”:“zhang yi”},

{“id”:2,“name”:“hai qing”},

{“id”:3,“name”:“zhang han yu”}

]

}

PUT /movie_index/movie/2

{

“id”:2,

“name”:“operation meigong river”,

“doubanScore”:8.0,

“actorList”:[

{“id”:3,“name”:“zhang han yu”}

]

}

PUT /movie_index/movie/3

{

“id”:3,

“name”:“incident red sea”,

“doubanScore”:5.0,

“actorList”:[

{“id”:4,“name”:“zhang chen”}

]

}
在這里插入圖片描述

如果之前沒建過index或者type,es 會自動創(chuàng)建。

直接用id查找

GET movie_index/movie/1

修改—整體替換

和新增沒有區(qū)別

PUT /movie_index/movie/3

{

“id”:“3”,

“name”:“incident red sea”,

“doubanScore”:“5.0”,

“actorList”:[

{“id”:“1”,“name”:“zhang chen”}

]

}
在這里插入圖片描述

























修改—某個字段

POST movie_index/movie/3/_update

{

“doc”: {

"doubanScore":"7.0"

}

}

修改—某個字段和 修改—整體替換二者選一,否則:
在這里插入圖片描述

刪除一個document

DELETE movie_index/movie/3

{
  "found": true,
  "_index": "movie_index",
  "_type": "movie",
  "_id": "3",
  "_version": 18,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

搜索type全部數(shù)據(jù)

GET movie_index/movie/_search

結(jié)果

{

“took”: 2, //耗費時間 毫秒

“timed_out”: false, //是否超時

“_shards”: {

"total": 5,   //發(fā)送給全部5個分片

"successful": 5,

"skipped": 0,

"failed": 0

},

“hits”: {

"total": 3,  //命中3條數(shù)據(jù)

"max_score": 1,   //最大評分

"hits": [  // 結(jié)果

  {

    "_index": "movie_index",

    "_type": "movie",

    "_id": 2,

    "_score": 1,

    "_source": {

      "id": "2",

      "name": "operation meigong river",

      "doubanScore": 8.0,

      "actorList": [

        {

          "id": "1",

          "name": "zhang han yu"

        }

      ]

    }

      。。。。。。。。

      。。。。。。。。

  }

按條件查詢(全部)

GET movie_index/movie/_search

{

“query”:{

"match_all": {}

}

}

按分詞查詢

GET movie_index/movie/_search

{

“query”:{

"match": {"name":"red"}

}

}

注意結(jié)果的評分
在這里插入圖片描述


























按分詞子屬性查詢

GET movie_index/movie/_search

{

“query”:{

"match": {"actorList.name":"zhang"}

}

}

結(jié)果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "movie_index",
        "_type": "movie",
        "_id": "2",
        "_score": 1,
        "_source": {
          "id": 2,
          "name": "operation meigong river",
          "doubanScore": 8,
          "actorList": [
            {
              "id": 3,
              "name": "zhang han yu"
            }
          ]
        }
      },
      {
        "_index": "movie_index",
        "_type": "movie",
        "_id": "1",
        "_score": 1,
        "_source": {
          "id": 1,
          "name": "operation red sea",
          "doubanScore": 8.5,
          "actorList": [
            {
              "id": 1,
              "name": "zhang yi"
            },
            {
              "id": 2,
              "name": "hai qing"
            },
            {
              "id": 3,
              "name": "zhang han yu"
            }
          ]
        }
      }
    ]
  }
}

match phrase

GET movie_index/movie/_search

{

"query":{

  "match_phrase": {"name":"operation red"}

}

}

按短語查詢,不再利用分詞技術(shù),直接用短語在原始數(shù)據(jù)中匹配

我就不發(fā)結(jié)果了,太長的博客也不好看。

fuzzy查詢

GET movie_index/movie/_search

{

"query":{

  "fuzzy": {"name":"rad"}

}

}

校正匹配分詞,當(dāng)一個單詞都無法準(zhǔn)確匹配,es通過一種算法對非常接近的單詞也給與一定的評分,能夠查詢出來,但是消耗更多的性能。

過濾–查詢后過濾

GET movie_index/movie/_search

{

"query":{

  "match": {"name":"red"}

},

"post_filter":{

  "term": {

    "actorList.id": 3

  }

}

}

先查詢后過濾效率慢,好比,我先從全國所有人中先過濾其他省份的留下廣東的,再查詢比先查詢?nèi)珖腥嗽龠^濾廣東的

過濾–查詢前過濾(推薦)

GET movie_index/movie/_search

{

"query":{

    "bool":{

      "filter":[ {"term": {  "actorList.id": "1"  }},

                 {"term": {  "actorList.id": "3"  }}

       ],

       "must":{"match":{"name":"red"}}

     }

}

}

過濾–按范圍過濾

GET movie_index/movie/_search

{

“query”: {

 "bool": {

   "filter": {

     "range": {

        "doubanScore": {"gte": 8}

     }

   }

 }

}

}

關(guān)于范圍操作符:
在這里插入圖片描述















排序

GET movie_index/movie/_search

{

“query”:{

"match": {"name":"red sea"}

}

, “sort”: [

{

  "doubanScore": {

    "order": "desc"

  }

}

]

}

這個先按名稱后按red sea排序

分頁查詢

GET movie_index/movie/_search

{

“query”: { “match_all”: {} },

“from”: 1,

“size”: 1

}

指定查詢的字段

GET movie_index/movie/_search

{

“query”: { “match_all”: {} },

“_source”: [“name”, “doubanScore”]

}

太多了,但我堅持,希望盡最大努力把我知道的全部寫出來。

高亮

GET movie_index/movie/_search

{

"query":{

  "match": {"name":"red sea"}

},

"highlight": {

  "fields": {"name":{} }

}  

}

聚合

取出每個演員共參演了多少部電影

GET movie_index/movie/_search

{

“aggs”: {

"groupby_actor": {

  "terms": {

    "field": "actorList.name.keyword"  

  }

}

}

}

每個演員參演電影的平均分是多少,并按評分排序

GET movie_index/movie/_search

{

“aggs”: {

"groupby_actor_id": {

  "terms": {

    "field": "actorList.name.keyword" ,

    "order": {

      "avg_score": "desc"

      }

  },

  "aggs": {

    "avg_score":{

      "avg": {

        "field": "doubanScore"

      }

    }

   }

}

}

}
關(guān)于mapping的類型

之前說type可以理解為table,那每個字段的數(shù)據(jù)類型是如何定義的呢

查看看mapping

GET movie_index/_mapping/movie

{
  "movie_index": {
    "mappings": {
      "movie": {
        "properties": {
          "actorList": {
            "properties": {
              "id": {
                "type": "long"
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          },
          "doubanScore": {
            "type": "float"
          },
          "id": {
            "type": "long"
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

實際上每個type中的字段是什么數(shù)據(jù)類型,由mapping定義。

但是如果沒有設(shè)定mapping系統(tǒng)會自動,根據(jù)一條數(shù)據(jù)的格式來推斷出應(yīng)該的數(shù)據(jù)格式。

true/false → boolean
1020  →  long
20.1 → double
“2017-02-01” → date
“hello world” → text +keyword

默認(rèn)只有text會進(jìn)行分詞,keyword是不會分詞的字符串。

mapping除了自動定義,還可以手動定義,但是只能對新加的、沒有數(shù)據(jù)的字段進(jìn)行定義。一旦有了數(shù)據(jù)就無法再做修改了。

注意:雖然每個Field的數(shù)據(jù)放在不同的type下,但是同一個名字的Field在一個index下只能有一種mapping定義。

中文分詞

elasticsearch本身自帶的中文分詞,就是單純把中文一個字一個字的分開,根本沒有詞匯的概念。但是實際應(yīng)用中,用戶都是以詞匯為條件,進(jìn)行查詢匹配的,如果能夠把文章以詞匯為單位切分開,那么與用戶的查詢條件能夠更貼切的匹配上,查詢速度也更加快速。

分詞器下載網(wǎng)址:https://github.com/medcl/elasticsearch-analysis-ik

安裝

下載好的zip包,請解壓后放到 /usr/share/elasticsearch/plugins/

cp /opt/elasticsearch-analysis-ik-5.6.4.zip elastic

unzip elastic

rm elastic

然后重啟es

service elasticsearch stop

service elasticsearch start

測試使用

使用默認(rèn)

GET movie_index/_analyze

{

“text”: “我是中國人”

}

請觀察結(jié)果

使用分詞器

GET movie_index/_analyze

{ “analyzer”: “ik_smart”,

“text”: “我是中國人”

}

請觀察結(jié)果

另外一個分詞器

ik_max_word

GET movie_index/_analyze

{ “analyzer”: “ik_max_word”,

“text”: “我是中國人”

}

請觀察結(jié)果

能夠看出不同的分詞器,分詞有明顯的區(qū)別,所以以后定義一個type不能再使用默認(rèn)的mapping了,要手工建立mapping, 因為要選擇分詞器。

基于中文分詞搭建索引

1、建立mapping

PUT movie_chn

{

“mappings”: {

"movie":{

  "properties": {

    "id":{

      "type": "long"

    },

    "name":{

      "type": "text"

      , "analyzer": "ik_smart"

    },

    "doubanScore":{

      "type": "double"

    },

    "actorList":{

      "properties": {

        "id":{

          "type":"long"

        },

        "name":{

          "type":"keyword"

        }

      }

    }

  }

}

}

}

插入數(shù)據(jù)

PUT /movie_chn/movie/1

{ “id”:1,

“name”:“紅海行動”,

“doubanScore”:8.5,

“actorList”:[

{“id”:1,“name”:“張譯”},

{“id”:2,“name”:“海清”},

{“id”:3,“name”:“張涵予”}

]

}

PUT /movie_chn/movie/2

{

“id”:2,

“name”:“湄公河行動”,

“doubanScore”:8.0,

“actorList”:[

{“id”:3,“name”:“張涵予”}

]

}

PUT /movie_chn/movie/3

{

“id”:3,

“name”:“紅海事件”,

“doubanScore”:5.0,

“actorList”:[

{“id”:4,“name”:“張晨”}

]

}

查詢測試

GET /movie_chn/movie/_search

{

“query”: {

"match": {

  "name": "紅海戰(zhàn)役"

}

}

}

GET /movie_chn/movie/_search

{

“query”: {

"term": {

  "actorList.name": "張譯"

}

}

}

自定義詞庫

修改/usr/share/elasticsearch/plugins/ik/config/中的IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>

        <comment>IK Analyzer 擴(kuò)展配置</comment>

        <!--用戶可以在這里配置自己的擴(kuò)展字典 -->

        <entry key="ext_dict"></entry>

         <!--用戶可以在這里配置自己的擴(kuò)展停止詞字典-->

        <entry key="ext_stopwords"></entry>

        <!--用戶可以在這里配置遠(yuǎn)程擴(kuò)展字典 -->

         <entry key="remote_ext_dict">http://192.168.67.163/fenci/myword.txt</entry>

        <!--用戶可以在這里配置遠(yuǎn)程擴(kuò)展停止詞字典-->

        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->

</properties>

按照標(biāo)紅的路徑利用nginx發(fā)布靜態(tài)資源

在nginx.conf中配置

server {

    listen  80;

    server_name  192.168.67.163;

    location /fenci/ {

       root es;

}

}

并且在/usr/local/nginx/下建/es/fenci/目錄,目錄下加myword.txt

myword.txt中編寫關(guān)鍵詞,每一行代表一個詞。

然后重啟es服務(wù)器,重啟nginx。

在kibana中測試分詞效果

更新完成后,es只會對新增的數(shù)據(jù)用新詞分詞。歷史數(shù)據(jù)是不會重新分詞的。如果想要歷史數(shù)據(jù)重新分詞。需要執(zhí)行:

POST movies_index_chn/_update_by_query?conflicts=proceed

有點長了,分章節(jié)吧,第三章繼續(xù)寫。