专业的JAVA编程教程与资源

网站首页 > java教程 正文

ES查询总不准?Keyword与分词的抉择

temp10 2025-09-24 03:45:56 java教程 2 ℃ 0 评论

目录

[基础概念](#基础概念)[基础概念](#基础概念)

ES查询总不准?Keyword与分词的抉择

[字段类型详解](#字段类型详解)[字段类型详解](#字段类型详解)

[Keyword 查询场景](#keyword-查询场景)[Keyword 查询场景](#keyword-查询场景)

[分词查询场景](#分词查询场景)[分词查询场景](#分词查询场景)

[查询类型对比](#查询类型对比)[查询类型对比](#查询类型对比)

[实际应用示例](#实际应用示例)[实际应用示例](#实际应用示例)

[性能优化建议](#性能优化建议)[性能优化建议](#性能优化建议)

[常见问题与解决方案](#常见问题与解决方案)[常见问题与解决方案](#常见问题与解决方案)

基础概念

在 Elasticsearch 中,理解何时使用 keyword 查询和何时使用分词查询是构建高效搜索系统的关键。这两种查询方式适用于不同的业务场景,选择正确的查询方式直接影响搜索的准确性和性能。在 Elasticsearch 中,理解何时使用 keyword 查询和何时使用分词查询是构建高效搜索系统的关键。这两种查询方式适用于不同的业务场景,选择正确的查询方式直接影响搜索的准确性和性能。

核心区别

特性

Keyword 查询

分词查询

**数据处理**

不分词,精确匹配

分词处理,模糊匹配

**适用场景**

结构化数据、精确查找

全文搜索、内容检索

**查询性能**

快速,可缓存

相对较慢,需要分析

**存储方式**

倒排索引,完整词条

倒排索引,分词后的词条

字段类型详解

Text 字段类型

用途:用于全文搜索

特点:会被分词器分析,支持模糊匹配

索引方式:分词后建立倒排索引

查询方式:match、match_phrase、multi_match 等

[json]

{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "standard"
}
}
}
}

Keyword 字段类型

用途:用于精确匹配、聚合、排序

特点:不分词,作为整体处理

索引方式:完整字符串建立倒排索引

查询方式:term、terms、wildcard、prefix 等

[json]

{
"mappings": {
"properties": {
"status": {
"type": "keyword"
}
}
}
}

多字段映射(Multi-field)

在实际应用中,经常使用多字段映射,同时支持精确匹配和全文搜索:在实际应用中,经常使用多字段映射,同时支持精确匹配和全文搜索:

[json]

{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}

Keyword 查询场景

1. 精确匹配场景

状态字段查询

[java]

// 查询状态为 "published" 的文档
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(QueryBuilders.termQuery("status", "published"));

适用数据

订单状态:pending, processing, completed, cancelled订单状态:pending, processing, completed, cancelled

用户角色:admin, user, guest用户角色:admin, user, guest

商品分类:electronics, clothing, books商品分类:electronics, clothing, books

ID 和编码查询

[java]

// 查询特定用户ID的文档
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(QueryBuilders.termQuery("user_id", "USER_12345"));
// 查询多个商品编码
BoolQueryBuilder query2 = QueryBuilders.boolQuery()
.filter(QueryBuilders.termsQuery("product_code", "P001", "P002", "P003"));

2. 模糊匹配场景

前缀匹配

[java]

// 查找以 "PROD" 开头的产品编码
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(QueryBuilders.prefixQuery("product_code", "PROD"));

应用场景

邮政编码:查找 "100" 开头的邮编邮政编码:查找 "100" 开头的邮编

电话号码:查找特定区号开头的号码电话号码:查找特定区号开头的号码

商品编码:查找特定前缀的商品商品编码:查找特定前缀的商品

通配符匹配

[java]

// 查找包含特定模式的邮箱
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(
QueryBuilders.wildcardQuery("email", "*@company.com"));

// 查找特定格式的订单号
BoolQueryBuilder query2 = QueryBuilders.boolQuery()
.filter(
QueryBuilders.wildcardQuery("order_no", "ORD-2023-*"));

注意:避免以通配符开头的查询(如 *term),这会导致性能问题。

正则表达式匹配

[java]

// 查找符合特定格式的手机号
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(QueryBuilders.regexpQuery("mobile", "1[3-9]\\d{9}"));

3. 范围查询

[java]

// 查找特定日期范围(keyword 类型的日期)
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(QueryBuilders.rangeQuery("create_date.keyword")
.gte("2023-01-01")
.lte("2023-12-31"));

分词查询场景

1. 全文搜索场景

标题和内容搜索

[java]

// 在文章标题中搜索关键词
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("title", "elasticsearch 教程"));
// 多字段搜索
BoolQueryBuilder query2 = QueryBuilders.boolQuery()
.must(
QueryBuilders.multiMatchQuery("机器学习", "title", "content", "tags"));

适用场景

文章标题搜索文章标题搜索

商品名称搜索商品名称搜索

新闻内容检索新闻内容检索

用户评论搜索用户评论搜索

短语匹配

[java]

// 精确短语匹配
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(
QueryBuilders.matchPhraseQuery("content", "人工智能技术"));

// 带间隔的短语匹配
BoolQueryBuilder query2 = QueryBuilders.boolQuery()
.must(
QueryBuilders.matchPhraseQuery("content", "机器 学习")

.slop(2)); // 允许词之间有最多2个词的间隔

2. 模糊搜索场景

容错搜索

[java]

// 支持拼写错误的搜索
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("title", "elasticsarch") // 拼写错误
.fuzziness(Fuzziness.AUTO)); // 自动容错

最小匹配数量

[java]

// 设置最小匹配词数
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("content", "java spring boot elasticsearch")
.minimumShouldMatch("75%")); // 至少匹配75%的词

3. 高级分词查询

布尔匹配

[java]

// 复杂的布尔查询
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("content", "java"))
.should(QueryBuilders.matchQuery("content", "spring"))
.mustNot(QueryBuilders.matchQuery("content", "python"))
.minimumShouldMatch(1);

查询字符串

[java]

// 支持查询语法的搜索
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(
QueryBuilders.queryStringQuery("java AND (spring OR boot) NOT python")

.defaultField("content"));

查询类型对比

实际对比示例

假设有以下文档:假设有以下文档:

[json]

{
"title": "Elasticsearch 入门教程",
"category": "技术文档",
"status": "published",
"tags": ["elasticsearch", "搜索引擎", "教程"]
}

场景1:分类查询

Keyword 查询(推荐)

[java]

// 精确匹配分类
QueryBuilders.termQuery("category", "技术文档")

精确匹配 精确匹配

性能好 性能好

结果准确 结果准确

Text 查询(不推荐)

[java]

// 分词匹配分类
QueryBuilders.matchQuery("category", "技术文档")

可能匹配到 "技术" 或 "文档" 单独出现的文档 可能匹配到 "技术" 或 "文档" 单独出现的文档

结果不够精确 结果不够精确

场景2:标题搜索

Text 查询(推荐)

[java]

// 全文搜索标题
QueryBuilders.matchQuery("title", "elasticsearch 教程")

支持分词匹配 支持分词匹配

可以匹配部分关键词 可以匹配部分关键词

支持相关性评分 支持相关性评分

Keyword 查询(限制较大)

[java]

// 精确匹配标题
QueryBuilders.termQuery("title.keyword", "Elasticsearch 入门教程")

必须完全匹配 必须完全匹配

大小写敏感 大小写敏感

用户体验差 用户体验差

实际应用示例

示例1:电商商品搜索

[java]

public class ProductSearchService {
public BoolQueryBuilder buildProductQuery(String searchText, String category,
String brand, String status) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 商品名称搜索 - 使用分词查询
if (searchText != null && !searchText.isEmpty()) {
boolQuery.must(
QueryBuilders.multiMatchQuery(searchText,

"name", "description", "features")
.type(
MultiMatchQueryBuilder.Type.BEST_FIELDS)

.fuzziness(Fuzziness.AUTO));
}
// 分类筛选 - 使用 keyword 精确匹配
if (category != null && !category.isEmpty()) {
boolQuery.filter(QueryBuilders.termQuery("category", category));
}
// 品牌筛选 - 使用 keyword 精确匹配
if (brand != null && !brand.isEmpty()) {
boolQuery.filter(QueryBuilders.termQuery("brand", brand));
}
// 状态筛选 - 使用 keyword 精确匹配
if (status != null && !status.isEmpty()) {
boolQuery.filter(QueryBuilders.termQuery("status", status));
}
return boolQuery;
}
}

