行业资讯
📅 2026/7/31 17:52:36
第二阶段 11 · term / terms 精确匹配
阶段第二阶段 / 查询能力ES 查询term/terms| PostgreSQL 对照 ?/IN (...)1. 概念term做精确匹配不分词一个字不差地匹配倒排索引里的词项。terms是它的复数版匹配一组值中的任意一个相当于IN。核心规则term必须用在keyword或数值、日期、布尔字段上。对text字段用term常常查不到因为text被分词小写了整串已经不在索引里。2. PostgreSQL 对照-- termSELECT*FROMsalesdataWHEREinvoice_numberINV-2026-001;-- termsSELECT*FROMsalesdataWHEREmaterialIN(ThinkPad-X1,ThinkPad-T14);3. ES DSLterm单值精确GET salesdata_idx/_search { query: { term: { invoice_number: INV-2026-001 } } }terms多值相当于 INGET salesdata_idx/_search { query: { terms: { material: [ThinkPad-X1, ThinkPad-T14] } } }放进 filter不算分、可缓存推荐GET salesdata_idx/_search { query: { bool: { filter: [ { term: { invoice_number: INV-2026-001 } } ] } } }4. Spring Boot 实现本项目AbstractEsHepler#query用的就是terms这里给出完整对照ComponentpublicclassDoc11TermQuery{AutowiredprivateElasticsearchClientelasticsearchClient;/** terminvoice_number ? */publicListMapString,ObjectbyInvoiceNumber(StringindexName,StringinvoiceNumber)throwsIOException{SearchResponseMaprespelasticsearchClient.search(s-s.index(indexName).query(q-q.term(t-t.field(invoice_number).value(invoiceNumber))),Map.class);returntoSourceList(resp);}/** termsmaterial IN (...)本项目 AbstractEsHepler 同款写法 */publicListMapString,ObjectbyMaterials(StringindexName,ListStringmaterials)throwsIOException{ListFieldValuevaluesmaterials.stream().map(FieldValue::of).collect(Collectors.toList());SearchResponseMaprespelasticsearchClient.search(s-s.index(indexName).query(q-q.terms(t-t.field(material).terms(tv-tv.value(values)))),Map.class);returntoSourceList(resp);}privateListMapString,ObjecttoSourceList(SearchResponseMapresp){returnresp.hits().hits().stream().map(Hit::source).filter(Objects::nonNull).collect(Collectors.toList());}}返回强类型 Java 对象推荐替代裸 Map前面都用Map.class接收_source灵活但没有类型检查、字段名拼错运行期才发现。生产接口建议用强类型 DTO把Map.class换成 DTO 的Class客户端会自动反序列化。下面用第 06 篇定义的OrderDocES 下划线字段用JsonProperty映射到驼峰/** terminvoice_number ?直接返回 ListOrderDoc强类型 */publicListOrderDocbyInvoiceNumberTyped(StringindexName,StringinvoiceNumber)throwsIOException{SearchResponseOrderDocrespelasticsearchClient.search(s-s.index(indexName).query(q-q.term(t-t.field(invoice_number).value(invoiceNumber))),OrderDoc.class);// ← 关键传 DTO 的 Class而非 Map.classreturnresp.hits().hits().stream().map(Hit::source)// HitOrderDocsource() 直接是 OrderDoc.filter(Objects::nonNull).collect(Collectors.toList());}对照Map版只把Map.class→OrderDoc.class、SearchResponseMap→SearchResponseOrderDoc其余不变。强类型的好处编译期就能发现字段/类型错误IDE 有补全重构更安全。缺点是字段固定临时探索性查询仍可用Map。5. 坑与最佳实践最经典的坑对text字段term查不到 → 改用field.keyword子字段或把字段设为keyword。大小写敏感term精确匹配INV-001和inv-001不同。keyword默认不小写化。多值用terms而非多个term一个terms比一串should term更简洁高效。优先放filter精确匹配不需要相关性放filter省 CPU 且结果可缓存。FieldValue.of(...)Java 客户端里terms的值要包成FieldValue这是本项目标准写法。下一篇12-range-范围查询.md。