在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的完全匹配搜索。

フィールドタイプクエリ部分一致textmatch完全一致keywordwildcard

获取包含文本和关键词两个数据的方法。

在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部分-多种搜索方式-

广告
将在 10 秒后关闭
bannerAds