读取 Solr 分词结果时,不能把 tokenizer 与每个 filter 的输出全部拼成一个词表。分析接口展示的是各阶段如何变换 token;如果业务需要最终查询词,应明确取查询侧最后一个阶段。

请求字段必须对应真实 schema

旧代码在 Solr 8.0 中对 bk_name 发起 /analysis/field 请求,fieldValue 为空、query 为待分析文本,再读取查询侧阶段。字段不能“随便选一个支持中文的字段”:它的 fieldType 和 query analyzer 必须正是业务需要观察的配置。Solr 8.0 分析界面

下面恢复成一个不混合阶段的方法,客户端由调用方传入并管理生命周期:

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.FieldAnalysisRequest;
import org.apache.solr.client.solrj.response.AnalysisResponseBase.AnalysisPhase;
import org.apache.solr.client.solrj.response.AnalysisResponseBase.TokenInfo;
import org.apache.solr.client.solrj.response.FieldAnalysisResponse;

public class QueryTokens {
    public static List<String> analyze(SolrClient client,
                                      String field, String sentence)
            throws IOException, SolrServerException {
        if (field == null || field.isEmpty() || sentence == null) {
            throw new IllegalArgumentException("字段与文本不能为空引用");
        }
        FieldAnalysisRequest request = new FieldAnalysisRequest("/analysis/field");
        request.addFieldName(field);
        request.setFieldValue("");
        request.setQuery(sentence);
        request.setMethod(SolrRequest.METHOD.POST);
        FieldAnalysisResponse response = request.process(client);
        FieldAnalysisResponse.Analysis analysis =
                response.getFieldNameAnalysis(field);
        if (analysis == null || analysis.getQueryPhases() == null) {
            return Collections.emptyList();
        }
        AnalysisPhase last = null;
        for (AnalysisPhase phase : analysis.getQueryPhases()) {
            last = phase;
        }
        if (last == null) {
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<>();
        for (TokenInfo token : last.getTokens()) {
            result.add(token.getText());
        }
        return result;
    }
}

方法按 8.0.0 API 整理,不包含目标 core 或中文插件安装。请求失败直接向调用者传播,不先打印异常再对 null response 继续操作。8.0.0 响应结构源码

最后阶段也不一定是一条线性词序

示例保留返回顺序和重复项;同义词等处理可能产生相同位置的多个 token。若要还原匹配关系,应同时保存 position、start、end 等信息,而不是只返回字符串。TokenInfo 定义

诊断入库文本时改看索引侧阶段,不要把查询链和索引链混用;两者可能刻意采用不同配置。

不按字节数判断中文

原代码通过字节长度阈值过滤“单字或非中文”,既受字符编码影响,也无法区分语言。单字中文、短英文、数字与混合文本都可能被误删。

是否去重、是否过滤停用词、是否保留同义词,应是明确业务规则。不能在解析响应时默默按编码长度删词,再把结果当作真实分析链输出。