全文檢索工具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ù)寫。