示例2:用户搜索系统

[java]

public class UserSearchService {
public BoolQueryBuilder buildUserQuery(String keyword, String department,
String role, List<String> skills) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 用户姓名和邮箱搜索 - 组合使用
if (keyword != null && !keyword.isEmpty()) {
BoolQueryBuilder userQuery = QueryBuilders.boolQuery()
// 精确邮箱匹配
.should(
QueryBuilders.wildcardQuery("email", "*" + keyword.toLowerCase() + "*"))

// 姓名模糊匹配
.should(QueryBuilders.matchQuery("full_name", keyword).boost(2.0f))
// 用户名前缀匹配
.should(QueryBuilders.prefixQuery("username", keyword.toLowerCase()))
.minimumShouldMatch(1);
boolQuery.must(userQuery);
}
// 部门筛选 - keyword 精确匹配
if (department != null && !department.isEmpty()) {
boolQuery.filter(QueryBuilders.termQuery("department", department));
}
// 角色筛选 - keyword 精确匹配
if (role != null && !role.isEmpty()) {
boolQuery.filter(QueryBuilders.termQuery("role", role));
}
// 技能匹配 - 分词查询
if (skills != null && !skills.isEmpty()) {
BoolQueryBuilder skillQuery = QueryBuilders.boolQuery();
for (String skill : skills) {
skillQuery.should(QueryBuilders.matchQuery("skills", skill));
}

skillQuery.minimumShouldMatch(1);

boolQuery.must(skillQuery);
}
return boolQuery;
}
}

