阶段:第二阶段 / 查询能力
ES:
_sourceincludes/excludes | PostgreSQL:SELECT col1, col2而非SELECT *
1. 概念
默认查询会返回整个文档(_source,相当于 SELECT *)。
用 _source 过滤可以只返回需要的字段,减少网络传输和内存开销,尤其字段多、文档大时收益明显。
2. PostgreSQL 对照
sql
-- SELECT *
SELECT * FROM salesdata WHERE material = 'ThinkPad-X1';
-- 只取需要的列
SELECT invoice_number, net_amount FROM salesdata WHERE material = 'ThinkPad-X1';
3. ES DSL
只返回指定字段(includes)
GET salesdata_idx/_search
{
"_source": ["invoice_number", "net_amount"],
"query": { "term": { "material": "ThinkPad-X1" } }
}
排除字段(excludes)
GET salesdata_idx/_search
{
"_source": {
"includes": ["invoice_number", "net_amount", "material"],
"excludes": ["description"]
},
"query": { "match_all": {} }
}
完全不返回 _source(只要命中/计数)
GET salesdata_idx/_search
{
"_source": false,
"query": { "match_all": {} }
}
支持通配:"customer_*" 匹配一组字段。
4. Spring Boot 实现
本项目在 AbstractEsHepler#buildSource 里就做了字段裁剪:
java
// 本项目原样:只 include 指定字段
public void buildSource(boolean t, SearchRequest.Builder builder, EsDataQuery query) {
if (t) {
builder.source(SourceConfig.of(s -> s.filter(
f -> f.includes(Arrays.asList(query.get_source())))));
}
}
独立示例:
java
@Component
public class Doc19SourceFilter {
@Autowired
private ElasticsearchClient elasticsearchClient;
/** 只返回 includes 里的字段 */
public List<Map<String, Object>> selectFields(String indexName, Query query,
List<String> includes) throws IOException {
SearchResponse<Map> resp = elasticsearchClient.search(s -> s
.index(indexName)
.query(query)
.source(sc -> sc.filter(f -> f.includes(includes))),
Map.class);
return resp.hits().hits().stream()
.map(Hit::source).filter(Objects::nonNull)
.collect(Collectors.toList());
}
/** includes + excludes */
public List<Map<String, Object>> selectWithExcludes(String indexName, Query query,
List<String> includes,
List<String> excludes) throws IOException {
SearchResponse<Map> resp = elasticsearchClient.search(s -> s
.index(indexName)
.query(query)
.source(sc -> sc.filter(f -> f.includes(includes).excludes(excludes))),
Map.class);
return resp.hits().hits().stream()
.map(Hit::source).filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
5. 坑与最佳实践
- 别习惯性
SELECT *:只取需要字段能明显降低响应体积和 GC 压力。 _source过滤不等于「不读磁盘」 :ES 仍会读整个_source再裁剪;
要彻底避免读大字段,可在 mapping 里对该字段设"store": true配合stored_fields,或拆索引。- 配合排序/聚合 :聚合场景通常
"_source": false,只要桶结果不要文档体。 - 通配符方便但小心 :
"*_dt"之类通配匹配要确认不会漏带关键字段。
阶段小结
第二阶段(查询)完成,你已掌握 match / term / range / bool / exists / prefix-wildcard-fuzzy /
match_phrase / multi_match / 排序分页 / 字段裁剪,覆盖日常 90% 的检索需求。
下一篇
进入第三阶段聚合:20-metric-指标聚合.md。