一、建议器简介

1、简介

官方文档:https://elasticsearch.bookhub.tech/rest_apis/search_apis/suggesters

ES建议器本质是通过“预计算”或“实时相似度匹配”,在用户输入过程中(或输入后)提供优化建议,解决两大核心问题:
输入效率:通过自动补全减少用户输入量(如搜索框实时联想)。
输入准确性:通过拼写纠错修正用户的错误输入(如“appel”修正为“apple”)。

所有建议器均通过ES的_searchAPI中的suggest字段触发,不影响主查询结果,仅返回建议内容。

2、四大建议器对比

建议器类型 核心作用 适用场景 优势 关键限制 示例
Completion 实时前缀补全 搜索框联想 速度极快(内存查询) 仅支持前缀,需专用字段 输入“iph” → 联想“iphone 15”
Term 单词拼写纠错 单个错词修正 逻辑简单,响应快 不考虑上下文 输入“appel” → 修正为“apple”
Phrase 短语拼写纠错 多词短语修正 考虑上下文连贯性 依赖n-gram,性能略低 输入“red appel” → 修正为“red apple”
Context Completion 上下文感知补全 带维度筛选的联想 支持业务维度过滤 需提前定义上下文结构 北京地区搜“咖啡” → 联想“北京星巴克”

二、四种建议器介绍与使用

1. Completion Suggestor(自动补全建议器)

核心作用:针对“实时前缀补全”设计,比如搜索框输入“iph”时,实时返回“iphone”“iphone 15”等结果,速度极快(毫秒级)。

(1)工作原理

基于FST(有限状态转换器)数据结构,将补全候选词提前构建成FST并存储在内存中,而非实时遍历索引。
仅支持前缀匹配(不支持模糊、后缀匹配),需提前定义专用的completion类型字段。

(2)适用场景

  • 搜索框实时联想(如电商商品名、新闻标题、APP名称)。
  • 需“输入即反馈”的高频交互场景。

(3)关键参数

参数 说明 示例值
field 必须是completion类型的字段 “product_name”
prefix 用户输入的前缀(建议内容的匹配依据) “iph”
size 返回的建议数量(默认10) 5
skip_duplicates 去重相同的建议内容 true

(4)使用示例(DSL)

1.第一步:创建索引并定义completion字段

PUT /products
{
  "mappings": {
    "properties": {
      "product_name": {
        "type": "completion"  // 专用补全字段
      },
      "price": {
        "type": "double"
      }
    }
  }
}

2.第二步:插入数据(补全候选词需明确)

POST /products/_doc/1
{
  "product_name": "iphone 15",
  "price": 5999
}

POST /products/_doc/2
{
  "product_name": "iphone 15 pro",
  "price": 7999
}

3.第三步:发起补全查询(输入“iph”)

GET /products/_search
{
  "suggest": {
    "product_suggest": {  // 自定义建议名称(可任意)
      "completion": {
        "field": "product_name",
        "prefix": "iph",
        "size": 5,
        "skip_duplicates": true
      }
    }
  }
}

4.返回结果:会在suggest.product_suggest.options中返回“iphone 15”“iphone 15 pro”等建议。

(5)使用实战

目标:实现“输入即联想”的前缀补全(如搜索框实时提示)。

Step 1:创建索引(定义completion字段)
需专门定义 completion 类型字段(底层基于FST存储,优化前缀查询速度)。

PUT /product_suggest  # 索引名:商品补全索引
{
  "mappings": {
    "properties": {
      "suggest": {  # 补全专用字段(名称可自定义)
        "type": "completion",
        "analyzer": "ik_smart",  # 可选:指定分词器(如中文用IK"preserve_separators": true,  # 保留分隔符(如空格、符号)
        "preserve_position_increments": true  # 保留位置增量(影响分词位置)
      },
      "name": { "type": "text" },  # 商品名(非补全用,仅存储)
      "price": { "type": "double" }
    }
  }
}

