
1. 初识ElasticSearch中的keyword与text类型刚接触ElasticSearch时很多人都会被字符串字段的两种类型搞懵keyword和text。这就像你去超市买水果发现苹果还分红富士和阿克苏——看起来都是苹果但口感和用途却大不相同。在ES 5.0之前字符串只有string一种类型这导致了很多混乱。就像把所有的工具都扔进一个抽屉找起来特别费劲。后来ES团队把string类型拆分成了keyword和text让数据存储更精准。我刚开始用ES 6.x版本时就踩过这个坑——明明字段定义的是text做精确查询时却死活查不到数据。核心区别其实很简单text类型就像一本被拆成单词的字典适合全文搜索keyword类型像未拆封的密码本适合精确匹配举个例子假设我们有个商品表其中商品描述字段就应该用text类型因为用户可能会搜索透气 运动鞋这样的关键词而商品编号字段就该用keyword因为我们需要精确匹配如Nike-AirMax-001这样的编号。2. 深入理解两种类型的存储机制2.1 text类型的分词魔法text类型最特别的地方在于它会自动分词。比如存入华为Mate60 Pro手机这个字符串PUT /products/_doc/1 { name: 华为Mate60 Pro手机 }ES会使用默认的标准分词器standard analyzer把它拆分成华为mate60pro手机这就像把一篇文章拆成了关键词索引。实测下来这种存储方式会让全文搜索非常高效但有个坑需要注意中文分词效果可能不理想。我曾在电商项目中遇到用户搜连衣裙找不到裙子的问题最后不得不接入IK分词器来解决。2.2 keyword类型的原样存储keyword类型则简单粗暴——存什么就是什么不分词。还是上面的例子PUT /products/_doc/2 { sku: HW-Mate60-Pro-256GB }这个SKU会作为一个整体存储非常适合做精确匹配。但要注意默认限制超过256字符的keyword字段不会被索引。之前做日志分析时就遇到过IP地址拼接超长的问题需要通过mapping调整ignore_above参数{ mappings: { properties: { long_url: { type: keyword, ignore_above: 1024 } } } }3. 查询方式的实战对比3.1 match查询的陷阱很多新手会困惑为什么用match查广东深圳会返回广西的数据看这个例子GET /address/_search { query: { match: { city: 广东深圳 } } }因为match查询会对查询词也分词广东深圳被拆成广、东、深、圳只要包含任意一个字就会匹配。这就像用手机壳搜索结果连电视机外壳都出来了。解决方案改用match_phrase保证词组顺序查询city.keyword字段做精确匹配提高match的minimum_should_match参数3.2 term查询的精确之道term查询适合keyword字段它不分析查询词GET /products/_search { query: { term: { sku.keyword: { value: HW-Mate60-Pro-256GB } } } }但注意一个常见坑点term查询text字段基本都会失败因为存的是分词后的terms而查询的是原始字符串。就像用完整指纹去匹配碎片当然对不上。4. 业务场景中的类型选择4.1 电商搜索的典型应用在商品搜索中我的经验是商品名称text类型ik分词商品IDkeyword类型商品标签text类型keyword子字段PUT /ecommerce { mappings: { properties: { product_name: { type: text, analyzer: ik_max_word, fields: { keyword: { type: keyword, ignore_above: 256 } } }, product_id: { type: keyword } } } }4.2 日志分析的特殊处理处理Nginx日志时要注意URL路径适合用textHTTP状态码必须用keywordIP地址推荐用keywordip类型PUT /nginx_logs { mappings: { properties: { request_url: { type: text }, status: { type: keyword }, client_ip: { type: ip } } } }曾有个坑把status误设为text类型导致status500的日志无法用terms聚合统计查询性能直接下降80%。5. 高级技巧与性能优化5.1 多字段(multi-field)映射这是ES最实用的功能之一允许一个字段同时拥有多种类型PUT /users { mappings: { properties: { username: { type: text, fields: { raw: { type: keyword }, pinyin: { type: text, analyzer: pinyin } } } } } }这样username可以用username做中文搜索用username.raw做精确匹配用username.pinyin做拼音搜索5.2 聚合查询的性能陷阱做聚合时一定要用keyword字段GET /sales/_search { size: 0, aggs: { popular_products: { terms: { field: product_id.keyword, size: 10 } } } }如果误用text字段ES会尝试对分词后的terms做聚合不仅结果错误还会引发内存爆炸。我就曾因此导致集群OOM教训深刻。6. 实际案例用户标签系统设计去年设计社交平台的用户标签系统时我们这样处理标签字段PUT /user_profiles { mappings: { properties: { tags: { type: keyword, normalizer: lowercase } } }, settings: { analysis: { normalizer: { lowercase: { type: custom, filter: [lowercase] } } } } }关键点使用keyword保证精确匹配添加normalizer统一转小写配合terms查询实现标签过滤GET /user_profiles/_search { query: { term: { tags: python } } }这套设计支持了千万级用户的实时标签查询QPS稳定在2k。