ElasticSearch搜索引擎搭建笔记
搜索引擎調研
Solr
Solr是一個用java開發的獨立的企業級搜索應用服務器,它提供了類似于Web-service的API接口,它是基于Lucene的全文檢索服務器,也算是Lucene的一個變種,很多一線互聯網公司都在使用Solr,也算是一種成熟的解決方案.
官方主頁:http://lucene.apache.org/solr/
Elasticsearch
Elasticsearch是一個采用java語言開發的,基于Lucene構造的開源,分布式的搜索引擎. 設計用于云計算中,能夠達到實時搜索,穩定可靠. Elasticsearch的數據模型是JSON.
官方主頁:http://www.elasticsearch.org/
xunsearch
Xunsearch 是一個高性能、全功能的全文檢索解決方案,旨在幫助一般開發者針對既有的海量數據,快速而方便地建立自己的全文搜索引擎。
官方主頁:http://www.xunsearch.com/
Zebra
Zebra是一個用C語言實現的檢索程序,特點是對大數據的支持,支持EMAIL,XML,MARC等格式的數據.
官方主頁:https://www.indexdata.com/zebra
Nutch
Nutch是一個用java實現的開源的web搜索引擎,包括爬蟲crawler,索引引擎,查詢引擎. 其中Nutch是基于Lucene的,Lucene為Nutch提供了文本索引和搜索的API.
對于應該使用Lucene還是使用Nutch,應該是如果你不需要抓取數據的話,應該使用Lucene,最常見的應用是:你有數據源,需要為這些數據提供一個搜索頁面,在這種情況下,最好的方式是直接從數據庫中取出數據,并用Lucene API建立索引.
官方主頁:http://nutch.apache.org/
參考:https://blog.csdn.net/business122/article/details/78064092
Solr搭建
Slor安裝教程
需要環境:JAVA 8
平臺安裝
cp -r jdk1.8.0_181/ ~/env/ cd solr-7.4.0/ export PATH=/home/username/env/jdk1.8.0_181/bin:$PATH bin/solr start -e cloud ps -ef|grep solr # 停止Solr服務 bin/solr stop -all數據導入
bin/post -c testsolr example/exampledocs/* -p 9700配置
Slor配置詳解
ElasticSearch搭建
ES官方文檔
ES可視化Kibana
配置插件
分詞器配置
IK分詞器安裝
IK Analysis plugin integrates Lucene IK analyzer (http://code.google.com/p/ik-analyzer/) into elasticsearch, support customized dictionary.
ik_max_word 會將文本做最細粒度的拆分,比如會將“中華人民共和國國歌”拆分為“中華人民共和國,中華人民,中華,華人,人民共和國,人民,人,民,共和國,共和,和,國國,國歌”,會窮盡各種可能的組合;
ik_smart 會做最粗粒度的拆分,比如會將“中華人民共和國國歌”拆分為“中華人民共和國,國歌”。
SmartCN安裝
The Smart Chinese Analysis plugin integrates Lucene’s Smart Chinese analysis module into elasticsearch.
分詞結果查詢Sample
同義詞配置
同義詞安裝
同義詞一般格式:
簡單擴展:我們可以把同義詞列表中的任意一個詞擴展成同義詞列表所有的詞。
舉例 “jump,hop,leap”
簡單收縮:把左邊的多個同義詞映射到了右邊的單個詞。它必須同時應用于索引和查詢階段,以確保查詢詞項映射到索引中存在的同一個值。
舉例 “leap,hop => jump”
類型擴展:類型擴展是完全不同于簡單收縮或擴張,并不是平等看待所有的同義詞,而是擴大了詞的意義,使被拓展的詞更為通用。
舉例"cat => cat,pet",“kitten => kitten,cat,pet”,“dog => dog,pet”“puppy => puppy,dog,pet”
同義詞配置教程
PUT max {"settings": {"analysis": {"filter": {"my_synonym_filter": {"type": "synonym", "expand": true,"synonyms_path" : "analysis/synonyms.dict"}},"analyzer": {"my_synonyms": {"tokenizer": "ik_smart","filter": ["lowercase","my_synonym_filter" ]}}}} }POST max/_doc/_mapping {"properties": {"question": {"type": "text"},"content": {"type": "text","analyzer": "ik_smart","search_analyzer": "my_synonyms"}} }IK Analyzer在默認的停用詞表中包含了一些關鍵字和特殊符號,如果同義詞表中有相同的詞,那么ES中的IK插件就會報錯。
如果配置了自定義停用詞表時,也要避免會與同義詞表沖突,否則會報錯:
解決辦法:
illegal_list = ['%', '#', '+', '?'] illegal_words = ['be', 'a', 'no', "==", '"?"']非法字符串過濾解決辦法:
# 字符轉半角 def strQ2B(str):res_str = ""for ichar in str:inside_code = ord(ichar)if inside_code == 12288:inside_code = 32elif 65281 <= inside_code <= 65374:inside_code -= 65248res_str += chr(inside_code)return res_str # 專治各種錯誤字符 def simplify_str(str, type="lower"):str = strQ2B(str)bad_char = ['(', ')', ',', ' ', '】', '【', '?', ' ', '=', '“', '”']simpli_char = ['(', ')', ',', '', '', '', '?', '', '=', '"', '"']for x, y in zip(bad_char, simpli_char):str = str.replace(x, y)if type == "upper":return str.upper()elif type == "lower":return str.lower()同義詞配置過程小結:
1、如果使用同義詞插件,優先選擇ik_smart分詞,因為這樣分詞不會將錯誤的分詞結果對應的同義詞引入到搜索結果當中
2、如果使用簡單收縮模式,會將所有的搜索term替換,即使搜索結果中包含也不能匹配到
3、如果同義詞表噪音較大,盡量使用類型擴展模式,再保證搜索term存在的情況下,其同義詞的匹配也會計入得分
4、無論是檢索還是問題相似度匹配,我對max(取匹配度最高的作為分類結果)和avg(取所有結果中平均得分最高的分類結果)都做了很多實驗,max準確率遠遠高于avg,而且速度也比avg快很多。此結果是在數據噪聲比較大的前提下。
Analyzer配置
Analyzer作用域解釋
項目要求搜索時使用同義詞+ik_smart(自定義詞表),匹配時使用ik_smart(自定義詞表),因此在不通的作用域以及場景下配置不同的Analyzer。
打分策略
ElasticSearch 打分策略 ES評分機制
Lucene打分數學推導
通過得分和權重對ES搜索排名優化
ES官網給出的打分公式(Lucene):
score(q,d)=coord(q,d)?queryNorm(q)?∑t∈qtf(t)?idf(t)2?t.getBoost()?norm(t,d)score(q,d) = coord(q,d)*queryNorm(q)*{\sum_{t \in q}{tf(t)*{idf(t)}^2*t.getBoost()*norm(t,d)}}score(q,d)=coord(q,d)?queryNorm(q)?t∈q∑?tf(t)?idf(t)2?t.getBoost()?norm(t,d)
queryNorm(q)
對查詢進行一個歸一化,有幾個分片就會有幾個不同的queryNorm值
queryNorm(q)=1q.getBoost()2?∑t∈q(idf(t)?t.getBoost())2queryNorm(q) = \frac{1}{\sqrt{{q.getBoost()}^2*\sum_{t \in q}{(idf(t)*t.getBoost())^2}}}queryNorm(q)=q.getBoost()2?∑t∈q?(idf(t)?t.getBoost())2?1?
coord(q,d)
協調因子,暫時沒找到計算的結果在哪里…coord官方介紹
tf(t in d)
詞頻數
idf(t)
逆詞頻數
t.getboost()
獲取每個term的權重,需要人為調整
norm(d)
標準化因子
norm(t,d)=d.getBoost()?lengthNorm(field)?∏f∈df.getBoost()norm(t,d) = d.getBoost()*lengthNorm(field)*\prod_{f \in d}{f.getBoost()}norm(t,d)=d.getBoost()?lengthNorm(field)?f∈d∏?f.getBoost()
lengthNorm(f)=1nums?of?terms?in?field?flengthNorm(f) = \frac{1}{\sqrt{\text{nums of terms in field f}}}lengthNorm(f)=nums?of?terms?in?field?f?1?
ES基本操作
# 新建索引 PUT index # 定義映射 POST index/max/_mapping {"properties": {"question": {"type": "text"},"content": {"type": "text","analyzer": "ik_smart","search_analyzer": "my_synonyms"}} } # 批量插入數據 POST _bulk {"index":{"_index": "index","_type": "max"}} {"content":"C5000設置來電黑名單"} # 查詢 POST index/max/_search {"query" : { "match" : { "content" : "刪除文件夾" }},"highlight" : {"pre_tags" : ["<tag1>", "<tag2>"],"post_tags" : ["</tag1>", "</tag2>"],"fields" : {"content" : {}}} }用文本替換將句子修改為bulk格式的方法:
replaceAll('\r\n','"}}\r\n{"index":{"_index": "index","_type": "fulltext"}}\r\n{ "create" :{"content":"')ES 自定義得分
PUT max_test {"settings": {"number_of_shards": 1,"similarity": {"scripted_tfidf": {"type": "scripted","script": {"source": "double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;"}},"scripted_bm25": {"type": "scripted","script" :{"source": "double k1 = 1.2;double b = 0.75;double idf = Math.log(1.0 + (field.docCount-term.docFreq+0.5)/(term.docFreq+0.5));double tfNorm = (doc.freq * (k1 + 1)) / (doc.freq + k1 * (1 - b + b * doc.length / field.avgLength));return query.boost * idf * tfNorm;"}}}},"mappings": {"_doc": {"properties": {"content": {"type": "text","similarity": "scripted_bm25"}}}} }ES搜索引擎的工程解決方案
搭建過程:
工程部分目錄結構:
. └── rule├── output│ ├── class_key.list 用于batch_test,鑒別標注數據是否在已有類別中│ ├── rule.data 生成規范的句子擴充│ ├── synonyms.dict [1]用于ES_synonym同義詞│ ├── stopwords.txt [2]停用詞表│ └── words.list [3]生成的詞表,用于自定義分詞以及ES_IK分詞├── README.md├── resource│ ├── lexical20180504.txt.map 開發提供的近義詞詞表│ └── rules20180504.rule 開發提供的規則├── searchtool│ ├── batch_test.py│ ├── const.py│ ├── db_type.es│ ├── lx_main.py│ ├── operate_db.py│ ├── prodata.py├── test│ └── 0522full.rule 用于batch_test[1]: elasticsearch-6.3.0/config/analysis/synonyms.dict同時在ES新增index時配置,詳見searchtool/db_type.es"filter": {"my_synonym_filter": {"type": "synonym", "expand": true,"synonyms_path" : "analysis/synonyms.dict"}}[2]:elasticsearch-6.3.0/plugins/elasticsearch-analysis-ik-6.3.1/config/custom/stopwords.txt同時配置 elasticsearch-6.3.0/plugins/elasticsearch-analysis-ik-6.3.1/config/IKAnalyzer.cfg.xml<entry key="ext_stopwords">custom/stopwords.txt</entry>[3]: elasticsearch-6.3.0/plugins/elasticsearch-analysis-ik-6.3.1/config/custom/words.list同時配置 elasticsearch-6.3.0/plugins/elasticsearch-analysis-ik-6.3.1/config/IKAnalyzer.cfg.xml<entry key="ext_dict">custom/words.list</entry>搜索流程:
def get_result(self, query, type="search_one"):res_body = {}# 替換非法字符(漢字字符+英文全角)query = simplify_str(query)# 去前后綴query = self.del_prefix(query)# 自定義分詞token_ques = self.tokenizer.max_forward_tokenizer(query)if type == "term_boost":# 設置 iterm Boostterm_dict = self.getTermBoost(token_ques)# ES 搜索引擎查詢res_body["result"] = self.oper_es.searchByTermBoost(term_dict)res_body["term_boost"] = str(term_dict)elif type == "search_one":res_body["result"] = self.oper_es.searchOne(token_ques)return res_body搜索query預處理
IK Analyzer分詞插件只能為中文分詞,而對于一些中間無空格的英文或者字符,不能解決。
所以自己實現了一個最大正向匹配分詞的算法,參考鏈接:分詞算法介紹
term權重修改
ES官方文檔以及國內的博客都說明了match搜索可以用通過bool的term搜索等價替代。
由于ES的Analyzer沒有找到修改權重的方法,所以通過更換搜索方式解決。
搜索結果分析
在搜索流程上,最初是將IK分詞后的結果設置每個term的權重,然后進行bool搜索。
但是這樣的搜索結果性能上下降了很多,文檔上提到match搜索最終都會在內部處理為bool搜索,理論上應該性能應該是等價的。
在進一步分析對比搜索結果后發現,"description": "weight(Synonym(content:手機 content:移動設備 content:集合) in 65902)和weight(content:手機 in 62749)從計算得分上存在差異。
雖然兩者都是相加,但是match會將同一類同義詞算作一個term來計算tfNorm&idf,而bool因提前做了同義詞轉換,每一個詞都是一個term,匹配句子中沒有同義詞就不會引入計算。
最后采用了折中的辦法,在自定義分詞后,將每個詞作為一個term,加權后使用IK Analyzer做搜索,當所有詞的權重設置為1.0時,性能指標幾乎和match方法一致。
而缺點是僅僅能設置一類同義詞的權重,而不能精確到詞。
XunSearch搭建
XunSearch安裝教程
ERROR: failed to compile xunsearch, see ‘setup.log’ for more detail
解決方式: 需要聯網…
總結
以上是生活随笔為你收集整理的ElasticSearch搜索引擎搭建笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP中交货计划导出需求处理实例
- 下一篇: 空间四点定位原理及应用