【问题标题】:How to perform partial word searches on a text input with Elasticsearch?如何使用 Elasticsearch 对文本输入执行部分单词搜索?
【发布时间】:2020-12-27 15:31:42
【问题描述】:

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

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

TR0000022_1_2020TR000002_1_20202020。我使用的是 Elasticsearch 6.8,所以我不能使用 E7 中引入的内置 Search-As-You-Type。因此,我认为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)
),

正如in this answer 所述,这不起作用,因为标记器将字符拆分为20022020 等元素,因此我的查询返回了我的索引中包含的所有文档2020 年,例如 TR000002_1_2020TR000008_1_2020TR000003_6_2020

什么是 Elasticsearch 的最佳利用方式来实现我想要的搜索行为?我也看到query string 被使用过。谢谢!

【问题讨论】:

  • TR000002_1_2020 格式是否有一些通用模式,即可以从正则表达式中识别出来的模式?
  • @RussCam 嗯,所以该字符串中唯一一致的部分是TR000002 将递增(称为测试记录编号),_1_ 是任务编号,因此会发生变化,2020 是年份,因此显然也会递增
  • 好的,那么像TR\d+_\d+_\d+ 这样的模式会起作用吗?我认为模式标记器与带状疱疹和小写标记过滤器结合使用可能是一种有效的方法

标签: elasticsearch nest elasticsearch-6


【解决方案1】:

这是满足您要求的一种简单方法(我希望如此)。

  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"
    }
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-21
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-29
    • 1970-01-01
    • 2016-01-07
    相关资源
    最近更新 更多