You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ElasticSearch NEST自定义词连接分析器返回结果异常排查

Elasticsearch NEST API自动补全:连写词无法匹配带空格结果的解决方案

嘿,我来帮你搞定这个问题!你现在遇到的核心问题是:当用户搜索Transhex这类连写词时,没法返回Trans Hex这种带空格的结果,本质是你的索引分析器没有为带空格的词条生成对应的连写形式token,导致搜索连写词时匹配不到。

先梳理下你当前配置的问题:

之前配置的问题分析

  1. 最初用standard分词器的问题
    standard分词器会把Trans Hex拆成两个独立token["Trans", "Hex"],但你的word-joiner-filter(WordDelimiter过滤器)是作用于单个token的——它只会拆分单个token里的大小写/数字分隔(比如TransHex会拆成transhex),但没法把两个独立的token(transhex)连接成transhex。所以索引里根本没有transhex这个token,自然搜不到。

  2. 改成keyword分词器后的问题
    这个分词器会把整个Trans Hex当成一个完整token,里面的空格不会被拆分。WordDelimiter过滤器没法识别空格作为分隔符,所以还是不会生成连写的transhextoken。再加上你把EdgeNGrammin_gram设为1,导致大量短ngram(比如ppe)被生成,搜索perfectpools时会匹配到所有包含这些短ngram的无关词条(比如Perfect Hideaways里的perfect)。

正确的配置方案

要实现“搜连写词返回带空格结果”,我们需要让索引时同时保留原带空格的词条、拆分后的单个词、以及连写形式的词。具体要调整分析器的过滤器链,新增shingle过滤器生成相邻词的组合,再用pattern_replace去掉空格得到连写token,最后用EdgeNGram生成补全需要的ngram。

这里是调整后的NEST代码:

var response = this.Client.CreateIndex("company-index", index => index
    .Mappings(ms => ms
        .Map<CompanyDocument>(m => m
            .Properties(p => p
                .Text(t => t
                    .Name(n => n.CompanyName)
                    .Analyzer("auto-complete")
                    .Fields(ff => ff
                        .Keyword(k => k.Name("keyword"))
                    )
                )
            )
        )
    )
    .Settings(f => f
        .Analysis(analysis => analysis
            .Analyzers(analyzers => analyzers
                .Custom("auto-complete", a => a
                    .Tokenizer("standard")
                    .Filters(
                        "lowercase",
                        "word-delimiter",
                        "shingle-join",
                        "remove-space",
                        "auto-complete-filter"
                    )
                )
            )
            .TokenFilters(tokenFilter => tokenFilter
                // 处理单个token的大小写/数字拆分,同时生成连写形式(比如TransHex→transhex)
                .WordDelimiter("word-delimiter", t => t
                    .SplitOnCaseChange(true)
                    .CatenateAll(true)
                )
                // 生成相邻词的组合(比如trans+hex→trans hex)
                .Shingle("shingle-join", s => s
                    .MinShingleSize(2)
                    .MaxShingleSize(2)
                    .OutputUnigrams(true) // 保留原单个词,不影响正常空格搜索
                )
                // 把shingle生成的带空格组合变成连写形式(trans hex→transhex)
                .PatternReplace("remove-space", pr => pr
                    .Pattern(@" ")
                    .Replacement("")
                )
                // 生成EdgeNGram用于自动补全,min_gram设为3减少无关匹配
                .EdgeNGram("auto-complete-filter", t => t
                    .MinGram(3)
                    .MaxGram(30)
                )
            )
        )
    )
);

为什么这个配置能解决问题?

Perfect Pools举例,索引时的处理流程:

  1. standard分词器拆分出["Perfect", "Pools"]
  2. lowercase转成["perfect", "pools"]
  3. word-delimiter处理单个token(这里没有拆分,保持原token)
  4. shingle-join生成["perfect", "pools", "perfect pools"]
  5. remove-spaceperfect pools变成perfectpools,最终token列表是["perfect", "pools", "perfectpools"]
  6. auto-complete-filter为每个token生成3到30长度的ngram(比如perfect生成perperf...perfectperfectpools生成perperf...perfectpools

当你搜索perfectpools时,分析器会把它处理成["perfectpools"],并生成对应的ngram,这些ngram会和索引里Perfect Pools生成的perfectpools的ngram匹配,从而正确返回目标结果。

另外,把min_gram设为3能避免大量短ngram带来的无关匹配,让搜索结果更精准。

内容的提问来源于stack exchange,提问作者R4nc1d

火山引擎 最新活动