第一阶段 06 · 常用查询类速查与示例(每类一个可运行例子)

阶段:第一阶段 / 核心概念

目标:把官方 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. 坑与最佳实践

  1. 精确匹配用 keywordterm/terms/prefix/wildcard 都作用在不分词字段上。
  2. 全文搜索用 textmatch/match_phrase/multi_match 作用在分词字段上。
  3. termsList<FieldValue>range 值要 JsonData.of(...)
  4. 通配符以 * 开头很慢 ,能用 prefix 就别用前置通配。
  5. 返回统一用实体类 :本篇全程 OrderDoc.class,字段名不一致处用 @JsonProperty 映射。
  6. 组合查询优先 filter :不需要打分的条件放 filter,更快且可缓存。

下一篇

进入第二阶段查询能力:10-match-全文匹配.md

本篇是速查,后续每篇会对单个查询做深入讲解(分词细节、打分、性能等)。

相关推荐
comedate2 小时前
Windows / Linux GPU 虚拟内存布局差异与 CUDA 越界访问行为
linux·windows·mmu·cuda 越界访问
剩下了什么3 小时前
float32 与 float64 精度陷阱:如何在 Go 中避免错误使用
开发语言·后端·golang
指尖时光.3 小时前
Three.js 超全入门综合案例
开发语言·javascript·ecmascript
暴力求解4 小时前
Linux网络---传输层协议TCP(二)
linux·服务器·开发语言·网络·tcp/ip
程序员三藏4 小时前
Python+requests实现接口自动化测试
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
吴声子夜歌4 小时前
Java面试题——基础(一)
java·开发语言
小庞在加油4 小时前
WinDbg实战:QT/跨平台项目死锁与无响应问题排查指南
开发语言·qt·windbg·工具
IT技术分享社区5 小时前
Windows桌面时钟推荐:始终置顶、无级缩放、支持全屏游戏显示时间
windows·微软技术·电脑技巧·桌面美化·电脑干货
Vae_Mars5 小时前
C#中的delegate委托
开发语言·c#
一直走下去-明5 小时前
简单的http抓包解包完整代码
开发语言·python