阶段:第一阶段 / 核心概念
目标:把官方 Java 客户端里最常用的查询类 逐个用例子写出来,
拿来即用。第 05 篇讲的是「类之间的关系与拼接思路」,本篇是「一类一例」的速查手册。
本篇是独立文档 ,示例不依赖任何具体项目。
约定:所有查询返回值统一用实体类
OrderDoc接收,不用Map。
0. 统一约定(贯穿全篇)
0.1 返回实体类 OrderDoc
java
@Data
public class OrderDoc {
private String id;
private String region;
private String status;
private String channel;
private BigDecimal amount;
private String materialDesc; // 见下方 @JsonProperty 映射
private LocalDate invoiceDt;
// ES 字段名是下划线,Java 是驼峰,用 @JsonProperty 映射
@JsonProperty("material_desc")
public String getMaterialDesc() { return materialDesc; }
@JsonProperty("material_desc")
public void setMaterialDesc(String v) { this.materialDesc = v; }
@JsonProperty("invoice_dt")
public LocalDate getInvoiceDt() { return invoiceDt; }
@JsonProperty("invoice_dt")
public void setInvoiceDt(LocalDate v) { this.invoiceDt = v; }
}
0.2 通用执行方法(每个例子都调它)
传入一个 Query,统一用 OrderDoc.class 反序列化结果并返回实体列表:
java
@Slf4j
@Component
public class OrderSearcher {
@Autowired
private ElasticsearchClient client;
private static final String INDEX = "orders_idx";
/** 执行查询,返回实体列表(不返回 Map) */
public List<OrderDoc> search(Query query, int size) throws IOException {
SearchResponse<OrderDoc> resp = client.search(s -> s
.index(INDEX)
.query(query)
.size(size), OrderDoc.class); // ← 统一用实体类
return resp.hits().hits().stream()
.map(Hit::source)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
下面每个例子都只展示怎么构造
Query,然后searcher.search(query, 50)即可拿到List<OrderDoc>。
1. TermQuery ------ 单值精确匹配(col = ?)
不分词的精确匹配,用于 keyword/数值/布尔等字段。
java
// WHERE region = 'AP'
Query query = Query.of(q -> q.term(t -> t
.field("region")
.value("AP")));
List<OrderDoc> list = searcher.search(query, 50);
坑:不要对
text字段用term(被分词,通常查不到);精确匹配请用keyword字段。
2. TermsQuery ------ 多值精确匹配(col IN (...))
java
// WHERE status IN ('PAID', 'SHIPPED')
List<FieldValue> values = Stream.of("PAID", "SHIPPED")
.map(FieldValue::of)
.collect(Collectors.toList());
Query query = Query.of(q -> q.terms(t -> t
.field("status")
.terms(tv -> tv.value(values))));
List<OrderDoc> list = searcher.search(query, 50);
关键:
terms需要List<FieldValue>,字符串要先用FieldValue::of转换。
3. RangeQuery ------ 范围(BETWEEN / >= / <=)
数值范围:
java
// WHERE amount >= 1000 AND amount <= 5000
Query query = Query.of(q -> q.range(r -> r
.field("amount")
.gte(JsonData.of(1000))
.lte(JsonData.of(5000))));
List<OrderDoc> list = searcher.search(query, 50);
日期范围:
java
// WHERE invoice_dt BETWEEN '2026-01-01' AND '2026-03-31'
Query query = Query.of(q -> q.range(r -> r
.field("invoice_dt")
.gte(JsonData.of("2026-01-01"))
.lte(JsonData.of("2026-03-31"))));
关键:范围值要用
JsonData.of(...)包装;gt/gte/lt/lte分别对应> >= < <=。
4. MatchQuery ------ 全文匹配(分词,text 字段)
对分词字段做全文搜索,命中带相关性打分。
java
// 搜 material_desc 里包含 thinkpad / x1 的(会分词)
Query query = Query.of(q -> q.match(m -> m
.field("material_desc")
.query("thinkpad x1")));
List<OrderDoc> list = searcher.search(query, 50);
默认多个词是 OR。要求「都命中」用 operator(AND):
java
Query query = Query.of(q -> q.match(m -> m
.field("material_desc")
.query("thinkpad x1")
.operator(Operator.And))); // 所有词都要命中
5. MatchPhraseQuery ------ 短语匹配(保持词序)
要求词连续且顺序一致,相当于精确短语。
java
// 命中包含短语 "carbon x1"(顺序不能反)
Query query = Query.of(q -> q.matchPhrase(m -> m
.field("material_desc")
.query("carbon x1")));
List<OrderDoc> list = searcher.search(query, 50);
对比:
match "carbon x1"命中含 carbon 或 x1 的;match_phrase只命中 "carbon x1" 连在一起的。
6. MultiMatchQuery ------ 多字段全文匹配
一个关键字,同时搜多个字段。
java
// 在 material_desc 和 region 两个字段里搜 "thinkpad"
Query query = Query.of(q -> q.multiMatch(m -> m
.fields("material_desc", "region")
.query("thinkpad")));
List<OrderDoc> list = searcher.search(query, 50);
7. ExistsQuery ------ 字段是否有值(IS NOT NULL)
java
// WHERE channel IS NOT NULL
Query query = Query.of(q -> q.exists(e -> e
.field("channel")));
List<OrderDoc> list = searcher.search(query, 50);
要表达 IS NULL,用 bool.must_not(exists):
java
// WHERE channel IS NULL
Query query = Query.of(q -> q.bool(b -> b
.mustNot(mn -> mn.exists(e -> e.field("channel")))));
8. PrefixQuery ------ 前缀匹配(LIKE 'abc%')
java
// WHERE region LIKE 'AP%'
Query query = Query.of(q -> q.prefix(p -> p
.field("region")
.value("AP")));
List<OrderDoc> list = searcher.search(query, 50);
用于
keyword字段;前缀匹配比通配符高效。
9. WildcardQuery ------ 通配符(LIKE '%x%')
* 匹配任意多字符,? 匹配单字符。
java
// WHERE region LIKE '%MEA%'(如 EMEA)
Query query = Query.of(q -> q.wildcard(w -> w
.field("region")
.value("*MEA*")));
List<OrderDoc> list = searcher.search(query, 50);
坑:以
*开头的通配符很慢(无法用索引前缀),数据量大时慎用。
10. FuzzyQuery ------ 模糊/容错(拼写近似)
允许一定编辑距离,容忍拼写错误。
java
// 搜 "thinkpat" 也能命中 "thinkpad"(编辑距离自动)
Query query = Query.of(q -> q.fuzzy(f -> f
.field("material_desc")
.value("thinkpat")
.fuzziness("AUTO")));
List<OrderDoc> list = searcher.search(query, 50);
11. IdsQuery ------ 按 _id 查(WHERE id IN (...))
java
// 按文档 _id 批量取
Query query = Query.of(q -> q.ids(i -> i
.values("1001", "1002", "1003")));
List<OrderDoc> list = searcher.search(query, 50);
12. MatchAllQuery ------ 匹配全部(无 WHERE)
java
// SELECT * FROM orders LIMIT 50
Query query = Query.of(q -> q.matchAll(m -> m));
List<OrderDoc> list = searcher.search(query, 50);
13. BoolQuery ------ 组合查询(AND / OR / NOT)
把上面各种查询组合起来,是最常用的「复合查询」。
java
// WHERE region = 'AP' (filter,不打分)
// AND amount >= 1000 (filter)
// AND material_desc MATCH 'thinkpad' (must,打分)
// AND status <> 'CANCELLED' (must_not)
Query query = Query.of(q -> q.bool(b -> b
.must(m -> m.match(mt -> mt.field("material_desc").query("thinkpad")))
.filter(f -> f.term(t -> t.field("region").value("AP")))
.filter(f -> f.range(r -> r.field("amount").gte(JsonData.of(1000))))
.mustNot(mn -> mn.term(t -> t.field("status").value("CANCELLED")))));
List<OrderDoc> list = searcher.search(query, 50);
bool的详细用法(must/filter/should/must_not、动态拼接、嵌套)见第 05 篇与第 13 篇。
14. 速查总表
| 查询类 | 选择器 | 作用 | SQL 类比 | 本篇 |
|---|---|---|---|---|
TermQuery |
.term |
单值精确 | col = ? |
§1 |
TermsQuery |
.terms |
多值精确 | col IN (...) |
§2 |
RangeQuery |
.range |
范围 | BETWEEN |
§3 |
MatchQuery |
.match |
全文分词 | 全文检索 | §4 |
MatchPhraseQuery |
.matchPhrase |
短语 | 短语匹配 | §5 |
MultiMatchQuery |
.multiMatch |
多字段全文 | 多列 OR ILIKE |
§6 |
ExistsQuery |
.exists |
有无值 | IS NOT NULL |
§7 |
PrefixQuery |
.prefix |
前缀 | LIKE 'abc%' |
§8 |
WildcardQuery |
.wildcard |
通配符 | LIKE '%x%' |
§9 |
FuzzyQuery |
.fuzzy |
模糊容错 | 近似匹配 | §10 |
IdsQuery |
.ids |
按 _id |
id IN (...) |
§11 |
MatchAllQuery |
.matchAll |
全部 | 无 WHERE |
§12 |
BoolQuery |
.bool |
组合 | AND/OR/NOT |
§13 |
15. 坑与最佳实践
- 精确匹配用
keyword:term/terms/prefix/wildcard都作用在不分词字段上。 - 全文搜索用
text:match/match_phrase/multi_match作用在分词字段上。 terms要List<FieldValue>,range值要JsonData.of(...)。- 通配符以
*开头很慢 ,能用prefix就别用前置通配。 - 返回统一用实体类 :本篇全程
OrderDoc.class,字段名不一致处用@JsonProperty映射。 - 组合查询优先
filter:不需要打分的条件放filter,更快且可缓存。
下一篇
进入第二阶段查询能力:10-match-全文匹配.md。
本篇是速查,后续每篇会对单个查询做深入讲解(分词细节、打分、性能等)。