关键参数
analyzer:分词器(中文必须指定,如ik_smart;英文默认standard)。
preserve_separators:若为false,会忽略空格(如“iphone 15”和“iphone15”视为同一前缀)。

Step 2:插入数据(定义补全候选词)
补全候选词通过 input 字段指定(支持数组,可添加多个同义词/变体)。

POST /product_suggest/_doc/1
{
  "name": "iPhone 15 128G",
  "price": 5999,
  "suggest": {
    "input": ["iphone 15", "苹果15", "iPhone15"]  # 多个候选词,覆盖不同输入习惯
  }
}

POST /product_suggest/_doc/2
{
  "name": "iPhone 15 Pro 256G",
  "price": 7999,
  "suggest": {
    "input": ["iphone 15 pro", "苹果15 Pro"]
  }
}

Step 3:发起补全查询
通过 prefix 指定用户输入的前缀,获取匹配的候选词。

GET /product_suggest/_search
{
  "suggest": {
    "product_complete": {  # 自定义建议名称(可任意)
      "completion": {
        "field": "suggest",  # 关联补全字段
        "prefix": "iph",  # 用户输入的前缀(如搜索框实时输入)
        "size": 5,  # 返回最多5条建议
        "skip_duplicates": true,  # 去重相同建议
        "fuzzy": {  # 可选:允许前缀模糊匹配(如输入“ihp”也能匹配“iph”)
          "fuzziness": "AUTO"  # 自动模糊度(1-2次编辑)
        }
      }
    }
  }
}

Step 4:解析结果
建议结果在 suggest.product_complete.options 中,包含补全文本和原始文档ID:

{
  "suggest": {
    "product_complete": [
      {
        "text": "iphone 15",
        "offset": 0,
        "length": 3,
        "options": [
          { "text": "iphone 15", "_index": "product_suggest", "_id": "1", "score": 1.0 },
          { "text": "iphone 15 pro", "_index": "product_suggest", "_id": "2", "score": 1.0 }
        ]
      }
    ]
  }
}

2. Term Suggestor(单词拼写纠错建议器)

核心作用:针对单个词的拼写错误提供纠错建议,比如用户输入“appel”时,返回“apple”;输入“teh”时,返回“the”。

(1)工作原理

基于编辑距离(Levenshtein Distance) 计算用户输入词与索引中词的相似度(默认允许最多2次编辑:增、删、改字符)。
仅分析单个词,不考虑词与词的上下文关系。

(2)适用场景

单个关键词的拼写纠错(如“搜索框输入单个错词”“标签输入错误”)。
需快速修正简单输入错误的场景。

(3)关键参数

参数 说明 示例值
text 用户输入的错误文本(单个词) “appel”
field 目标字段(需是text/keyword类型) “product_name”
suggest_mode 建议触发模式:
- missing:输入词无结果时才建议
- popular:优先建议出现频率高的词
- always:无论是否有结果都建议
“missing”
max_edits 最大编辑距离(1-2,默认2) 2
min_word_length 最小词长(低于此长度不建议,默认4) 3

(4)使用示例(DSL)

1.前提:使用已有的products索引(product_name为text类型)。
2.发起单词纠错查询(输入“appel”)

GET /products/_search
{
  "suggest": {
    "term_suggest": {
      "text": "appel",  // 用户输入的错词
      "term": {
        "field": "product_name",
        "suggest_mode": "missing",
        "max_edits": 2,
        "min_word_length": 3
      }
    }
  }
}

3.返回结果:会在suggest.term_suggest.options中返回“apple”作为建议,并附带相似度得分。

(5)使用实战

目标:修正单个词的拼写错误(如“teh”→“the”)。

Step 1:准备索引(复用现有text字段)
无需专用字段,直接使用已有 textkeyword 字段(需确保字段有足够的词项数据)。

PUT /article  # 示例:文章索引
{
  "mappings": {
    "properties": {
      "title": { "type": "text", "analyzer": "standard" }  # 用于纠错的字段
    }
  }
}

