摘要:

本文介绍了在JDK8环境下使用Elasticsearch7.17.21结合ik分词器实现文本相似度查询的方法。主要内容包括:1)环境准备,要求JDK1.8.x、ES7.17.21和匹配版本的ik分词器;2)创建索引并指定ik分词器;3)三种相似度查询方式(match、multi_match和fuzzy查询)的实现;4)Java代码示例,展示如何使用RestHighLevelClient构建查询并解析结果。文章还提供了优化建议,如选择合适的分词器、调整评分权重和处理拼写错误等,以提升查询效果。

实现步骤(完整可落地)

前置准备
  1. 环境确认
    • JDK 版本:1.8.x(ES 7.17.x 要求 JDK8 或 11,优先 8)
    • ES 版本:7.17.21
    • IK 分词器:elasticsearch-analysis-ik-7.17.21(需与 ES 版本严格一致)
  2. IK 分词器安装

    bash

    运行

    # 进入 ES 插件目录
    cd elasticsearch-7.17.21/plugins
    # 创建 ik 目录
    mkdir ik
    cd ik
    # 下载对应版本的 ik 分词器压缩包并解压(或手动下载后上传)
    wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.21/elasticsearch-analysis-ik-7.17.21.zip
    unzip elasticsearch-analysis-ik-7.17.21.zip
    # 重启 ES 生效
    cd ../../bin
    ./elasticsearch -d
    

步骤 1:创建索引(指定 IK 分词器)

首先创建索引,并为字段指定 IK 分词器(ik_max_word 细粒度分词 /ik_smart 粗粒度分词),这是相似度查询的基础。

请求示例(REST API)

http

PUT /article_index
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "analysis": {
      "analyzer": {
        "ik_analyzer": {  # 自定义 ik 分词器
          "type": "custom",
          "tokenizer": "ik_max_word",  # 细粒度分词(适合相似度查询)
          "filter": ["lowercase"]  # 转小写,避免大小写影响匹配
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {  # 要做相似度查询的字段
        "type": "text",
        "analyzer": "ik_analyzer",  # 使用自定义 ik 分词器
        "search_analyzer": "ik_analyzer"
      },
      "content": {
        "type": "text",
        "analyzer": "ik_analyzer",
        "search_analyzer": "ik_analyzer"
      }
    }
  }
}
步骤 2:插入测试数据

http

POST /article_index/_bulk
{"index":{"_id":1}}
{"title":"Java 高并发编程实战","content":"讲解 Java 并发编程的核心原理和实战案例"}
{"index":{"_id":2}}
{"title":"Java 并发编程艺术","content":"深入剖析 Java 并发编程的底层实现"}
{"index":{"_id":3}}
{"title":"Python 高并发编程","content":"Python 异步编程和多线程实战"}

步骤 3:实现相似度查询(核心)

ES 实现相似度查询的核心是基于分词的相关性评分,常用 3 种方式,按实用性排序:

方式 1:match 查询(最常用,基于 TF-IDF 评分)

match 查询会先对查询词做 IK 分词,再匹配文档,返回按相似度评分(_score)排序的结果。

http

GET /article_index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Java 并发编程",  # 查询词
        "minimum_should_match": "70%"  # 至少匹配 70% 的分词项(提升精准度)
      }
    }
  }
}

返回结果(核心部分)

json

{
  "hits": [
    {
      "_id": "1",
      "_score": 1.8234,  # 相似度评分(越高越相似)
      "_source": {"title":"Java 高并发编程实战"}
    },
    {
      "_id": "2",
      "_score": 1.5678,
      "_source": {"title":"Java 并发编程艺术"}
    },
    {
      "_id": "3",
      "_score": 0.2345,
      "_source": {"title":"Python 高并发编程"}
    }
  ]
}
方式 2:multi_match 查询(多字段相似度匹配)

同时对多个字段(如 title + content)做相似度查询,适合全文检索场景。

http

GET /article_index/_search
{
  "query": {
    "multi_match": {
      "query": "Java 并发编程",
      "fields": ["title^2", "content"],  # title 权重×2(更关注标题)
      "type": "best_fields",  # 取匹配度最高的字段评分
      "minimum_should_match": "60%"
    }
  }
}
方式 3:fuzzy 查询(模糊匹配,容错输入错误)

