如何使用 Elasticsearch 对文本输入执行部分单词搜索?



>我有一个查询来搜索以下格式的记录:TR000002_1_2020.

用户应该能够通过以下方式搜索结果:

TR0000022_1_2020TR000002_1_20202020.我使用的是 Elasticsearch 6.8,所以我无法使用 E7 中引入的内置搜索类型。因此,我认为要么wildcard搜索,要么ngram最适合我的需要。以下是我的两种方法以及它们不起作用的原因。

  1. 通配符

属性映射:

.Text(t => t
.Name(tr => tr.TestRecordId)
)

查询:

m => m.Wildcard(w => w
.Field(tr => tr.TestRecordId)
.Value($"*{form.TestRecordId}*")
),

这有效,但它区分大小写,因此如果用户使用tr000002_1_2020进行搜索,则不会返回任何结果(因为查询中的tr是小写的)

  1. ngram(键入等效项时搜索)

创建自定义 ngram 分析器

.Analysis(a => a
.Analyzers(aa => aa
.Custom("autocomplete", ca => ca
.Tokenizer("autocomplete")
.Filters(new string[] {
"lowercase"
})
)
.Custom("autocomplete_search", ca => ca
.Tokenizer("lowercase")
)
)
.Tokenizers(t => t
.NGram("autocomplete", e => e
.MinGram(2)
.MaxGram(16)
.TokenChars(new TokenChar[] {
TokenChar.Letter,
TokenChar.Digit,
TokenChar.Punctuation,
TokenChar.Symbol
})
)
)
)

属性映射

.Text(t => t
.Name(tr => tr.TestRecordId)
.Analyzer("autocomplete")
.SearchAnalyzer("autocomplete_search")
)

查询

m => m.Match(m => m
.Query(form.TestRecordId)
),

如本答案所述,这是行不通的,因为分词器将字符拆分为20022020等元素,因此我的查询返回了索引中包含 2020 年的所有文档,例如TR000002_1_2020TR000008_1_2020TR000003_6_2020

Elasticsearch 的最佳用途是什么,以允许我想要的搜索行为?我也见过query string使用过。谢谢!

这里有一种简单的方法来满足您的要求(我希望)。

  1. 我们使用模式替换字符过滤器来删除引用的固定部分(TR000... )
  2. 我们使用拆分分词器来拆分对"_"字符的引用
  3. 我们使用 matchPhrase 查询来确保引用的片段按顺序匹配

有了这个分析链作为参考TR000002_1_2020我们得到了["2", "1", "2020" ]的代币。所以它会匹配["TR000002_1_2020", "TR000002 1 2020", "2_1_2020", "1_2020"]查询,但它不会匹配3_1_20202_2_2020

下面是映射和分析的示例。它不在Nest中,但我认为您将能够进行翻译。

PUT pattern_split_demo
{
"settings": {
"analysis": {
"char_filter": {
"replace_char_filter": {
"type": "pattern_replace",
"pattern": "^TR0*",
"replacement": ""
}
},
"tokenizer": {
"split_tokenizer": {
"type": "simple_pattern_split",
"pattern": "_"
}
},
"analyzer": {
"split_analyzer": {
"tokenizer": "split_tokenizer",
"filter": [
"lowercase"
],
"char_filter": [
"replace_char_filter"
]
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "split_analyzer"
}
}
}
}
POST pattern_split_demo/_analyze
{
"text": "TR000002_1_2020",
"analyzer": "split_analyzer"
} => ["2", "1", "2020"]
POST pattern_split_demo/_doc?refresh=true
{
"content": "TR000002_1_2020"
}
POST pattern_split_demo/_search
{
"query": {
"match_phrase": {
"content": "TR000002_1_2020"
}
}
}

最新更新