示例3:日志搜索系统

[java]

public class LogSearchService {
public BoolQueryBuilder buildLogQuery(String message, String level,
String service, String startTime,
String endTime) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 日志消息搜索 - 分词查询
if (message != null && !message.isEmpty()) {
boolQuery.must(QueryBuilders.matchQuery("message", message)
.operator(Operator.AND) // 所有词都必须出现
.minimumShouldMatch("75%"));
}
// 日志级别 - keyword 精确匹配
if (level != null && !level.isEmpty()) {
boolQuery.filter(QueryBuilders.termQuery("level", level.toUpperCase()));
}
// 服务名称 - keyword 精确匹配或前缀匹配
if (service != null && !service.isEmpty()) {
if (service.endsWith("*")) {
boolQuery.filter(QueryBuilders.prefixQuery("service",
service.substring(0, service.length() - 1)));
} else {
boolQuery.filter(QueryBuilders.termQuery("service", service));
}
}
// 时间范围 - 范围查询
if (startTime != null || endTime != null) {
RangeQueryBuilder timeRange = QueryBuilders.rangeQuery("timestamp");
if (startTime != null) timeRange.gte(startTime);
if (endTime != null) timeRange.lte(endTime);
boolQuery.filter(timeRange);
}
return boolQuery;
}
}

性能优化建议

1. 选择合适的查询类型

优先使用 Filter 查询

[java]

// 推荐:使用 filter,不计算评分
boolQuery.filter(QueryBuilders.termQuery("status", "active"));
// 不推荐:使用 must,会计算评分
boolQuery.must(QueryBuilders.termQuery("status", "active"));

合理使用通配符查询

[java]

// 推荐:后缀通配符

QueryBuilders.wildcardQuery("product_code", "PROD*");

// 不推荐:前缀通配符(性能差)

QueryBuilders.wildcardQuery("product_code", "*123");

// 更好的选择:使用 suffix 或 reverse 分析器

2. 索引优化策略

合理设计字段映射

[json]

{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
},
"suggest": {
"type": "completion"
}
}
},
"category": {
"type": "keyword"
},
"content": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}

使用合适的分析器

[java]

