在 Elasticsearch 中,數據建模與映射(Mapping) 是決定搜索性能、存儲效率和功能支持的核心環節。合理的映射設計能讓搜索更精準、聚合更高效、存儲更節省。
本文將全面詳解 Elasticsearch 的 數據建模原則、字段類型、動態映射、自定義分析器、嵌套結構、最佳實踐 等關鍵內容。
一、什么是映射(Mapping)?
映射(Mapping) 是 Elasticsearch 中對索引結構的定義,類似于關系型數據庫中的 Schema。它描述了:
- 每個字段的名稱和數據類型;
- 是否分詞(用于全文搜索);
- 是否索引(可被搜索);
- 如何處理日期、數字、對象等復雜類型。
? 每個索引都有一個 mapping,定義其文檔結構。
二、映射的核心組成
一個完整的 mapping 包含:
{"mappings": {"properties": { ... }, // 字段定義"dynamic": true, // 動態映射策略"date_detection": true, // 是否自動檢測日期"numeric_detection": false, // 是否自動檢測數字"_source": { "enabled": true }, // 是否存儲原始 JSON"routing": { "required": false }}
}
三、字段類型詳解(Field Types)
Elasticsearch 支持豐富的字段類型,選擇合適的類型至關重要。
1. 常用字段類型
類型 | 用途 | 是否分詞 | 示例 |
---|---|---|---|
text | 全文搜索(會分詞) | ? | "這是一段中文文本" |
keyword | 精確匹配、聚合、排序 | ? | "status:active" |
date | 日期類型 | - | "2024-06-01T10:00:00Z" |
long , integer , short , byte | 整數 | - | 100 , -5 |
double , float | 浮點數 | - | 3.14 |
boolean | 布爾值 | - | true , false |
ip | IP 地址 | - | "192.168.1.1" |
geo_point | 地理位置坐標 | - | {"lat": 39.9, "lon": 116.4} |
object | 嵌套 JSON 對象 | - | {"name": "張三", "age": 30} |
nested | 獨立索引的嵌套對象 | - | 數組中的對象列表 |
binary | 二進制數據(Base64) | - | 圖片縮略圖 |
flattened | 將復雜 JSON 作為 keyword 處理 | ? | 動態標簽 |
2. text
vs keyword
:最重要的選擇
場景 | 推薦類型 |
---|---|
搜索商品標題 | text |
聚合品牌、分類 | keyword |
排序價格、時間 | keyword 或數值類型 |
精確匹配訂單號 | keyword |
? 最佳實踐:對同一字段使用 多字段(multi-fields):
"title": {"type": "text","analyzer": "ik_max_word","fields": {"keyword": { "type": "keyword" }}
}
- 搜索用
title
- 聚合/排序用
title.keyword
四、動態映射(Dynamic Mapping)
1. 什么是動態映射?
當插入一個新字段時,Elasticsearch 自動推斷其類型并添加到 mapping 中。
PUT /my-index/_doc/1
{"user": "張三", → 推斷為 text + keyword"age": 30, → 推斷為 long"created": "2024-06-01" → 推斷為 date
}
2. 動態策略(dynamic
)
設置 | 說明 |
---|---|
true (默認) | 自動添加新字段 |
false | 忽略新字段,不報錯 |
strict | 拒絕未知字段,拋出異常 |
? 生產環境建議設為
strict
,防止字段污染。
PUT /my-index
{"mappings": {"dynamic": "strict","properties": { ... }}
}
五、自定義分析器(Analyzer)
用于控制 text
字段的分詞方式,直接影響搜索結果。
1. 分析器組成
- Character Filters:預處理(如 HTML 標簽過濾)
- Tokenizer:分詞器(如空格、中文分詞)
- Token Filters:后處理(小寫、停用詞)
2. 示例:中文分詞配置
PUT /news
{"settings": {"analysis": {"analyzer": {"my_ik_analyzer": {"type": "custom","tokenizer": "ik_max_word","filter": ["lowercase"]}}}},"mappings": {"properties": {"content": {"type": "text","analyzer": "my_ik_analyzer"}}}
}
3. 常見中文分詞插件
ik
:最常用,支持自定義詞典jieba
:Python 社區流行smartcn
:官方提供,功能有限
六、復雜結構建模
1. object
類型(扁平化對象)
{"address": {"city": "北京","district": "朝陽區"}
}
映射:
"address": {"properties": {"city": { "type": "keyword" },"district": { "type": "keyword" }}
}
?? 內部字段是扁平化的:
address.city
和address.district
是獨立字段。
2. nested
類型(獨立索引的對象)
適用于數組中的對象,需保持內部關系。
示例:用戶評論
{"comments": [{ "user": "A", "content": "好", "likes": 10 },{ "user": "B", "content": "差", "likes": 1 }]
}
錯誤方式(object
):
"comments": {"properties": { ... }
}
→ 會匹配 "user:A AND content:差"
(跨文檔匹配)
正確方式(nested
):
"comments": {"type": "nested","properties": {"user": { "type": "keyword" },"content": { "type": "text" },"likes": { "type": "integer" }}
}
查詢:
"query": {"nested": {"path": "comments","query": {"bool": {"must": [{ "match": { "comments.user": "A" } },{ "match": { "comments.content": "好" } }]}}}
}
3. join
類型(父子文檔)
用于一對多關系(如文章與評論),但性能較低。
"relation": {"type": "join","relations": {"post": "comment"}
}
?? 7.x 后推薦用
nested
或應用層關聯。
七、高級映射設置
1. _source
控制
- 是否存儲原始 JSON,默認
true
- 設為
false
可節省空間,但無法:- 獲取原始文檔
- 使用
update
API - 高亮、腳本字段
"_source": { "enabled": false }
2. doc_values
與 fielddata
doc_values: true
(默認):列式存儲,用于排序、聚合(推薦開啟)fielddata
:用于text
字段聚合,消耗大,不推薦
"tags": {"type": "text","fielddata": true // 僅在必須時啟用
}
3. index
設置
控制字段是否可被搜索:
"internal_id": {"type": "keyword","index": false // 不參與搜索,僅存儲
}
八、數據建模最佳實踐 ?
場景 | 建議 |
---|---|
文本搜索 | 使用 text + 中文分詞器(如 ik) |
聚合/排序 | 使用 keyword 或數值類型 |
多字段 | 使用 fields 定義 text + keyword |
動態字段 | 生產環境設為 strict |
嵌套對象 | 使用 nested 保持關系 |
日期 | 使用 date 類型,格式統一 |
大字段 | index: false 節省空間 |
冗余設計 | 適當反規范化提升查詢性能 |
九、反規范化 vs 正規范化
策略 | 說明 | 適用場景 |
---|---|---|
反規范化(Denormalization) | 將關聯數據冗余存儲 | 查詢頻繁、寫少 |
正規范化(Normalization) | 拆分為多個索引,通過 join 查詢 | 數據一致性要求高 |
? Elasticsearch 推薦 適度反規范化,以提升查詢性能。
十、總結:映射設計 checklist
項目 | 是否完成 |
---|---|
使用 text 用于全文搜索 | ? |
使用 keyword 用于聚合排序 | ? |
中文字段配置 IK 分詞器 | ? |
關鍵字段啟用 doc_values | ? |
嵌套對象使用 nested | ? |
生產環境 dynamic: strict | ? |
_source 根據需求啟用 | ? |
避免 fielddata on text | ? |