RestHighLevelClient 7.10.0
RestHighLevelClient 是全局单例类,核心参数决定连接稳定性和性能
一、客户端初始化及参数
| 参数名 |
含义 |
默认值 |
生产建议值 |
注意事项 |
| connectTimeout |
连接 ES 节点的超时时间(TCP 握手阶段) |
1000ms |
3000ms |
过小易因网络抖动失败,过大导致连接等待时间过长 |
| socketTimeout |
数据传输超时时间(请求发送后等待响应的时间) |
30000ms |
60000ms |
复杂查询 / 大批量数据需调大(如 Bulk 批量写入、深度聚合) |
| connectionRequestTimeout |
从连接池获取连接的超时时间 |
500ms |
5000ms |
连接池满时,超过该时间会抛连接超时异常 |
| maxConnTotal |
客户端最大连接数(所有 ES 节点共享) |
20 |
100-200 |
按 QPS 调整,QPS 1000+ 建议 200 |
| maxConnPerRoute |
单个 ES 节点的最大连接数 |
10 |
50-100 |
避免单个节点连接数过多导致服务端限流 |
《初始化 示例Demo》
java
复制代码
@Bean
public RestHighLevelClient esRestClient(){
//创建RestHighLevelClient客户端
RestClientBuilder builder = RestClient.builder(new HttpHost(hostname, port, scheme));
// 认证配置(无密码则跳过)
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password)); //es账号密码
// 连接池配置
builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
if (maxConnectNum != null && maxConnectNumPerRoute != null) {
httpClientBuilder.setMaxConnTotal(maxConnectNum); // 最大总连接数
httpClientBuilder.setMaxConnPerRoute(maxConnectNumPerRoute); // 单节点最大连接数
}
httpClientBuilder.disableAuthCaching();
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
});
// 请求配置(超时参数)
builder.setRequestConfigCallback(requestConfigBuilder -> {
if (connectTimeout != null && socketTimeout != null && connectionRequestTimeout != null) {
requestConfigBuilder.setConnectTimeout(connectTimeout); // 连接超时
requestConfigBuilder.setSocketTimeout(socketTimeout); // 传输超时
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout); // 连接池获取超时
}
return requestConfigBuilder;
});
return new RestHighLevelClient(builder);
}
二、索引操作核心类
1. CreateIndexRequest(创建索引)
核心作用
创建 ES 索引,支持配置分片、副本、Mapping、索引级参数(如刷新间隔)。
关键参数 / 方法
| 方法 / 参数 |
含义 |
默认值 |
生产建议 |
settings(Settings) |
设置索引级别参数(分片、副本、刷新间隔) |
- |
必配:分片数 3-8,副本数 1-2 |
mapping(XContentBuilder) |
配置索引 Mapping(字段类型、分词器) |
动态映射 |
禁用动态映射,手动配置所有字段 |
alias(String) |
为索引设置别名 |
- |
按时间分索引时必用(如 order_info_202602 → alias: order_info) |
settings的相关参数
-
number_of_shards :主分片数(生产 3-8)
-
number_of_replicas :副本数(生产 1-2)
-
refresh_interval :刷新间隔(写入密集调大)
-
index.query.bool.max_clause_count :布尔查询最大子句数
-
index.mapping.total_fields.limit :最大字段数限制
mapping的参数
XContentBuilder 是 Elasticsearch Java 客户端中构建结构化 JSON 数据的核心工具,主要用于:
- 构建 ES 文档内容(新增 / 更新文档时的 JSON 数据);
- 构建索引 Mapping 配置(定义字段类型、分词器、格式等);
- 构建索引设置(分片、副本、刷新间隔等);
- 替代手动拼接 JSON 字符串(避免语法错误、JSON 注入、格式不规范等问题)
XContentBuilder核心特性
- 流式 API:支持链式调用,代码简洁且易维护;
- 类型安全:自动处理 JSON 格式(如引号、逗号、花括号配对),避免手动拼接的语法错误;
- 多格式支持:默认构建 JSON 格式,也支持 Smile(二进制 JSON)、YAML 等格式(生产仅用 JSON);
- UTF-8 编码:默认采用 UTF-8 编码,兼容中文等多语言。
初始化方式
| 方法 |
含义 |
适用场景 |
| XContentFactory.jsonBuilder() |
创建 JSON 格式的 XContentBuilder |
核心方法(推荐使用) |
XContentFactory.smileBuilder() |
二进制 JSON 格式 |
高性能二进制传输(极少用) |
XContentFactory.yamlBuilder() |
YAML 格式 |
配置文件解析(非 ES 交互) |
XContentFactory.cborBuilder() |
CBOR 二进制格式 |
小众场景 |
方法
| 方法 |
含义 |
参数说明 |
使用场景 |
startObject() |
开启一个空的 JSON 对象({}) |
无 |
根对象 / 嵌套对象的起始 |
startObject(String fieldName) |
开启指定字段名的嵌套 JSON 对象(如 "order_items": {}) |
fieldName:嵌套对象字段名 |
文档中的嵌套对象(如订单项) |
endObject() |
关闭当前 JSON 对象 |
无 |
与 startObject() 成对关闭 |
startArray(String fieldName) |
开启指定字段名的 JSON 数组(如 "order_items": []) |
fieldName:数组字段名 |
文档中的数组(如多订单项、多标签) |
endArray() |
关闭当前 JSON 数组 |
无 |
与 startArray() 成对关闭 |
《XContentBuilder实例Demo》
java
复制代码
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject() // 根对象开始
.startObject("user_info") // 嵌套对象开始
.field("name", "张三")
.field("age", 25)
.endObject() // 嵌套对象结束
.startArray("tags") // 数组开始
.value("VIP") // 数组元素
.value("新用户")
.endArray() // 数组结束
.endObject(); // 根对象结束
field中常用的参数
| 参数名 |
含义 |
适用字段类型 |
取值范围 |
默认值 |
type |
字段类型 |
所有字段 |
keyword/text/date/double/nested 等 |
- |
analyzer |
索引时分词器 |
text 字段 |
standard:ES 默认分词器,按空格 / 标点拆分,英文小写化,中文单字拆分 |
默认值 |
analyzer |
索引时分词器 |
text 字段 |
ik_max_word:IK 分词器 - 细粒度拆分(如 "华为 Mate60" 拆为 "华为、Mate60、Mate、60") |
|
analyzer |
索引时分词器 |
text 字段 |
ik_smart:IK 分词器 - 粗粒度拆分(如 "华为 Mate60" 拆为 "华为、Mate60") |
|
analyzer |
索引时分词器 |
text 字段 |
whitespace :仅按空格拆分,不小写化,不拆分标点 |
|
analyzer |
索引时分词器 |
text 字段 |
keyword :不分词(整个字段作为一个词条) |
|
analyzer |
索引时分词器 |
text 字段 |
pattern :按正则表达式拆分 |
|
| search_analyzer |
搜索时分词器 |
|
与 analyzer 取值相同 |
|
format |
日期格式 |
date 字段 |
yyyy-MM-dd HH:mm:ss/epoch_millis 等 |
epoch_millis |
ignore_above |
超过长度不索引 |
keyword 字段 |
1-32766 |
2147483647 |
dynamic |
是否开启动态映射 |
根对象 /properties |
true/false/strict |
true |
fields |
多字段配置(如 text+keyword) |
text/keyword 字段 |
- |
- |
null_value |
空值替代值 |
keyword 字段 |
任意字符串 |
null |
enabled |
是否启用字段(禁用后不索引 / 存储) |
所有字段 |
true/false |
true |
《创建索引示例Demo》
java
复制代码
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.client.RestHighLevelClient;
import java.io.IOException;
/**
* CreateIndexRequest 完整 Demo
*/
public class IndexOperationDemo {
private final RestHighLevelClient client;
// 索引名(按时间分索引)
private static final String INDEX_NAME = "order_info_202602";
// 索引别名
private static final String INDEX_ALIAS = "order_info";
public IndexOperationDemo(RestHighLevelClient client) {
this.client = client;
}
public void createOrderIndex() throws IOException {
// 1. 检查索引是否已存在
if (client.indices().exists(new GetIndexRequest(INDEX_NAME), RequestOptions.DEFAULT)) {
System.out.println("索引已存在:" + INDEX_NAME);
return;
}
// 2. 构建创建索引请求
CreateIndexRequest request = new CreateIndexRequest(INDEX_NAME);
// 3. 设置索引级别参数(核心)
request.settings(Settings.builder()
.put("number_of_shards", 3) // 主分片数(生产 3-8)
.put("number_of_replicas", 1) // 副本数(生产 1-2)
.put("refresh_interval", "5s") // 刷新间隔(写入密集调大)
.put("index.query.bool.max_clause_count", 2048) // 布尔查询最大子句数
.put("index.mapping.total_fields.limit", 1000)); // 最大字段数限制
// 4. 配置 Mapping(禁用动态映射,手动配置所有字段)
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()
.field("dynamic", false) // 禁用动态映射(核心!避免字段类型混乱)
.startObject("properties")
// 订单号:精准匹配,不分词
.startObject("order_id")
.field("type", "keyword")
.endObject()
// 用户ID:精准匹配
.startObject("user_id")
.field("type", "keyword")
.endObject()
// 订单金额:数值类型(支持范围查询)
.startObject("amount")
.field("type", "double")
.endObject()
// 创建时间:日期类型(支持范围/排序)
.startObject("create_time")
.field("type", "date")
.field("format", "yyyy-MM-dd HH:mm:ss||epoch_millis")
.endObject()
// 订单备注:分词+精准双字段
.startObject("order_remark")
.field("type", "text")
.field("analyzer", "ik_max_word") // 中文分词
.startObject("fields")
.startObject("keyword") // 精准匹配子字段
.field("type", "keyword")
.field("ignore_above", 256) // 超过256字符不索引
.endObject()
.endObject()
.endObject()
// 订单项:嵌套类型(1:N 关联)
.startObject("order_items")
.field("type", "nested") // 嵌套类型(查询需用 nested 查询)
.startObject("properties")
.startObject("sku_id")
.field("type", "keyword")
.endObject()
.startObject("sku_name")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.endObject()
.endObject()
.endObject()
.endObject();
request.mapping(mapping);
// 5. 设置索引别名(屏蔽分索引细节)
request.alias(INDEX_ALIAS);
// 6. 执行创建
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
if (response.isAcknowledged()) {
System.out.println("索引创建成功:" + INDEX_NAME + ",别名:" + INDEX_ALIAS);
} else {
System.out.println("索引创建失败:" + INDEX_NAME);
}
}
}
2. DeleteIndexRequest(删除索引)
核心作用
删除指定索引,支持批量删除(多个索引用逗号分隔)。
关键参数
| 方法 |
含义 |
注意事项 |
DeleteIndexRequest(String) |
指定要删除的索引名 |
支持通配符(如 order_info_*),慎用! |
《删除索引示例Demo》
java
复制代码
/**
* DeleteIndexRequest Demo
*/
public void deleteIndex(String indexName) throws IOException {
// 1. 检查索引是否存在
if (!client.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT)) {
System.out.println("索引不存在:" + indexName);
return;
}
// 2. 构建删除请求
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
// 3. 执行删除
client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println("索引删除成功:" + indexName);
}
三、文档操作核心类(Document)
1. IndexRequest(新增单条文档)
核心作用
新增 / 更新单条文档(指定 ID 时,存在则更新,不存在则新增)。
关键参数 / 方法
| 方法 / 参数 |
含义 |
默认值 |
生产建议 |
id(String) |
文档 ID(业务唯一键,如订单号) |
自动生成 |
必配!保证幂等性,避免重复数据 |
routing(String) |
路由键(将文档路由到指定分片) |
- |
按用户 ID / 商户 ID 路由,提升查询性能 |
refreshPolicy(String) |
刷新策略 |
"wait_for" |
批量写入设为 "false",减少 IO |
source(XContentBuilder) |
文档内容 |
- |
仅传入查询所需字段,减少存储 |
刷新策略说明
| 策略值 |
含义 |
适用场景 |
| "immediate" |
立即刷新(同步) |
实时性要求极高的场景(如秒杀) |
| "wait_for" |
等待下一次刷新(默认) |
常规场景 |
| "false" |
不立即刷新,由索引 refresh_interval 控制 |
批量写入、非实时场景 |
《新增文档示例Demo》
java
复制代码
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
/**
* IndexRequest Demo(新增单条文档)
*/
public void addSingleDoc() throws IOException {
String docId = "ORDER20260212001"; // 文档ID=订单号
String userId = "10086"; // 路由键=用户ID
// 1. 构建文档内容
XContentBuilder doc = XContentFactory.jsonBuilder()
.startObject()
.field("order_id", docId)
.field("user_id", userId)
.field("amount", 199.99)
.field("create_time", "2026-02-12 10:00:00")
.field("order_remark", "加急发货")
// 嵌套对象
.startArray("order_items")
.startObject()
.field("sku_id", "SKU1001")
.field("sku_name", "华为Mate60")
.field("price", 199.99)
.field("quantity", 1)
.endObject()
.endArray()
.endObject();
// 2. 构建新增请求
IndexRequest request = new IndexRequest(INDEX_NAME)
.id(docId) // 文档ID(业务唯一键)
.routing(userId) // 路由到用户ID分片
.refreshPolicy("false") // 不立即刷新
.source(doc);
// 3. 执行新增
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
// 结果枚举:CREATED(新增)、UPDATED(更新)
System.out.println("文档新增结果:" + response.getResult());
System.out.println("文档版本号:" + response.getVersion()); // ES 版本号(乐观锁)
}
2. BulkRequest(批量文档操作)
核心作用
批量执行新增 / 更新 / 删除文档操作,减少网络请求,提升写入性能。
| 方法 / 参数 |
含义 |
默认值 |
生产建议 |
add(IndexRequest/UpdateRequest/DeleteRequest) |
添加单条操作请求 |
- |
每批 500-1000 条(最优性能) |
setRefreshPolicy(String) |
刷新策略 |
"wait_for" |
设为 "false",减少 IO |
timeout(TimeValue) |
批量操作超时时间 |
1m |
大批量(>1000 条)设为 5m |
setPipeline(String) |
指定数据处理管道(如脱敏、格式转换) |
- |
敏感数据写入前脱敏 |
《批量新增文档示例Demo》
java
复制代码
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import java.util.ArrayList;
import java.util.List;
/**
* BulkRequest Demo(批量新增)
*/
public void batchAddDocs() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
// 批量大小建议:500-1000 条
int batchSize = 500;
// 模拟批量数据
List<OrderDoc> orderList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
orderList.add(new OrderDoc(
"ORDER20260212" + String.format("%03d", i + 2),
"10086" + i,
99.99 + i,
"2026-02-12 10:" + String.format("%02d", i) + ":00",
"普通发货"
));
}
// 1. 组装批量请求
for (OrderDoc order : orderList) {
XContentBuilder doc = XContentFactory.jsonBuilder()
.startObject()
.field("order_id", order.getOrderId())
.field("user_id", order.getUserId())
.field("amount", order.getAmount())
.field("create_time", order.getCreateTime())
.field("order_remark", order.getRemark())
.endObject();
// 添加单条新增请求
bulkRequest.add(new IndexRequest(INDEX_NAME)
.id(order.getOrderId())
.routing(order.getUserId())
.source(doc));
}
// 2. 批量配置
bulkRequest.setRefreshPolicy("false") // 不立即刷新
.timeout(TimeValue.timeValueMinutes(5)); // 超时时间 5 分钟
// 3. 执行批量操作
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
// 4. 结果解析
if (response.hasFailures()) {
// 打印失败详情
System.out.println("批量操作失败:" + response.buildFailureMessage());
// 遍历失败项
for (var item : response.getItems()) {
if (item.isFailed()) {
System.out.println("失败文档ID:" + item.getId() + ",原因:" + item.getFailureReason());
}
}
} else {
System.out.println("批量操作成功,条数:" + response.getItems().length);
System.out.println("总耗时:" + response.getTook().getMillis() + "ms");
}
}
// 订单文档实体
static class OrderDoc {
private String orderId;
private String userId;
private Double amount;
private String createTime;
private String remark;
// 构造器、getter/setter 省略
public OrderDoc(String orderId, String userId, Double amount, String createTime, String remark) {
this.orderId = orderId;
this.userId = userId;
this.amount = amount;
this.createTime = createTime;
this.remark = remark;
}
// getter/setter...
}
3. GetRequest(按 ID 查询文档)
核心作用
按文档 ID 精准查询单条文档。
关键参数 / 方法
| 方法 / 参数 |
含义 |
注意事项 |
fetchSourceContext(FetchSourceContext) |
指定返回字段 / 排除字段 |
仅返回所需字段,减少网络传输 |
routing(String) |
指定路由键 |
必须与写入时的路由键一致,否则查不到 |
version(boolean) |
是否返回文档版本号 |
乐观锁场景需开启 |
java
复制代码
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
/**
* GetRequest Demo(按ID查询)
*/
public void getDocById(String docId, String userId) throws IOException {
// 1. 构建查询请求
GetRequest request = new GetRequest(INDEX_NAME, docId)
.routing(userId) // 必须与写入时的路由键一致
// 指定返回字段(仅返回订单号、金额、创建时间)
.fetchSourceContext(new FetchSourceContext(
true, // 开启字段过滤
new String[]{"order_id", "amount", "create_time"}, // 包含字段
null // 排除字段
));
// 2. 执行查询
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3. 结果解析
if (response.isExists()) {
System.out.println("文档版本号:" + response.getVersion());
// 文档内容转为 Map
var source = response.getSourceAsMap();
System.out.println("订单ID:" + source.get("order_id"));
System.out.println("金额:" + source.get("amount"));
System.out.println("创建时间:" + source.get("create_time"));
} else {
System.out.println("文档不存在:" + docId);
}
}
四、查询核心类(Search)
1. SearchRequest + SearchSourceBuilder(搜索请求)
核心作用
SearchRequest 封装搜索的索引范围、路由等元信息;SearchSourceBuilder 封装查询条件、分页、排序、返回字段等核心查询逻辑。
SearchRequest 关键参数
| 方法 / 参数 |
含义 |
注意事项 |
indices(String...) |
指定要查询的索引(支持通配符,如 order_info_2026*) |
按时间分索引时用通配符 |
routing(String) |
指定路由键(仅查询该分片) |
提升查询性能,减少分片遍历 |
preference(String) |
查询偏好(如优先查副本) |
"_replica":优先查副本,读写分离 |
source(SearchSourceBuilder) |
设置查询核心逻辑 |
必配!所有查询条件都在这里 |
SearchSourceBuilder 关键参数 / 方法
| 方法 / 参数 |
含义 |
默认值 |
生产建议 |
query(QueryBuilder) |
设置查询条件(BoolQuery/TermQuery 等) |
match_all |
必配!避免全量查询 |
from(int) |
分页起始位置 |
0 |
深度分页禁用(>10000 性能极差) |
size(int) |
每页条数 |
10 |
最大 10000(ES 默认限制) |
searchAfter(Object[]) |
游标分页(替代 from+size) |
- |
深度分页必用 |
sort(String, SortOrder) |
排序字段 + 顺序 |
- |
searchAfter 必须配唯一排序字段 |
fetchSource(String[], String[]) |
指定返回字段 / 排除字段 |
返回所有 |
仅返回所需字段,减少传输 |
trackTotalHits(boolean) |
是否返回总命中数 |
true |
无需总条数时设为 false,提升性能 |
2. BoolQueryBuilder(布尔查询核心)
核心作用
组合多个子查询条件,实现复杂的多条件过滤 / 匹配。
关键方法(子句类型)
| 方法 |
含义 |
评分 / 性能 |
适用场景 |
must(QueryBuilder) |
必须满足的条件(参与相关性评分) |
参与评分 |
核心查询条件(如关键词匹配) |
filter(QueryBuilder) |
必须满足的条件(不参与评分,ES 缓存结果) |
不评分 |
状态、时间、金额范围等过滤条件 |
should(QueryBuilder) |
或条件(满足其一即可) |
参与评分 |
多关键词模糊匹配 |
mustNot(QueryBuilder) |
必须不满足的条件 |
不评分 |
排除指定数据(如已删除订单) |
常见子查询 Builder
| 查询类型 |
作用 |
示例代码 |
| TermQueryBuilder |
精准匹配(不分词) |
QueryBuilders.termQuery("user_id", "10086") |
| MatchQueryBuilder |
分词模糊匹配 |
QueryBuilders.matchQuery("order_remark", "加急") |
| RangeQueryBuilder |
范围查询(时间 / 金额) |
QueryBuilders.rangeQuery("amount").gte(100).lte(1000) |
| WildcardQueryBuilder |
通配符查询(慎用) |
QueryBuilders.wildcardQuery("order_id", "ORDER2026*") |
| NestedQueryBuilder |
嵌套对象查询 |
QueryBuilders.nestedQuery("order_items", QueryBuilders.termQuery("order_items.sku_id", "SKU1001"), ScoreMode.None) |
《复杂布尔查询 + 游标分页示例Demo》
java
复制代码
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
/**
* 布尔查询 + 游标分页 Demo
*/
public void boolQueryWithSearchAfter() throws IOException {
// 1. 构建布尔查询条件
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 必须满足:用户ID=10086(精准匹配)
boolQuery.must(QueryBuilders.termQuery("user_id", "10086"));
// 必须满足:备注含"加急"(分词匹配)
boolQuery.must(QueryBuilders.matchQuery("order_remark", "加急"));
// 过滤:金额 100-1000 元(不评分,性能高)
boolQuery.filter(QueryBuilders.rangeQuery("amount")
.gte(100)
.lte(1000)
.format("yyyy-MM-dd HH:mm:ss"));
// 过滤:创建时间在 2026-02-01 至 2026-02-12
boolQuery.filter(QueryBuilders.rangeQuery("create_time")
.gte("2026-02-01 00:00:00")
.lte("2026-02-12 23:59:59"));
// 必须不满足:订单状态=已取消(2)
boolQuery.mustNot(QueryBuilders.termQuery("order_status", 2));
// 2. 构建 SearchSourceBuilder(查询核心)
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
.query(boolQuery)
.size(20) // 每页 20 条
// 排序:创建时间降序 + 订单号降序(保证唯一性)
.sort("create_time", SortOrder.DESC)
.sort("order_id", SortOrder.DESC)
// 仅返回指定字段
.fetchSource(
new String[]{"order_id", "amount", "create_time", "order_remark"},
null
)
.trackTotalHits(true); // 返回总命中数
// 3. 构建 SearchRequest
SearchRequest searchRequest = new SearchRequest(INDEX_ALIAS) // 查询别名(屏蔽分索引)
.source(sourceBuilder)
.routing("10086") // 仅查询该用户分片
.preference("_replica"); // 优先查副本(读写分离)
// 4. 执行首次查询(第一页)
SearchResponse firstPageResponse = client.search(searchRequest, RequestOptions.DEFAULT);
parseSearchResponse(firstPageResponse);
// 5. 游标分页(第二页)
SearchHit[] hits = firstPageResponse.getHits().getHits();
if (hits.length > 0) {
// 获取最后一条文档的排序值(游标)
Object[] searchAfterValues = hits[hits.length - 1].getSortValues();
// 构建第二页查询
SearchSourceBuilder secondPageBuilder = new SearchSourceBuilder()
.query(boolQuery)
.size(20)
.sort("create_time", SortOrder.DESC)
.sort("order_id", SortOrder.DESC)
.searchAfter(searchAfterValues) // 游标分页
.from(0); // 必须设为 0
// 执行第二页查询
SearchResponse secondPageResponse = client.search(
new SearchRequest(INDEX_ALIAS).source(secondPageBuilder),
RequestOptions.DEFAULT
);
System.out.println("===== 第二页结果 =====");
parseSearchResponse(secondPageResponse);
}
}
/**
* 通用 SearchResponse 解析方法
*/
private void parseSearchResponse(SearchResponse response) {
SearchHits hits = response.getHits();
// 总命中数
long total = hits.getTotalHits().value;
// 最大相关性评分
float maxScore = hits.getMaxScore();
System.out.println("总命中数:" + total + ",最大评分:" + maxScore);
System.out.println("查询耗时:" + response.getTook().getMillis() + "ms");
// 遍历命中文档
for (SearchHit hit : hits) {
String docId = hit.getId(); // 文档ID
float score = hit.getScore(); // 相关性评分(filter 子句为 0)
var source = hit.getSourceAsMap(); // 文档内容
System.out.println(String.format(
"文档ID:%s,评分:%.2f,订单ID:%s,金额:%s,创建时间:%s",
docId, score,
source.get("order_id"),
source.get("amount"),
source.get("create_time")
));
}
}
五、核心响应类解析(Response)
1. SearchResponse(搜索响应)
核心字段 / 方法
| 方法 / 字段 |
含义 |
示例值 |
getHits() |
获取查询结果集 |
SearchHits 对象 |
getTook() |
查询耗时 |
50ms |
isTimedOut() |
是否超时 |
false |
getShards() |
分片执行情况(成功 / 失败数) |
SuccessfulShards=3, FailedShards=0 |
2. SearchHits(结果集)
| 方法 / 字段 |
含义 |
示例值 |
getTotalHits().value |
总命中数 |
1000 |
getMaxScore() |
最大相关性评分 |
1.23 |
getHits() |
命中文档数组 |
SearchHit[] |
3. SearchHit(单条文档)
| 方法 / 字段 |
含义 |
示例值 |
getId() |
文档 ID |
ORDER20260212001 |
getScore() |
相关性评分 |
1.0 |
getSourceAsMap() |
文档内容(Map 格式) |
{order_id: "ORDER20260212001", ...} |
getSortValues() |
排序字段值(searchAfter 用) |
[1740000000000, "ORDER20260212001"] |
作者:筱白爱学习!!
欢迎关注转发评论点赞沟通,您的支持是筱白的动力!