适合处理查询词有拼写错误的场景(如把「并发」写成「并法」),基于编辑距离(Levenshtein)实现。

http

GET /article_index/_search
{
  "query": {
    "fuzzy": {
      "title": {
        "value": "Java 并法编程",  # 含拼写错误的查询词
        "fuzziness": "AUTO",  # 自动适配编辑距离(单字错误可匹配)
        "prefix_length": 1  # 前缀 1 个字符不模糊(提升精准度)
      }
    }
  }
}

步骤 4:Java 代码实现(JDK8 适配)

以下是基于 ES 官方 Java High Level REST Client(7.17.21)的代码示例,实现相似度查询:

1. 引入依赖(Maven)

xml

<dependencies>
  <!-- ES High Level Client -->
  <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.17.21</version>
  </dependency>
  <!-- ES 核心依赖 -->
  <dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.17.21</version>
  </dependency>
  <!-- JDK8 兼容依赖 -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.5</version>
  </dependency>
</dependencies>
2. 核心代码

java

运行

import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;

public class ESSimilarityQuery {
    // 创建 ES 客户端(7.17.21 适配 JDK8)
    private static RestHighLevelClient createClient() {
        return new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );
    }

    // 相似度查询(match 方式)
    public static void searchSimilar(String indexName, String field, String queryText) throws IOException {
        RestHighLevelClient client = createClient();
        
        // 1. 构建查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery(field, queryText)
                .minimumShouldMatch("70%"));  // 至少匹配 70% 分词项
        sourceBuilder.from(0);
        sourceBuilder.size(10);  // 返回前 10 条
        
        // 2. 构建搜索请求
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(sourceBuilder);
        
        // 3. 执行查询
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        
        // 4. 解析结果
        System.out.println("相似度查询结果:");
        for (SearchHit hit : response.getHits().getHits()) {
            String id = hit.getId();
            float score = hit.getScore();  // 相似度评分
            String source = hit.getSourceAsString();
            System.out.printf("ID: %s, 相似度评分: %.4f, 内容: %s%n", id, score, source);
        }
        
        // 关闭客户端
        client.close();
    }

    public static void main(String[] args) {
        try {
            // 查询 "title" 字段中与 "Java 并发编程" 相似的文档
            searchSimilar("article_index", "title", "Java 并发编程");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • RestHighLevelClient:ES 7.x 官方推荐的客户端,适配 JDK8;
  • matchQuery:核心相似度查询语法,结合 IK 分词器的结果做 TF-IDF 评分;
  • minimumShouldMatch:控制匹配的严格程度,值越高越精准(如 80% 只返回高度相似的文档);
  • getScore():获取文档的相似度评分(0~∞),评分越高表示越相似。

关键优化点(提升相似度查询效果)

  1. 分词器选择
    • 相似度查询优先用 ik_max_word(细粒度分词),能拆分更多关键词,提升匹配维度;
    • 若需精准匹配,可混用 ik_smart + 关键词权重。
  2. 评分优化
    • 通过 boost 调整字段权重(如 title^2 让标题权重是内容的 2 倍);
    • 结合 function_score 自定义评分规则(如按发布时间、点击量加权)。
  3. 容错处理
    • 对长文本查询,使用 match_phrase(短语匹配)+ slop(允许短语偏移),提升语义相似度;
    • 对拼写错误,叠加 fuzzy 查询。

验证查询效果

  1. 运行 Java 代码后,控制台会输出文档 ID、相似度评分和内容;
  2. 评分越高的文档,与查询词「Java 并发编程」的相似度越高;
  3. 可通过调整 minimum_should_match 的值,测试不同严格程度的匹配结果。

总结

  1. 实现核心:先为字段配置 IK 分词器,再通过 match/multi_match/fuzzy 查询实现相似度检索,ES 会基于 TF-IDF 自动计算相似度评分;
  2. Java 适配:使用 ES 7.17.21 版本的 High Level Client,完全兼容 JDK8,核心是构建 matchQuery 并解析评分结果;
  3. 效果优化:优先用 ik_max_word 分词,通过 minimum_should_match 和字段权重(boost)调整匹配精准度。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