Elasticsearch:查找子字符串匹配

68
我希望您能够进行精确词匹配和部分单词/子字符串匹配。例如,如果我搜索“男士剃须刀”,那么应该能够在结果中找到“男士剃须刀”。但是如果我搜索“en's shaver”,也应该能够在结果中找到“men's shaver”。
我使用以下设置和映射:
索引设置:
PUT /my_index
{
    "settings": {
        "number_of_shards": 1, 
        "analysis": {
            "filter": {
                "autocomplete_filter": { 
                    "type":     "edge_ngram",
                    "min_gram": 1,
                    "max_gram": 20
                }
            },
            "analyzer": {
                "autocomplete": {
                    "type":      "custom",
                    "tokenizer": "standard",
                    "filter": [
                        "lowercase",
                        "autocomplete_filter" 
                    ]
                }
            }
        }
    }
}

映射:

PUT /my_index/my_type/_mapping
{
    "my_type": {
        "properties": {
            "name": {
                "type":            "string",
                "index_analyzer":  "autocomplete", 
                "search_analyzer": "standard" 
            }
        }
    }
}

插入记录:

POST /my_index/my_type/_bulk
{ "index": { "_id": 1            }}
{ "name": "men's shaver" }
{ "index": { "_id": 2            }}
{ "name": "women's shaver" }

查询:

1. 通过精确短语匹配搜索 --> "men's"

POST /my_index/my_type/_search
{
    "query": {
        "match": {
            "name": "men's"
        }
    }
}

上述查询结果会在返回结果中包括"men's shaver"这个词条。

2. 按部分单词匹配搜索 --> "en's"

POST /my_index/my_type/_search
{
    "query": {
        "match": {
            "name": "en's"
        }
    }
}

上面的查询没有返回任何内容。

我也尝试了以下查询

POST /my_index/my_type/_search
{
    "query": {
        "wildcard": {
           "name": {
              "value": "%en's%"
           }
        }
    }
}

仍然没有获取到任何结果。我认为这是由于索引上的“edge_ngram”类型过滤器无法找到“部分单词/子字符串匹配”。我也尝试了“n-gram”类型过滤器,但它会显著减慢搜索速度。

请建议我如何在相同的索引设置下实现精确短语匹配和部分短语匹配。

3个回答

86

如果您希望搜索部分字段匹配和完全匹配,最好将字段定义为“未分析”或关键字(而不是文本),然后使用通配符查询

另请参见此处

要使用通配符查询,请在要搜索的字符串两端添加*:

POST /my_index/my_type/_search
{
"query": {
    "wildcard": {
       "name": {
          "value": "*en's*"
       }
    }
}
}

为了实现大小写不敏感,请使用一个自定义分析器,其中包含一个小写过滤器和关键字分词器

自定义分析器:

"custom_analyzer": {
    "tokenizer": "keyword",
    "filter": ["lowercase"]
}

将搜索字符串改为小写

如果您得到的搜索字符串是AsD:将其更改为*asd*


16
引用ElasticSearch的文档:“警告:允许在单词开头使用通配符(例如“*ing”)特别耗费资源,因为需要检查索引中的所有术语。”http://www.elastic.co/guide/en/elasticsearch/reference/1.x/query-dsl-query-string-query.html#_wildcards - david_p
2
@david_p的链接已经失效,但正如他所说,ElasticSearch建议“避免使用以通配符开头的模式(例如*foo或作为正则表达式的.*foo)”。https://www.elastic.co/guide/en/elasticsearch/guide/current/_wildcard_and_regexp_queries.html - cavpollo
它不支持大小写不敏感。我们如何使用它来支持大小写不敏感? - Mukesh
如果我有一个名字字段,其值为“Soundarya Thyagu” - 如果我使用SoU进行搜索,它应该将字段转换为小写并返回正确结果。 - Soundarya Thiagarajan
@SoundaryaThiagarajan,你可以将搜索字符串转换为小写字母,或者使用搜索时间分析器进行转换。 - BlackPOP
显示剩余4条评论

6

@BlackPOP提供的答案是可行的,但它使用了通配符方法,这不是首选方法,因为它存在性能问题,如果滥用会在Elasticsearch集群中产生巨大的连锁反应(性能问题)。

我已经写了一篇详细的博客文章,涵盖了截至2020年12月最新的Elasticsearch部分搜索/自动完成选项,考虑到性能。有关更多权衡信息,请参阅答案。

在我看来,一个更好的方法是根据用例使用定制的n-gram tokenizer,它将已经拥有搜索术语所需的标记,因此速度会更快,尽管它的索引大小会更大,但您的大小并不那么昂贵,并且可以更好地控制子字符串搜索的精确工作方式。

如果在tokenizer设置中保守地定义min和max gram,则还可以控制大小。


-3

通过使用任何字符串或子字符串进行搜索:

query: {
    or: [{
      match_phrase_prefix: {
            name: str
     }
    }, {
        match_phrase_prefix: {
            surname: str
        }
    }]
}

愉快地使用 Elastic Search 进行编程...


1
他并不是在寻找匹配前缀。 - kjprice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接