安装
pip install elasticsearch
对于elasticsearch 5.x 版本 需要按以下方式导入
from elasticsearch5 import Elasticsearch
# elasticsearch集群服务器的地址
ES = [
'127.0.0.1:9200'
]
# 创建elasticsearch客户端
es = Elasticsearch(
ES,
# 启动前嗅探es集群服务器
sniff_on_start=True,
# es集群服务器结点连接异常时是否刷新es结点信息
sniff_on_connection_fail=True,
# 每60秒刷新结点信息
sniffer_timeout=60
)
使用
query = {
'query': {
'bool': {
'must': [
{'match': {'_all': 'python web'}}
],
'filter': [
{'term': {'status': 2}}
]
}
}
}
# index:库名,doc_typ:表名 类型, body:查询条件 字典
ret = es.search(index='articles', doc_type='article', body=query)
print(ret['hits']['hits'])
拼写纠错
# 当我们输入错误的关键词phtyon web时,es可以提供根据索引库数据得出的正确拼写python web
curl 127.0.0.1:9200/articles/article/_search?pretty -d '
{
"from": 0,
"size": 10,
"_source": false,
"suggest": {
"text": "phtyon web",
"word-phrase": {
"phrase": {
"field": "_all",
"size": 1
}
}
}
}'
# word-phrase表示返回来的数据放在word-phrase中。
# phrase表示以短语的方式返回,field指定从哪里获取数据,size指定返回一条数据。
自动补全
自动补全需要用到suggest查询建议中的type=completion,所以原先建立的文章索引库不能用于自动补全,需要再建立一个自动补全的索引库
创建自动补全的索引库
curl -X PUT 127.0.0.1:9200/completions -H 'Content-Type: application/json' -d'
{
"settings" : {
"index": {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
}'
创建自动补全的映射类型
curl -X PUT 127.0.0.1:9200/completions/_mapping/words -H 'Content-Type: application/json' -d'
{
"words": {
"properties": {
"suggest": {
"type": "completion",
"analyzer": "ik_max_word"
}
}
}
}'
# 自动补全建议,必须是completion类型。
使用logstash导入自动补全初始数据
# 创建脚本:
vim /home/python/logstash_mysql_completion.conf
# 脚本内容:
input{
jdbc {
jdbc_driver_library => "/home/python/mysql-connector-java-8.0.13/mysql-connector-java-8.0.13.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://127.0.0.1:3306/toutiao?tinyInt1isBit=false"
jdbc_user => "root"
jdbc_password => "mysql"
jdbc_paging_enabled => "true"
jdbc_page_size => "1000"
jdbc_default_timezone =>"Asia/Shanghai"
statement => "select title as suggest from news_article_basic"
clean_run => true
}
}
output{
elasticsearch {
hosts => "127.0.0.1:9200"
index => "completions"
document_type => "words"
}
}
# 执行脚本:
sudo /usr/share/logstash/bin/logstash -f /home/python/logstash_mysql_completion.conf
自动补全建议查询
curl 127.0.0.1:9200/completions/words/_search?pretty -d '
{
"suggest": {
"title-suggest" : {
"prefix" : "pyth",
"completion" : {
"field" : "suggest"
}
}
}
}'
业务需求
用户没完全输入完成的情况,可以做自动补全提升
首先查询es进行提示补全
如果查询到结果直接返回
如果没有查询到结果,可能用户拼写错误
查询es进行拼写纠错
返回结果
当用户在输入库输入关键词点击确定进行搜索时,在es中检索匹配关键词的结果
配置elasticsearch集群服务器的地址,放在配置文件中
# elasticsearch 集群配置
ES = [
'127.0.0.1:9200'
]
创建elasticsearch客户端,放在工厂函数里
# 创建elasticsearch对象
app.es = Elasticsearch(
app.config['ES'],
# 启动前嗅探es集群服务器
sniff_on_start=True,
# es集群服务器结点连接异常时是否刷新es结点信息
sniff_on_connection_fail=True,
# 每60秒刷新结点信息
sniffer_timeout=60
)
编写视图
用户搜索输入提示
class SuggestionResource(Resource):
"""
联想建议
"""
def get(self):
"""
获取联想建议
"""
qs_parser = RequestParser()
qs_parser.add_argument('q', type=inputs.regex(r'^.{1,50}$'), required=True, location='args')
args = qs_parser.parse_args()
q = args.q
query = {
'from': 0, # 从第0个文档开始
'size': 10, # 返回10条数据
'_source': False, # 不用返回文档中的字段
'suggest': { # 建议查询
'word-completion': { # 指定建议查询返回结果的字段名称
'prefix': q, # 需要补全的前缀
'completion': { # 从哪个字段获取补全的数据
'field': 'suggest' # 指定从suggest字段中获取补全的数据
}
}
}
}
# 先从completions索引库,获取自动补全数据
ret = current_app.es.search(index='completions', body=query)
options = ret['suggest']['word-completion'][0]['options']
# 自动补全没数据,再从自动纠错中获取数据
if not options:
query = {
'from': 0,
'size': 10,
'_source': False,
'suggest': { # 建议查询
'text': q, # 需要纠错的数据
'word-phrase': { # 指定建议查询返回结果的字段名称
'phrase': { # 短语
'field': '_all', # 从_all字段中获取纠错数据
'size': 1, # 返回一个纠错结果
'direct_generator': [{
'field': '_all',
'suggest_mode': 'always'
}]
}
}
}
}
ret = current_app.es.search(index='articles', doc_type='article', body=query)
options = ret['suggest']['word-phrase'][0]['options']
results = []
for option in options:
if option['text'] not in results:
results.append(option['text'])
return {'options': results}
搜索结果
class SearchResource(Resource):
"""
搜索结果
"""
def get(self):
"""
获取搜索结果
"""
if g.use_token and not g.user_id:
return {'message': 'Token has some errors.'}, 401
qs_parser = RequestParser()
qs_parser.add_argument('q', type=inputs.regex(r'^.{1,50}$'), required=True, location='args')
qs_parser.add_argument('page', type=inputs.positive, required=False, location='args')
qs_parser.add_argument('per_page', type=inputs.int_range(constants.DEFAULT_SEARCH_PER_PAGE_MIN,
constants.DEFAULT_SEARCH_PER_PAGE_MAX,
'per_page'),
required=False, location='args')
args = qs_parser.parse_args()
q = args.q
page = 1 if args.page is None else args.page
per_page = args.per_page if args.per_page else constants.DEFAULT_SEARCH_PER_PAGE_MIN
# Search from Elasticsearch
query = {
'from': (page-1)*per_page, # 偏移
'size': per_page, # 返回数量
'_source': ["article_id", "title"],
'query': {
'bool': {
'must': [
{'match': {'_all': q}} # 对q进行全文检索
],
'filter': [
{'term': {'status': 2}} # 必须是通过审核的文章
]
}
}
}
ret = current_app.es.search(index='articles', doc_type='article', body=query)
total_count = ret['hits']['total']
results = []
hits = ret['hits']['hits']
for result in hits:
article_id = int(result['_id'])
# 从缓存中获取文章数据
article = cache_article.ArticleInfoCache(article_id).get()
# 如果缓存中存在就添加到返回结果
if article:
results.append(article)
# 记录用户的搜索记录
if g.user_id and page == 1:
try:
cache_user.UserSearchingHistoryStorage(g.user_id).save(q)
except RedisError as e:
current_app.logger.error(e)
# 添加elasticsearch索引文档
if total_count and page == 1:
query = {
'_source': False,
'query': {
'match': {
'suggest': q
}
}
}
# 查询自动补全是否有该搜索词
ret = current_app.es.search(index='completions', doc_type='words', body=query)
if ret['hits']['total'] == 0:
doc = {
'suggest': {
'input': q,
'weight': constants.USER_KEYWORD_ES_SUGGEST_WEIGHT
}
}
try:
# 没有就添加一个文档记录
current_app.es.index(index='completions', doc_type='words', body=doc)
except Exception:
pass
return {'total_count': total_count, 'page': page, 'per_page': per_page, 'results': results}