在Elasticsearch中同时实现部分匹配搜索和完全匹配搜索
我几个月前利用Elasticsearch创建了一个部分匹配搜索(普通搜索)的应用程序。
然后,我添加了一个完全匹配搜索(在Google中使用“”引号包围的搜索词)。
中间遇到了一些困难,所以我总结了一下。
我使用Python库来访问Elasticsearch。
环境
-
- Elasticsearch 7.8.0
elasticsearch 7.9.1 (pythonのライブラリ)
Python Elasticsearch 基本的な使い方まとめ
部分匹配搜索和完全匹配搜索
由于部分一致搜索和完全一致搜索这两个词相当模糊,所以在这篇文章中我会详细描述它们的概念。(这并不是一般的定义)
- 検索対象文字列が「関西国際空港」の場合
部分一致検索は、検索語を単語分けした後の各単語が、検索対象文字列に含まれている場合にヒットします。
検索語を単語分けした後の各単語の一部が、検索対象文字列に含まれている場合は、ヒットする場合もヒットしない場合もあります。
Google検索で、普通の検索と同じと思います。
完全一致検索は、検索語の全部が、検索対象文字列に含まれている場合にヒットします。
Google検索で、検索語を「”」(ダブルクォーテーション)で囲んだ場合と同じイメージです。
在Elasticsearch中的实现方法
Elasticsearch有两种字符串字段类型:text和keyword。
text将字符串分析并按单词存储。
keyword则直接保存字符串而不进行分词。
使用匹配查询match query,可以实现对文字text的部分匹配搜索。
使用通配符查询wildcard query,可以实现对关键字keyword的完全匹配搜索。
获取包含文本和关键词两个数据的方法。
在Elasticsearch中,通过在多字段上定义映射,可以为一个字符串提供多个字段类型的数据(官方参考文档)。
# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch
es = Elasticsearch("localhost:9200")
mapping = {
"mappings" : {
"properties" : {
"content": {
"type":"text",
"analyzer": "kuromoji", # アナライザーはkuromoji
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 8191 # デフォルトでは256
}
}
}
}
}
}
es.indices.create(index='myes', body=mapping)
通过这样做,”myes”可以作为一个主要的text类型数据,还可以拥有一个关键词类型的子数据。可以使用”text”来访问text类型的数据,可以使用”content.keyword”来访问关键词类型的数据。
作为子数据拥有的关键词类型,如果字符串超过256个字符,默认情况下将不会创建。
如果要处理超过256个字符的数据,需要明确设置”ignore_above”。
上面设置了”ignore_above”:8191。
在Elasticsearch内部使用的Lucene程序中,关键词类型数据的最大容量为32766字节,UTF-8一个字符最大占4个字节,所以设置为32766/4=8191。
此外,还配置了日语的分析器kuromoji。
注册索引
注册样本字符串(约300个字符)。
(某剧原著 ^^/)
# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch
es = Elasticsearch("localhost:9200")
sample = u"東京中央銀行大阪西支店の融資課長・半沢直樹のもとにとある案件が持ち込まれる。\
大手IT企業ジャッカルが、業績低迷中の美術系出版社・仙波工藝社を買収したいというのだ。 \
大阪営業本部による強引な買収工作に抵抗する半沢だったが、やがて背後にひそむ秘密の存在に気づく。\
有名な絵に隠された「謎」を解いたとき、半沢がたどりついた驚愕の真実とは――。\
探偵半沢が絵画に込められた謎を解く、江戸川乱歩賞出身の池井戸潤、真骨頂ミステリー!\
『半沢直樹1 オレたちバブル入行組』の前日譚となるシリーズ原点にして、「やられたら、倍返し!」\
あの突き上げる爽快感とともに、明かされる真実に胸が熱くなる、\
7月19日放送開始ドラマ「半沢直樹」シリーズ待望の最新原作小説が、ついに登場。"
book = {"content":sample}
es.index(index="myes", doc_type="_doc", id=1, body=book)
部分匹配搜索
对于内容,使用匹配查询进行搜索。
# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch
es = Elasticsearch("localhost:9200")
rq = {
"query" : {
"match": {
"content": "東京中央銀行"
}
}
}
result = es.search(index="myes", body=rq)
print(result)
搜索关键词和搜索结果如下所示。
在匹配查询中,搜索词会被拆解成单词并进行搜索。搜索词“东京的中央银行”会被拆分成“东京”、“中央”、“银行”,然后与同样被拆分成单词的文本类型的搜索目标数据进行匹配,从而实现搜索命中。虽然搜索词“三井住友银行”也有可能命中,因为“银行”包含在搜索目标数据中,但在这次搜索中没有命中。
完全一致搜索
对于content.keyword,使用通配符查询进行搜索。
在搜索词的前后加上“*”(通配符)。
“*”将与0个或多个任意字符匹配。
# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch
es = Elasticsearch("localhost:9200")
rq = {
"query" : {
"wildcard": {
"content.keyword": "*東京中央銀行*"
}
}
}
result = es.search(index="myes", body=rq)
print(result)
搜索词和搜索结果如下所示。
在通配符查询(术语级别查询)中,将搜索词直接与搜索目标字符串的数据进行比较。
由于搜索目标字符串中不包含”东京中央银行”和”三井住友银行”,因此搜索结果为空。
概述
在尝试实现Elasticsearch的部分匹配搜索和完全匹配搜索时,我发现可以轻松地同时使用text和keyword这两种数据类型,感叹道:“这是多么方便的数据结构啊!”然而,我并没有意识到作为子类型的keyword数据默认情况下会被限制在”ignore_above”: 256之内,导致我遇到了一些困难。
后来我了解到,还有一种称为wildcard字段类型的数据可以进行通配符查询优化,也许使用它取代keyword会更好。
请看参考。
用于初学者的Elasticsearch第1部分
用于初学者的Elasticsearch第2部分-多种搜索方式-