You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Elasticsearch 6.1的Java环境中实现词干搜索?

解决Elasticsearch词干搜索的Java实现及查询无结果问题

首先,先帮你解决查询返回为空的核心问题:你已经定义了自定义词干分析器my_analyzer,但大概率你的value36字段并没有关联这个分析器。Elasticsearch默认会用standard分析器处理字段,所以索引skis的时候不会被词干化为ski,自然用ski查询匹配不到结果。

第一步:修正索引映射

你需要在创建索引时,明确为value36字段指定使用my_analyzer。修改你的索引创建请求,补充mapping部分:

PUT /data
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "filter": ["standard", "lowercase", "my_stemmer"]
        }
      },
      "filter": {
        "my_stemmer": {
          "type": "stemmer",
          "name": "english"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "value36": {
        "type": "text",
        "analyzer": "my_analyzer"  // 关键:绑定自定义分析器
      }
    }
  }
}

如果索引已经创建,你需要先删除旧索引再重新创建(已存在的字段无法修改分析器),或者用_reindex API迁移数据到新索引。

可以用_analyze接口验证分析器是否生效:

POST /data/_analyze
{
  "analyzer": "my_analyzer",
  "text": "skis"
}

如果返回的token是ski,说明词干处理已经正常工作了。

第二步:Java中实现词干搜索(基于High Level Rest Client)

假设你用的是Elasticsearch 7.x+版本,下面是完整的可运行示例:

1. 添加Maven依赖

<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
  <version>7.17.0</version> <!-- 请和你的ES服务器版本保持一致 -->
</dependency>
<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch</artifactId>
  <version>7.17.0</version>
</dependency>

2. 创建ES客户端连接

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.apache.http.HttpHost;

public class ESClientUtil {
    public static RestHighLevelClient getClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http")));
    }
}

3. 创建带自定义分析器的索引

import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;

import java.io.IOException;

public class CreateStemIndex {
    public static void main(String[] args) throws IOException {
        try (RestHighLevelClient client = ESClientUtil.getClient()) {
            // 构建settings配置
            Settings settings = Settings.builder()
                    .put("analysis.analyzer.my_analyzer.tokenizer", "standard")
                    .put("analysis.analyzer.my_analyzer.filter.0", "standard")
                    .put("analysis.analyzer.my_analyzer.filter.1", "lowercase")
                    .put("analysis.analyzer.my_analyzer.filter.2", "my_stemmer")
                    .put("analysis.filter.my_stemmer.type", "stemmer")
                    .put("analysis.filter.my_stemmer.name", "english")
                    .build();

            CreateIndexRequest request = new CreateIndexRequest("data");
            request.settings(settings);

            // 构建字段映射
            XContentBuilder mapping = XContentFactory.jsonBuilder()
                    .startObject()
                    .startObject("properties")
                    .startObject("value36")
                    .field("type", "text")
                    .field("analyzer", "my_analyzer")
                    .endObject()
                    .endObject()
                    .endObject();
            request.mapping(mapping);

            CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
            System.out.println("索引创建成功:" + response.isAcknowledged());
        }
    }
}

4. 索引测试文档

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;

import java.io.IOException;

public class IndexStemDocument {
    public static void main(String[] args) throws IOException {
        try (RestHighLevelClient client = ESClientUtil.getClient()) {
            XContentBuilder doc = XContentFactory.jsonBuilder()
                    .startObject()
                    .field("value36", "skis")
                    .endObject();

            IndexRequest request = new IndexRequest("data").id("1").source(doc);
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
            System.out.println("文档索引成功:" + response.getResult());
        }
    }
}

5. 执行词干查询

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;

public class StemSearchQuery {
    public static void main(String[] args) throws IOException {
        try (RestHighLevelClient client = ESClientUtil.getClient()) {
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            // 和你之前的查询逻辑一致,使用simple_query_string
            sourceBuilder.query(QueryBuilders.simpleQueryStringQuery("ski")
                    .field("value36"));

            SearchRequest request = new SearchRequest("data").source(sourceBuilder);
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            System.out.println("命中结果数:" + response.getHits().getTotalHits().value);
            response.getHits().forEach(hit -> System.out.println("文档内容:" + hit.getSourceAsString()));
        }
    }
}

额外小提示

  • 客户端版本必须和ES服务器版本严格匹配,否则会出现兼容性问题。
  • 如果你已有存量数据,可用_reindex API将旧数据迁移到新索引中。
  • 也可以用match查询替代simple_query_string,效果类似:QueryBuilders.matchQuery("value36", "ski")。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:52:51