如何在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服务器版本严格匹配,否则会出现兼容性问题。
- 如果你已有存量数据,可用
_reindexAPI将旧数据迁移到新索引中。 - 也可以用
match查询替代simple_query_string,效果类似:QueryBuilders.matchQuery("value36", "ski")。
内容的提问来源于stack exchange,提问作者eriksson
相关产品推荐
相关产品推荐