# 插入测试数据
POST /article/_doc/1
{ "title": "the quick brown fox" }

POST /article/_doc/2
{ "title": "apple is a fruit" }

Step 2:发起单词纠错查询
针对用户输入的错误单词(如“appel”“teh”)生成建议。

GET /article/_search
{
  "suggest": {
    "term_correction": {  # 自定义建议名称
      "text": "appel",  # 用户输入的错误词
      "term": {
        "field": "title",  # 目标字段
        "suggest_mode": "missing",  # 仅当输入词无结果时建议
        "max_edits": 2,  # 最大编辑距离(1-2,默认2"min_word_length": 3,  # 最短词长(低于此长度不建议)
        "sort": "frequency"  # 按词频排序(默认按相似度)
      }
    }
  }
}

Step 3:解析结果
建议结果包含纠错词、相似度得分和词频:

{
  "suggest": {
    "term_correction": [
      {
        "text": "appel",
        "offset": 0,
        "length": 5,
        "options": [
          { "text": "apple", "score": 0.8, "freq": 1 }  # freq:词在索引中出现的次数
        ]
      }
    ]
  }
}

3. Phrase Suggestor(短语拼写纠错建议器)

核心作用:在Term Suggestor基础上升级,支持多词短语的拼写纠错,且考虑词的上下文和共现频率,比如用户输入“red appel”时,返回“red apple”;输入“the quick brow fox”时,返回“the quick brown fox”。

(1)工作原理

1.第一步:对短语中的每个词单独做Term纠错,生成多个候选词组合。
2.第二步:基于n-gram模型(默认bigram,即2个词的组合)计算候选短语的“合理性”(如“red apple”在索引中出现的频率远高于“red appel”)。
3.第三步:筛选出得分最高的合理短语作为建议。

(2)适用场景

  • 多词短语的输入纠错(如“搜索句子”“长标题查询”“商品描述搜索”)。
  • 需保证短语语义连贯的场景。

(3)关键参数

参数 说明 示例值
text 用户输入的错误短语 “red appel”
field 目标字段(需是text类型,且开启了n-gram分词) “product_name”
max_errors 短语中允许的最大错误词数(默认1) 1
confidence 置信度(0-1,得分高于此值才返回,默认1) 0.8
gram_size n-gram的长度(默认2,即bigram) 2

(4)使用示例(DSL)

1.前提:确保product_name字段支持n-gram(若未配置,需先更新映射):

PUT /products/_mapping
{
  "properties": {
    "product_name": {
      "type": "text",
      "analyzer": "standard",
      "fields": {
        "ngram": {  // 新增n-gram子字段
          "type": "text",
          "analyzer": "ngram_analyzer"
        }
      }
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "tokenizer": "standard",
          "filter": ["ngram_filter"]
        }
      },
      "filter": {
        "ngram_filter": {
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 3
        }
      }
    }
  }
}

2.发起短语纠错查询(输入“red appel”)

GET /products/_search
{
  "suggest": {
    "phrase_suggest": {
      "text": "red appel",  // 用户输入的错误短语
      "phrase": {
        "field": "product_name.ngram",  // 使用n-gram子字段
        "max_errors": 1,
        "confidence": 0.8,
        "gram_size": 2
      }
    }
  }
}

3.返回结果:会在suggest.phrase_suggest.options中返回“red apple”作为建议,并附带短语的合理性得分。

(5)使用实战

目标:修正多词短语的拼写错误(如“red appel”→“red apple”),需结合上下文。

Step 1:创建索引(配置n-gram分词)
Phrase依赖n-gram模型计算短语合理性,需提前配置n-gram分词器。

