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
启动ES
./../elasticsearch-6.3.0/bin/elasticsearch > logs/elasticsearch.log &
wget https://artifacts.elastic.co/downloads/kibana/kibana-6.3.0-linux-x86_64.tar.gz
tar -xzvf kibana-6.3.0-linux-x86_64.tar.gz
cd kibana-6.3.0-linux-x86_64
vi config/kibana.yml
启动Kibana
./bin/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
POST max_synonyms/_analyze
{"analyzer": "my_synonyms","text": "C7000处理器"
}
同义词配置
同义词安装
同义词一般格式:
简单扩展:我们可以把同义词列表中的任意一个词扩展成同义词列表所有的词。
举例 “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插件就会报错。
如果配置了自定义停用词表时,也要避免会与同义词表冲突,否则会报错:
"type": "illegal_argument_exception",
"reason": "term: ? was completely eliminated by analyzer"
解决办法:
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。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties><comment>IK Analyzer 扩展配置</comment><!--用户可以在这里配置自己的扩展字典 --><entry key="ext_dict">custom/words.list</entry><!--用户可以在这里配置自己的扩展停止词字典--><entry key="ext_stopwords">custom/stopwords.txt</entry><!--用户可以在这里配置远程扩展字典 --><!-- <entry key="remote_ext_dict">words_location</entry> --><!--用户可以在这里配置远程扩展停止词字典--><!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
打分策略
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)
score(q,d) = queryNorm(q) · coord(q,d) · ∑ ( tf(t in d) · idf(t)² · t.getBoost() · norm(t,d) ) (t in q)
queryNorm(q) = 1 / sqrt(idf(t1)*idf(t1)+idf(t2)*idf(t2)+...+idf(tn)*idf(tn))
coord(q,d) = overlap/maxoverlap
tf(t in d) = sqrt(frequency)
idf(t) = 1 + log (numDocs / (docFreq + 1))
norm(d) = 1 / sqrt(numTerms)
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())21
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 f1
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搜索引擎的工程解决方案
搭建过程:
- ES搭建及插件配置(分词、同义词、去停用词)
- 搜索query预处理(自定义分词、去前后缀)
- term权重修改(词的权重表)
- 搜索结果分析
工程部分目录结构:
.
└── 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分词插件只能为中文分词,而对于一些中间无空格的英文或者字符,不能解决。
所以自己实现了一个最大正向匹配分词的算法,参考链接:分词算法介绍
# 最大正向匹配分词算法
def max_forward_tokenizer(self, str):assert len(self.wordset) > 0s_index = 0e_index = len(str)temp_list = []temp_str = ""while s_index < e_index:for i in reversed(range(s_index, e_index + 1)):iter_str = str[s_index:i]if iter_str in self.wordset:if temp_str != "":temp_list.append(temp_str)temp_str = ""temp_list.append(iter_str)s_index += len(iter_str)breakelif i == s_index:temp_str += str[s_index]s_index += 1if temp_str != "":temp_list.append(temp_str)return ' '.join(temp_list)
term权重修改
ES官方文档以及国内的博客都说明了match搜索可以用通过bool的term搜索等价替代。
由于ES的Analyzer没有找到修改权重的方法,所以通过更换搜索方式解决。
POST max_token/_doc/_search?explain=true
{"query": {"bool": {"should": [{"match": {"content": {"query": "手机","analyzer": "my_synonyms","boost": 2}}},{"match": {"content": {"query": "性能","analyzer": "my_synonyms","boost": 3}}},{"match": {"content": {"query": "参数","analyzer": "my_synonyms","boost": 3}}}]}}
}
搜索结果分析
在搜索流程上,最初是将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搜索引擎搭建笔记相关推荐
- ElasticSearch搜索引擎API笔记
ElasticSearch搜索引擎API笔记 1. pom.xml <dependency> <groupId>org.elasticsearch.client</g ...
- java分布式免费开源搜索引擎 Elasticsearch 详细学习笔记
网页右边,向下滑有目录索引,可以根据标题跳转到你想看的内容 如果右边没有就找找左边 此文是学习尚硅谷Elasticsearch课程的笔记 Elasticsearch 全文检索引擎 Lucene 是 A ...
- ElasticSearch搜索引擎-2_学习笔记2021.4.18)
ElasticSearch搜索引擎-2_学习笔记(2021.4.18) 前言: RESTful 介绍 , 我们通过RESTful来操作ElasticSearch (所有请求都是通过Postman ) ...
- 我的ELK搭建笔记(阿里云上部署)
文章转载:http://www.jianshu.com/p/797073c1913f 仅用作个人学习,收藏 我的 ELK 搭建笔记(基于阿里云) "不是最好的,但一定是有良心的操作记录.&q ...
- ElasticSearch搜索引擎详解-持续更新中
ElasticSearch搜索引擎详解 1. ElasticSearch概述 1.1 elasticsearch是什么 1.2 全文搜索引擎 1.3 elasticsearch and solr 1. ...
- 第三百六十二节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)基本的索引和文档CRUD操作、增、删、改、查...
第三百六十二节,Python分布式爬虫打造搜索引擎Scrapy精讲-elasticsearch(搜索引擎)基本的索引和文档CRUD操作.增.删.改.查 elasticsearch(搜索引擎)基本的索引 ...
- 《Node.js入门》Windows 7下Node.js Web开发环境搭建笔记
最近想尝试一下在IBM Bluemix上使用Node.js创建Web应用程序,所以需要在本地搭建Node.js Web的开发测试环境. 这里讲的是Windows下的搭建方法,使用CentOS 的小伙伴 ...
- [云炬python3玩转机器学习笔记] 2-7开发环境搭建笔记
开发环境搭建笔记
- ElasticSearch搜索引擎: 内存分析与设置
在 Elasticsearch 的运行过程中,如何合理分配与设置内存是一件十分重要的事情,否则十分容易出现各种问题. 一.Elasticsearch为什么吃内存: 我们先看下 ES 服务器的总体内存消 ...
- ElasticSearch搜索引擎常见面试题总结
一.ElasticSearch基础: 1.什么是Elasticsearch: Elasticsearch 是基于 Lucene 的 Restful 的分布式实时全文搜索引擎,每个字段都被索引并可被搜索 ...
最新文章
- php中创建关联数组,以及遍历数组
- java 内存映射文件 主要应用_VC++中使用内存映射文件处理大文件(3)
- 问号在c语言中运算符,C# 运算符 ?、??、?: 各种问号的用法和说明
- php7对象转换成数组,php 如何把对象转换成数组对象
- vue点击切换css样式
- 【英语学习】【English L06】U01 Breakfast L3 I'm full from my brunch
- android httpClient 支持HTTPS的访问方式
- [转]十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
- 一文解析SQLServer数据库
- 产品研发项目管理软件哪个好?
- 基于SSM框架的网上购物商城及电商后台管理系统
- 【Windows】安装显卡驱动+cuda+cudnn
- 2022天府杯国际赛数学建模题目和思路
- Internet结构和ISP
- 10个 安卓应用商店开发者公司账号注册(2022最新最全)
- c++读取cfg文件
- python 量化交易:MACD指标双金叉形态识别
- 什么是搜索词?有什么用?
- java最基本的基础知识
- stegsolve打不开原因