【Elasticsearch从入门到精通】第26篇:Elasticsearch Term级别查询——精确匹配与范围查询
上一篇【第25篇】Elasticsearch全文检索——match、phrase与query_string详解下一篇【第27篇】Elasticsearch复合查询——bool、dis_max与function_score摘要与全文检索的模糊匹配不同Term级别查询用于结构化数据的精确检索不对查询词进行分词分析直接与倒排索引中的词条Term进行匹配。这类查询在电子商务筛选、日志分析、权限控制等业务场景中至关重要。本文将从term精确查询入手剖析keyword字段与text字段在使用term查询时的本质区别随后介绍terms多值匹配等效于SQL的IN子句和terms_set最小匹配数查询重点讲解range范围查询的各类参数配置包括日期格式、时区处理和几何关系最后系统梳理exists存在检查、prefix前缀查询、wildcard通配符查询和regexp正则查询的用法与注意事项。全文配有丰富的REST API代码示例和对比表格帮助读者构建精准高效的结构化检索方案。关键词term查询、精确匹配、range范围查询、prefix前缀查询、正则查询。正文1. Term级别查询概述Term词条是Elasticsearch倒排索引中最小的索引和搜索单元。与全文检索Full-text Query不同Term级别查询具有以下特征特征全文检索Term级别查询分析处理查询文本先经过分析器Analyzer分词查询文本不经过分析直接匹配匹配方式模糊、近似匹配精确匹配适用字段text类型字段keyword、number、date、boolean等评分机制计算BM25相关度评分默认计分可通过constant_score包裹典型场景文章搜索、商品搜索状态筛选、分类过滤、ID查询关键认知对text字段使用term查询可能得不到预期结果因为text字段在索引时已被分词原始值不再存在于倒排索引中。2. term精确查询term查询用于查找指定字段中包含精确值的文档。2.1 基本用法GET/products/_search{query:{term:{status:published}}}以上查询在status字段假设为keyword类型中精确查找值为published的文档。2.2 keyword字段与text字段的区别这是Term级别查询最重要、最容易出错的概念。# 创建索引演示两种字段类型的区别PUT/demo_index{mappings:{properties:{title_keyword:{type:keyword},title_text:{type:text}}}}# 索引文档POST/demo_index/_doc{title_keyword:Elasticsearch权威指南,title_text:Elasticsearch权威指南}# keyword字段精确匹配能找到GET/demo_index/_search{query:{term:{title_keyword:Elasticsearch权威指南}}}# text字段找不到text字段在索引时已被分词GET/demo_index/_search{query:{term:{title_text:Elasticsearch权威指南}}}# text字段应使用match查询GET/demo_index/_search{query:{match:{title_text:Elasticsearch权威指南}}}场景字段类型推荐查询状态/类型/枚举值keywordterm标题/描述/正文textmatch数字IDinteger/longterm标签列表keywordterm布尔值booleanterm2.3 boost加权GET/products/_search{query:{bool:{should:[{term:{brand:{value:Apple,boost:2.0}}},{term:{brand:Samsung}}]}}}2.4 case_insensitive忽略大小写7.10GET/products/_search{query:{term:{category:{value:ELECTRONICS,case_insensitive:true}}}}3. terms多值匹配terms查询等效于SQL中的IN操作匹配字段值在指定集合中的文档。3.1 基本用法GET/products/_search{query:{terms:{category:[electronics,computers,accessories]}}}以上查询等价于SQLWHERE category IN (electronics, computers, accessories)3.2 使用索引中的值作为terms集合在大批量筛选时可以使用索引中的文档值作为terms来源GET/products/_search{query:{terms:{brand_id:{index:brands,id:popular_brands,path:brand_ids}}}}3.3 terms_set最小匹配数查询terms_set查询在terms基础上增加了至少匹配N个的条件# 创建索引定义required_matches字段PUT/job_candidates{mappings:{properties:{name:{type:keyword},programming_languages:{type:keyword},required_matches:{type:integer}}}}# 索引候选人文档POST/job_candidates/_doc/1{name:张三,programming_languages:[java,python,go,rust],required_matches:2}POST/job_candidates/_doc/2{name:李四,programming_languages:[java,python],required_matches:3}# terms_set查询至少匹配required_matches指定的技能数GET/job_candidates/_search{query:{terms_set:{programming_languages:{terms:[java,python,go],minimum_should_match_field:required_matches}}}}在这个场景中候选人张三需要至少匹配2项技能javapython2 满足李四需要至少匹配3项只有2项 不满足。terms_set也支持脚本动态计算最小匹配数GET/job_candidates/_search{query:{terms_set:{programming_languages:{terms:[java,python,go],minimum_should_match_script:{source:Math.min(params.num_terms, 2)}}}}}4. range范围查询range查询用于查找字段值在指定区间内的文档支持数字、日期等类型。4.1 基本参数参数含义SQL等效gte大于等于≥gt大于lte小于等于≤lt小于GET/products/_search{query:{range:{price:{gte:100,lte:500}}}}等价SQLWHERE price 100 AND price 5004.2 boost加权GET/products/_search{query:{range:{price:{gte:100,lte:200,boost:2.0}}}}4.3 日期范围查询GET/orders/_search{query:{range:{order_date:{gte:2025-01-01,lte:2025-06-30,format:yyyy-MM-dd}}}}Elasticsearch支持多种日期格式GET/orders/_search{query:{range:{order_date:{gte:01/01/2025,lte:2025-06-30T23:59:59,format:dd/MM/yyyy||yyyy-MM-ddTHH:mm:ss}}}}日期数学表达式GET/logs/_search{query:{range:{timestamp:{gte:now-7d/d,lte:now/d,format:epoch_millis}}}}常用日期数学表达式表达式含义now当前时间now-1d24小时前now-7d/d7天前的午夜now/M当月第一天now-1M/M上个月第一天now1h1小时后4.4 relation参数地理/区间关系relation参数定义了范围查询与字段值尤其是数组字段的匹配规则值含义匹配条件INTERSECTS默认交集字段值区间与查询区间有交集CONTAINS包含字段值区间完全包含查询区间WITHIN被包含字段值区间完全在查询区间内# 假设文档有 date_range 字段存储事件持续时间段GET/events/_search{query:{range:{event_period:{gte:2025-06-01,lte:2025-06-15,relation:WITHIN}}}}4.5 time_zone时区处理GET/orders/_search{query:{range:{order_date:{gte:2025-01-01,lte:2025-01-31,time_zone:08:00,format:yyyy-MM-dd}}}}5. exists存在查询exists查询用于查找指定字段有值的文档字段不为null且不为空数组。GET/users/_search{query:{exists:{field:email}}}常见的业务场景# 查找未设置手机号的用户GET/users/_search{query:{bool:{must_not:{exists:{field:phone}}}}}# 查找既有邮箱又有手机号的用户GET/users/_search{query:{bool:{must:[{exists:{field:email}},{exists:{field:phone}}]}}}注意null值、空数组[]和空字符串对于text字段都被视为不存在。6. prefix前缀查询prefix查询匹配指定字段以给定前缀开头的文档。适用于keyword类型字段。GET/products/_search{query:{prefix:{sku:ELEC}}}以上查询匹配sku以ELEC开头的所有文档如ELEC-001、“ELEC002”、“ELECTRONICS”。# 搜索用户名以特定前缀开头的用户GET/users/_search{query:{prefix:{username:{value:admin_,case_insensitive:true}}}}性能警告前缀查询遍历倒排索引中的词条列表对前缀短、匹配范围大的查询性能较差。建议考虑以下优化方案使用edge n-gram分词器预处理在应用层维护前缀树或Trie索引使用suggest API实现搜索提示7. wildcard通配符查询wildcard查询支持使用通配符进行模式匹配。通配符含义?匹配任意单个字符*匹配0个或多个字符GET/files/_search{query:{wildcard:{filename:{value:report_2025*.pdf}}}}匹配示例report_2025_sales.pdf✅report_2025Q1_financial.pdf✅report_2024_sales.pdf❌# 匹配固定长度的编号GET/orders/_search{query:{wildcard:{order_id:{value:2025????ORD-???}}}}性能注意避免在开头使用通配符如*keyword这会扫描所有Term如需反向匹配考虑在索引时使用reverse分词器设置case_insensitive: true也可能影响性能8. regexp正则查询regexp查询使用Lucene正则表达式进行匹配提供最灵活的模式匹配能力。8.1 Lucene正则语法语法含义.匹配任意单个字符?前面的字符出现0次或1次前面的字符出现1次或多次*前面的字符出现0次或多次{n}前面的字符恰好出现n次{n,m}前面的字符出现n到m次[abc]匹配a、b或c中的任意一个[^abc]匹配除a、b、c外的任意字符[a-z]匹配a到z范围内的任意字符|OR关系()分组8.2 实际示例# 匹配以http或https开头的URLGET/access_logs/_search{query:{regexp:{url:https?://.*}}}# 匹配标准邮箱格式GET/users/_search{query:{regexp:{email:[a-zA-Z0-9._%-][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}}}}# 匹配IP地址段10.0.0.xGET/network_logs/_search{query:{regexp:{client_ip:10\\.0\\.0\\.[0-9]}}}8.3 性能控制GET/logs/_search{query:{regexp:{message:{value:ERROR.*timeout,flags:ALL,max_determinized_states:10000}}}}flags正则引擎标志可选值为ALL、COMPLEMENT、EMPTY、INTERVAL、INTERSECTION、NONE、ANYSTRINGmax_determinized_states限制DFA确定性有限自动机状态数默认10000防止灾难性回溯安全提醒永远不要将用户直接输入的字符串作为正则表达式查询这可能导致ReDoS正则表达式拒绝服务攻击。9. ids根据ID查询ids查询通过文档_id进行检索是获取特定文档最高效的方式。GET/products/_search{query:{ids:{values:[1,2,3]}}}# type参数在7.x中已废弃不推荐使用GET/products/_search{query:{ids:{values:[product-001,product-002]}}}ids查询直接定位到具体文档Lucene内部会转换为文档ID级的过滤性能远优于其他查询。10. fuzzy模糊查询fuzzy查询允许在拼写错误时仍然匹配基于Damerau-Levenshtein编辑距离。GET/products/_search{query:{fuzzy:{name:{value:iphone,fuzziness:AUTO}}}}详细参数GET/products/_search{query:{fuzzy:{name:{value:niik,fuzziness:2,prefix_length:1,max_expansions:50,transpositions:true}}}}参数含义fuzziness最大编辑距离prefix_length前N个字符必须精确匹配max_expansions最大扩展词条数transpositions是否允许相邻字符换位11. Term级别查询全面对比查询类型匹配方式性能适用字段典型场景term精确值匹配高keyword/number/date状态筛选、ID查询terms多值精确匹配高keyword/number批量筛选INterms_set最小匹配数中keyword技能匹配、标签匹配range数值/日期范围高number/date价格、时间筛选exists字段存在性高所有类型空值检查prefix前缀匹配中keyword自动补全、代码前缀wildcard通配符匹配低keyword文件名匹配regexp正则匹配很低keyword复杂模式校验fuzzy模糊编辑距离低keyword/text拼写纠错idsID直接定位极高_id精确获取文档12. 实战组合示例# 电商商品复杂筛选GET/products/_search{query:{bool:{filter:[{terms:{category:[electronics,computers]}},{term:{status:active}},{range:{price:{gte:100,lte:1000}}},{exists:{field:stock}},{bool:{should:[{prefix:{sku:ELEC}},{term:{brand:Samsung}}],minimum_should_match:1}}]}}}总结与最佳实践字段类型决定查询选择keyword字段用term/terms/prefix/wildcardtext字段用match。对text字段使用term查询是一个常见且隐蔽的错误。性能优先原则ids term terms range exists prefix wildcard fuzzy regexp。在满足业务需求的前提下优先选择性能更高的查询类型。避免危险模式不要使用前置通配符*keyword慎用复杂的正则表达式。对于极端性能场景考虑使用索引时预处理如edge n-gram替代运行时前缀匹配。filter优先于must对于不需要评分的结构化筛选条件使用filter包裹以利用缓存提升性能。range查询注意时区跨时区业务场景下务必正确设置time_zone参数避免查询边界错误。terms查询的元素数量terms数组过大时超过数百个元素建议分批查询或使用terms lookup。安全防护禁止将未校验的用户输入直接用于regexp或wildcard查询防范ReDoS攻击。对query_string设置analyze_wildcard: false。上一篇【第25篇】Elasticsearch全文检索——match、phrase与query_string详解下一篇【第27篇】Elasticsearch复合查询——bool、dis_max与function_score