PUT /phrase_corpus  # 短语纠错语料库
{
  "settings": {
    "analysis": {
      "analyzer": {
        "phrase_analyzer": {  # 自定义n-gram分析器
          "tokenizer": "standard",
          "filter": ["lowercase", "ngram_filter"]  # 转小写+ngram过滤
        }
      },
      "filter": {
        "ngram_filter": {  # n-gram过滤器(提取2-3个连续字符)
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 3
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {  # 用于短语纠错的字段
        "type": "text",
        "analyzer": "phrase_analyzer"
      }
    }
  }
}

# 插入测试短语
POST /phrase_corpus/_doc/1
{ "content": "red apple" }

POST /phrase_corpus/_doc/2
{ "content": "the quick brown fox" }

Step 2:发起短语纠错查询
针对错误短语(如“red appel”)生成建议。

GET /phrase_corpus/_search
{
  "suggest": {
    "phrase_correction": {  # 自定义建议名称
      "text": "red appel",  # 用户输入的错误短语
      "phrase": {
        "field": "content",  # 目标字段(需n-gram分词)
        "max_errors": 1,  # 允许最多1个错词
        "confidence": 0.7,  # 得分≥0.7才返回(降低可增加建议数量)
        "gram_size": 2,  # 用bigram(2词组合)计算合理性
        "real_word_error_likelihood": 0.95  # 真实词出错的概率(默认0.95}
    }
  }
}

Step 3:解析结果
建议结果包含修正后的短语及整体得分(得分越高越合理):

{
  "suggest": {
    "phrase_correction": [
      {
        "text": "red appel",
        "offset": 0,
        "length": 9,
        "options": [
          { "text": "red apple", "score": 0.85 }  # 得分基于bigram共现频率
        ]
      }
    ]
  }
}

4. Context Suggestor(上下文感知自动补全建议器)

核心作用:作为Completion Suggestor的扩展,支持按上下文维度过滤补全结果,比如“在‘北京’地区搜‘咖啡’”时,只返回“北京星巴克”“北京瑞幸”,而不返回“上海星巴克”。

(1)工作原理

  • 在Completion的FST结构中,为每个补全候选词附加“上下文元数据”(如category(类别)、location(地区))。
  • 查询时指定上下文条件,仅返回匹配该条件的补全结果。

(2)适用场景

  • 带维度筛选的自动补全(如电商“分类+关键词”补全、本地生活“地区+服务”补全)。
  • 需按业务场景缩小补全范围的场景。

(3)关键参数

参数 说明 示例值
field 必须是配置了context的completion字段 “product_suggest”
prefix 用户输入的前缀 “咖啡”
contexts 上下文过滤条件(键为上下文名称,值为过滤值) {“location”: “北京”}

(4)使用示例(DSL)

1.第一步:创建带context的completion字段

PUT /local_shops
{
  "mappings": {
    "properties": {
      "shop_name": {
        "type": "text"
      },
      "product_suggest": {  // 带上下文的补全字段
        "type": "completion",
        "contexts": [
          {
            "name": "location",  // 上下文名称(地区)
            "type": "category"   // 上下文类型(类别型)
          }
        ]
      }
    }
  }
}

2.第二步:插入带上下文的数据

POST /local_shops/_doc/1
{
  "shop_name": "北京星巴克",
  "product_suggest": {
    "input": "星巴克",  // 补全候选词
    "contexts": {
      "location": "北京"  // 附加地区上下文
    }
  }
}

POST /local_shops/_doc/2
{
  "shop_name": "上海星巴克",
  "product_suggest": {
    "input": "星巴克",
    "contexts": {
      "location": "上海"
    }
  }
}

3.第三步:按上下文查询(北京地区搜“星”)

GET /local_shops/_search
{
  "suggest": {
    "context_suggest": {
      "completion": {
        "field": "product_suggest",
        "prefix": "星",
        "contexts": {
          "location": "北京"  // 仅返回北京地区的“星巴克”
        }
      }
    }
  }
}

4.返回结果:仅返回“北京星巴克”的补全建议,过滤“上海星巴克”。

(5)使用实战

目标:结合上下文(如地区、类别)过滤补全结果(如“北京+咖啡”只联想本地咖啡店)。

Step 1:创建索引(定义上下文)
completion 字段中添加 contexts,支持两种类型:
category:类别型(如地区、分类)。
geo:地理型(如经纬度范围)。

PUT /local_business  # 本地商户索引
{
  "mappings": {
    "properties": {
      "business_suggest": {  # 带上下文的补全字段
        "type": "completion",
        "contexts": [
          {
            "name": "city",  # 上下文名称(城市)
            "type": "category",  # 类别型上下文
            "path": "city"  # 可选:关联文档中的city字段(自动提取上下文值)
          }
        ]
      },
      "name": { "type": "text" },
      "city": { "type": "keyword" }  # 存储城市信息(用于上下文关联)
    }
  }
}

Step 2:插入带上下文的数据
通过 contexts 字段指定补全候选词的上下文(如城市)。

POST /local_business/_doc/1
{
  "name": "星巴克(北京朝阳店)",
  "city": "北京",
  "business_suggest": {
    "input": ["星巴克", "Starbucks"],  # 补全候选词
    "contexts": { "city": "北京" }  # 关联上下文(城市=北京)
  }
}

POST /local_business/_doc/2
{
  "name": "星巴克(上海浦东店)",
  "city": "上海",
  "business_suggest": {
    "input": ["星巴克", "Starbucks"],
    "contexts": { "city": "上海" }
  }
}

Step 3:按上下文查询
指定 contexts 过滤补全结果(如只查“北京”的星巴克)。

GET /local_business/_search
{
  "suggest": {
    "context_complete": {
      "completion": {
        "field": "business_suggest",
        "prefix": "星",  # 用户输入前缀
        "contexts": {
          "city": "北京"  # 上下文过滤:只返回北京的结果
        },
        "size": 3
      }
    }
  }
}

Step 4:解析结果
仅返回符合上下文条件的补全建议:

{
  "suggest": {
    "context_complete": [
      {
        "text": "星",
        "offset": 0,
        "length": 1,
        "options": [
          { "text": "星巴克", "_index": "local_business", "_id": "1", "score": 1.0 }
        ]
      }
    ]
  }
}

三、Java高级客户端使用建议器

1. 依赖配置(Maven)

pom.xml 中添加 7.10 版本的 High Level REST Client 依赖:

<dependencies>
    <!-- Elasticsearch 核心依赖 -->
    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>7.10.0</version>
    </dependency>
    <!-- High Level REST Client -->
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.10.0</version>
    </dependency>
</dependencies>

2. 客户端初始化

创建 RestHighLevelClient 实例(单例模式,避免频繁创建连接):

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

public class EsClient {
    private static RestHighLevelClient client;

    // 初始化客户端(连接本地 ES,默认端口 9200)
    public static RestHighLevelClient getClient() {
        if (client == null) {
            client = new RestHighLevelClient(
                RestClient.builder(
                    "localhost:9200"  // 若为集群,可添加多个节点:"host2:9200", "host3:9200"
                )
            );
        }
        return client;
    }

    // 关闭客户端(程序退出时调用)
    public static void closeClient() throws IOException {
        if (client != null) {
            client.close();
        }
    }
}

3.实战场景:商品搜索建议

需求:实现“商品搜索框”功能,包含两个核心能力:
1.输入前缀时实时补全(如输入“iph” → 联想“iphone 15”)。
2.输入错误单词时自动纠错(如输入“appel” → 修正为“apple”)。

Step 1:创建索引(含 Completion 和 Text 字段)
需定义两个关键字段:
suggestcompletion 类型,用于自动补全。
nametext 类型,用于 Term 纠错(基于商品名称的词项)。

import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import java.io.IOException;

public class SuggestIndexCreator {
    public static void createProductIndex() throws IOException {
        RestHighLevelClient client = EsClient.getClient();

        // 1. 创建索引请求
        CreateIndexRequest request = new CreateIndexRequest("product_suggest");

        // 2. 设置映射(mappings)
        XContentBuilder mappings = JsonXContent.contentBuilder()
            .startObject()
                .startObject("properties")
                    // Completion 字段:用于自动补全
                    .startObject("suggest")
                        .field("type", "completion")
                        .field("analyzer", "ik_smart")  // 中文分词器(需 ES 安装 IK 插件)
                        .field("preserve_separators", true)
                    .endObject()
                    // Text 字段:用于 Term 纠错(基于商品名称的词项)
                    .startObject("name")
                        .field("type", "text")
                        .field("analyzer", "standard")  // 英文默认分词器
                    .endObject()
                    // 其他字段(如价格)
                    .startObject("price")
                        .field("type", "double")
                    .endObject()
                .endObject()
            .endObject();
        request.mapping(mappings);

        // 3. 执行创建请求
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        if (response.isAcknowledged()) {
            System.out.println("索引创建成功:product_suggest");
        } else {
            System.err.println("索引创建失败");
        }
    }

    public static void main(String[] args) throws IOException {
        createProductIndex();
        EsClient.closeClient();
    }
}

Step 2:插入测试数据
插入包含补全候选词和商品名称的数据:

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;

public class ProductDataInserter {
    public static void insertData() throws IOException {
        RestHighLevelClient client = EsClient.getClient();

        // 数据1:iPhone 15
        String json1 = "{" +
            "\"name\": \"iphone 15 128G\"," +
            "\"price\": 5999," +
            "\"suggest\": {\"input\": [\"iphone 15\", \"苹果15\", \"iPhone15\"]}" +
        "}";
        IndexRequest request1 = new IndexRequest("product_suggest").id("1");
        request1.source(json1, XContentType.JSON);
        IndexResponse response1 = client.index(request1, RequestOptions.DEFAULT);
        System.out.println("插入数据1:" + response1.getId());

        // 数据2:Apple Watch
        String json2 = "{" +
            "\"name\": \"apple watch series 8\"," +
            "\"price\": 3199," +
            "\"suggest\": {\"input\": [\"apple watch\", \"苹果手表\"]}" +
        "}";
        IndexRequest request2 = new IndexRequest("product_suggest").id("2");
        request2.source(json2, XContentType.JSON);
        IndexResponse response2 = client.index(request2, RequestOptions.DEFAULT);
        System.out.println("插入数据2:" + response2.getId());

        // 强制刷新索引(确保数据可查)
        client.indices().refresh(null, RequestOptions.DEFAULT);
    }

    public static void main(String[] args) throws IOException {
        insertData();
        EsClient.closeClient();
    }
}

Step 3:集成建议器查询(Completion + Term)
构建包含两种建议的查询请求,同时返回补全结果和纠错建议:

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.term.TermSuggestion;
import java.io.IOException;
import java.util.List;

public class ProductSuggestService {

    /**
     * 同时获取补全建议和纠错建议
     * @param userInput 用户输入的文本(如"iph"或"appel")
     */
    public static void getSuggestions(String userInput) throws IOException {
        RestHighLevelClient client = EsClient.getClient();

        // 1. 创建搜索请求
        SearchRequest searchRequest = new SearchRequest("product_suggest");

        // 2. 构建建议器
        SuggestBuilder suggestBuilder = new SuggestBuilder();

        // 2.1 添加 Completion 补全建议(输入前缀联想)
        CompletionSuggestionBuilder completionSuggestion = SuggestBuilders
            .completionSuggestion("suggest")  // 关联 completion 字段
            .prefix(userInput)  // 用户输入的前缀
            .size(5)  // 返回5条建议
            .skipDuplicates(true)  // 去重
            .fuzziness(Fuzziness.AUTO);  // 允许模糊匹配(如"ihp"匹配"iph")
        suggestBuilder.addSuggestion("product_completion", completionSuggestion);

        // 2.2 添加 Term 纠错建议(单词拼写错误修正)
        TermSuggestionBuilder termSuggestion = SuggestBuilders
            .termSuggestion("name")  // 关联 text 字段(用于纠错)
            .text(userInput)  // 用户输入的文本
            .suggestMode(TermSuggestionBuilder.SuggestMode.MISSING)  // 无结果时才建议
            .maxEdits(2)  // 最大编辑距离
            .sort(TermSuggestionBuilder.Sort.FREQUENCY);  // 按词频排序
        suggestBuilder.addSuggestion("product_term_correction", termSuggestion);

        // 3. 将建议器添加到搜索请求
        searchRequest.source().suggest(suggestBuilder);

        // 4. 执行查询
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

        // 5. 解析结果
        parseSuggestions(response);
    }

    /**
     * 解析建议结果
     */
    private static void parseSuggestions(SearchResponse response) {
        Suggest suggest = response.getSuggest();

        // 5.1 解析 Completion 补全建议
        CompletionSuggestion completionSuggestion = suggest.getSuggestion("product_completion");
        List<CompletionSuggestion.Entry.Option> completionOptions = completionSuggestion.getOptions();
        System.out.println("\n===== 补全建议 =====");
        if (completionOptions.isEmpty()) {
            System.out.println("无补全建议");
        } else {
            for (CompletionSuggestion.Entry.Option option : completionOptions) {
                System.out.println("补全词:" + option.getText().string() + 
                                   ",文档ID:" + option.getHit().getId());
            }
        }

        // 5.2 解析 Term 纠错建议
        TermSuggestion termSuggestion = suggest.getSuggestion("product_term_correction");
        List<TermSuggestion.Entry.Option> termOptions = termSuggestion.getOptions();
        System.out.println("\n===== 纠错建议 =====");
        if (termOptions.isEmpty()) {
            System.out.println("无纠错建议");
        } else {
            for (TermSuggestion.Entry.Option option : termOptions) {
                System.out.println("纠错词:" + option.getText().string() + 
                                   ",相似度:" + option.getScore() + 
                                   ",词频:" + option.getFreq());
            }
        }
    }

    public static void main(String[] args) throws IOException {
        // 测试1:输入前缀"iph"(预期补全"iphone 15"等)
        System.out.println("----- 用户输入:iph -----");
        getSuggestions("iph");

        // 测试2:输入错误词"appel"(预期纠错为"apple")
        System.out.println("\n----- 用户输入:appel -----");
        getSuggestions("appel");

        EsClient.closeClient();
    }
}

执行结果与解析
运行 ProductSuggestServicemain 方法,输出如下:

----- 用户输入:iph -----

===== 补全建议 =====
补全词:iphone 15,文档ID:1
补全词:iPhone15,文档ID:1
补全词:苹果15,文档ID:1

===== 纠错建议 =====
无纠错建议  // 因为"iph"在索引中存在对应前缀,无需纠错


----- 用户输入:appel -----

===== 补全建议 =====
无补全建议  // 因为"appel"不是任何补全词的前缀

===== 纠错建议 =====
纠错词:apple,相似度:0.8,词频:1  // 匹配到"apple watch"中的"apple"

关键代码说明
1.SuggestBuilder 构建
通过 addSuggestion 可添加多个建议器(如同时添加 Completion 和 Term),每个建议器需指定唯一名称(如 product_completion)。

2.Completion 核心参数
prefix:用户输入的前缀(必选)。
fuzziness(Fuzziness.AUTO):允许一定程度的拼写错误(如“ihp”可匹配“iph”)。

3.Term 核心参数
suggestMode(MISSING):仅当输入词在索引中无结果时返回建议,避免干扰正确输入。
sort(FREQUENCY):按词在索引中出现的频率排序,优先返回更常见的词。

4.结果解析
补全结果从 CompletionSuggestion.Option 中获取 text(补全词)和 getHit().getId()(关联文档ID)。
纠错结果从 TermSuggestion.Option 中获取 text(纠错词)、score(相似度)和 freq(词频)。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