// 中文分词器配置
Settings settings = Settings.builder()
.put("
analysis.analyzer.ik_smart.type", "ik_smart")

.put("
analysis.analyzer.ik_max_word.type", "ik_max_word")

.build();

3. 查询性能优化

缓存策略

[java]

// 使用 filter 查询,自动缓存
boolQuery.filter(QueryBuilders.termQuery("category", category));
// 对于频繁查询的条件,考虑使用 constant_score

QueryBuilders.constantScoreQuery(

QueryBuilders.termQuery("status", "published")
).boost(1.0f);

分页优化

[java]

// 深度分页优化:使用 search_after
SearchSourceBuilder searchSource = new SearchSourceBuilder()
.query(boolQuery)
.size(20)
.sort("timestamp", SortOrder.DESC)
.sort("_id", SortOrder.ASC);
// 如果有上一页的最后一个文档的 sort 值
if (lastSortValues != null) {
searchSource.searchAfter(lastSortValues);
}

常见问题与解决方案

Q1: 什么时候使用 match 而不是 term?

A:

使用 match:需要分词处理的文本搜索(标题、内容、描述等)

使用 term:精确匹配的结构化数据(状态、ID、分类等)

[java]

// 文本搜索 - 使用 match
QueryBuilders.matchQuery("title", "elasticsearch 教程");
// 精确匹配 - 使用 term
QueryBuilders.termQuery("status", "published");

Q2: 为什么 term 查询没有结果?

A: 常见原因:

字段被分词了,应该使用 field.keyword

大小写不匹配大小写不匹配

包含特殊字符包含特殊字符

[java]

// 错误:text 字段使用 term 查询
QueryBuilders.termQuery("title", "Elasticsearch Tutorial");
// 正确:使用 keyword 子字段
QueryBuilders.termQuery("title.keyword", "Elasticsearch Tutorial");
// 或者使用 match 查询
QueryBuilders.matchQuery("title", "Elasticsearch Tutorial");

Q3: 如何实现大小写不敏感的精确匹配?

A: 几种方案:

[java]

// 方案1:使用 normalizer
{
"mappings": {
"properties": {
"category": {
"type": "keyword",
"normalizer": "lowercase_normalizer"
}
}
},
"settings": {
"analysis": {
"normalizer": {
"lowercase_normalizer": {
"type": "custom",
"char_filter": [],
"filter": ["lowercase"]
}
}
}
}
}
// 方案2:查询时转换大小写
QueryBuilders.termQuery("category", category.toLowerCase());
// 方案3:使用 match 查询
QueryBuilders.matchQuery("category", category);

Q4: 如何优化通配符查询性能?

A:

[java]

// 1. 避免前缀通配符
// 不推荐

QueryBuilders.wildcardQuery("field", "*suffix");

// 推荐:使用 reverse 分析器 + 前缀查询
// 2. 限制通配符查询的范围
BoolQueryBuilder query = QueryBuilders.boolQuery()
.filter(QueryBuilders.termQuery("category", "electronics"))
.must(
QueryBuilders.wildcardQuery("product_code", "ELEC*"));

// 3. 使用 prefix 查询替代简单的后缀通配符
QueryBuilders.prefixQuery("product_code", "PROD");

Q5: 如何处理中文搜索?

A:

[java]

// 1. 使用中文分词器(如 IK)
QueryBuilders.matchQuery("content", "机器学习算法")
.analyzer("ik_smart");
// 2. 组合查询提高准确性
BoolQueryBuilder query = QueryBuilders.boolQuery()
.should(QueryBuilders.matchQuery("title", keyword).boost(3.0f))
.should(QueryBuilders.matchQuery("content", keyword))
.should(
QueryBuilders.matchPhraseQuery("content", keyword).boost(2.0f))

.minimumShouldMatch(1);
// 3. 使用拼音搜索插件

QueryBuilders.multiMatchQuery("jiqixuexi", "title.pinyin", "content.pinyin");

总结

选择正确的查询方式是构建高效搜索系统的关键:选择正确的查询方式是构建高效搜索系统的关键:

Keyword 查询适用场景:

精确匹配:状态、ID、分类 精确匹配:状态、ID、分类

结构化数据:枚举值、编码 结构化数据:枚举值、编码

聚合分析:分组、统计 聚合分析:分组、统计

排序字段:需要精确排序的字段 排序字段:需要精确排序的字段

分词查询适用场景:

全文搜索:标题、内容、描述 全文搜索:标题、内容、描述

模糊匹配:用户输入容错 模糊匹配:用户输入容错

相关性排序:需要评分的搜索 相关性排序:需要评分的搜索

多语言支持:需要分词处理的文本 多语言支持:需要分词处理的文本

最佳实践:

合理设计映射:使用多字段映射同时支持精确匹配和全文搜索

选择合适的分析器:根据语言和业务需求选择分词器

优化查询性能:优先使用 filter 查询,合理使用缓存

监控查询效果:定期分析查询性能和结果质量

通过理解这些概念和最佳实践,您可以构建出既高效又准确的 Elasticsearch 搜索系统。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表