Elasticsearch 从 0 到 1(二):索引、Mapping 和分词器
上一篇讲了 ES 是什么,以及为什么它适合搜索场景:
Elasticsearch 从 0 到 1(一):ES 是什么,为什么需要它
这一篇开始真正建索引。
ES 入门最容易踩的坑就是:数据先随便写进去,Mapping 让 ES 自动推断,等后面要搜索、排序、聚合时才发现字段类型不对。
所以建 ES 索引前,最好先想清楚:
哪些字段要全文搜索?哪些字段要精确过滤?哪些字段要排序?哪些字段要聚合?哪些字段只是展示?创建一个简单索引
最简单的创建索引:
PUT /products这样 ES 会创建一个名为 products 的索引。
但真实项目不建议只这样创建,因为 Mapping 会交给 ES 动态推断。
更推荐显式指定字段:
PUT /products{ "mappings": { "properties": { "productId": { "type": "long" }, "title": { "type": "text" }, "brand": { "type": "keyword" }, "category": { "type": "keyword" }, "price": { "type": "integer" }, "sales": { "type": "integer" }, "createdAt": { "type": "date" } } }}这就明确告诉 ES 每个字段该怎么处理。
Mapping 是什么
Mapping 就是字段定义。
它决定:
- 字段是什么类型;
- 是否分词;
- 是否能搜索;
- 是否能排序;
- 是否能聚合;
- 用哪个分词器。
可以把 Mapping 类比成 MySQL 的表结构,但不要完全等同。
MySQL 建表关注的是数据约束和关系结构,ES Mapping 更关注搜索方式。
常见字段类型
常用字段类型可以先记这些:
| 类型 | 适合字段 |
|---|---|
text | 商品标题、文章内容、描述 |
keyword | 品牌、分类、状态、标签、编码 |
integer / long | 数量、ID、销量 |
double / scaled_float | 价格、评分 |
date | 创建时间、更新时间 |
boolean | 是否上架、是否删除 |
object | 普通对象 |
nested | 需要独立匹配的对象数组 |
初学时最重要的是区分 text 和 keyword。
text 和 keyword 的区别
text 会分词,适合全文搜索。
比如:
{ "title": "无线蓝牙耳机"}如果 title 是 text,它会被分词,用户搜索“蓝牙”“耳机”都有机会搜到。
keyword 不分词,适合精确匹配、排序、聚合。
比如:
{ "brand": "SoundGo"}品牌通常不需要拆成词,用户筛选品牌时要精确匹配。
一个常见设计:
{ "title": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }}这样 title 本身可以全文搜索,title.keyword 可以用于精确匹配或排序。
什么是分词器
分词器负责把文本拆成词。
比如:
无线蓝牙耳机如果没有合适的中文分词器,拆分结果可能不符合搜索预期。
英文天然有空格,分词相对容易;中文没有天然空格,所以通常需要中文分词器。
常见中文分词方案包括:
- IK 分词器;
- smartcn;
- 自定义词典;
- 拼音分词器。
具体用哪个要看业务。商品搜索里经常需要自定义词典,比如品牌名、型号、行业词。
analyzer 和 search_analyzer
字段可以指定两个分词器:
{ "title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }}analyzer 是写入文档时用的分词器。
search_analyzer 是用户搜索时用的分词器。
常见策略是:
- 建索引时切得细一点,尽量多召回;
- 搜索时切得稳一点,减少噪音。
这不是绝对规则,但商品搜索里很常见。
商品索引 Mapping 示例
一个商品搜索索引可以这样设计:
PUT /products{ "settings": { "number_of_shards": 3, "number_of_replicas": 1 }, "mappings": { "properties": { "productId": { "type": "long" }, "title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "brand": { "type": "keyword" }, "category": { "type": "keyword" }, "price": { "type": "integer" }, "sales": { "type": "integer" }, "stock": { "type": "integer" }, "status": { "type": "keyword" }, "createdAt": { "type": "date" } } }}说明:
title用text,支持全文搜索;brand、category、status用keyword,支持精确筛选;price、sales用数值类型,支持范围查询和排序;createdAt用date,支持时间范围查询;title.keyword可以用于精确匹配或排序。
写入一条商品文档
PUT /products/_doc/1001{ "productId": 1001, "title": "无线蓝牙耳机", "brand": "SoundGo", "category": "数码耳机", "price": 199, "sales": 3000, "stock": 80, "status": "ON_SALE", "createdAt": "2026-07-02T10:00:00"}这里 _doc/1001 的 1001 是文档 ID。
如果 MySQL 里商品 ID 是 1001,ES 里也用同一个 ID,会方便后续更新和删除。
Mapping 能不能改
Mapping 不是完全不能改,但有些字段类型一旦确定就不能随便改。
比如一个字段已经是 keyword,后面想改成 text,通常不能直接改。
更稳的方式是:
- 创建新索引;
- 使用新 Mapping;
- 重新导入数据;
- 通过别名切换读写。
所以 ES 建索引前要先设计好字段,不要完全依赖动态 Mapping。
常见 Mapping 坑
几个常见坑:
- 商品标题建成
keyword,结果不能正常全文搜索; - 品牌建成
text,聚合结果被分词拆乱; - 价格建成字符串,范围查询和排序出问题;
- 时间字段格式不统一,写入失败;
- 让 ES 自动推断字段,后面发现类型不符合业务;
- 对象数组用了
object,但实际需要nested。
前期 Mapping 设计多花一点时间,后面会少很多返工。
这一篇先记住什么
- Mapping 决定字段怎么存、怎么搜;
text会分词,适合全文搜索;keyword不分词,适合过滤、排序、聚合;- 中文搜索通常需要合适的中文分词器;
- 商品搜索要提前设计好字段类型;
- Mapping 不要完全依赖自动推断;
- 字段类型设计错了,后面通常要重建索引。
下一篇开始写查询 DSL:
参考
If this article helped you, please share it with others!
Some information may be outdated






