网站首页 > java教程 正文
目录
[基础概念](#基础概念)[基础概念](#基础概念)
[字段类型详解](#字段类型详解)[字段类型详解](#字段类型详解)
[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 搜索系统。
猜你喜欢
- 2025-09-24 30个Excel/WPS新公式实战大全,效率翻倍不是梦!
- 2025-09-24 Ngnix的server_name正则匹配执行顺序
- 2025-09-24 学习VBA,报表做到飞 第四章 正则表达式 4.12 正则表达式与数组结合
- 2025-09-24 用正则表达式解代数方程_用正则表达式解代数方程怎么写
- 2025-09-24 Django路由配置方法全解_请简述django的url路由流程
- 2025-09-24 这几种正则表达式的“字符集合”,想要入门regexp函数,必须了解
- 2025-09-24 玩转Spring中强大的spel表达式!_spring的scope有几种
- 2025-09-24 Python中使用正则表达式_python 正则表达
- 2025-09-24 Python 正则表达式教程 第 4 章:贪婪与非贪婪匹配
- 2025-09-24 5分钟掌握Python(十六)之正则表达式
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)