Elasticsearch 中Text 类型和 Keyword 类型有什么区别?
在 Elasticsearch 中,Text 和 Keyword 是处理字符串(String)数据的两种主要类型。它们的核心区别在于是否进行分词(Analysis)以及底层存储结构的不同,这也决定了它们各自适用的场景。
以下是详细的对比分析:
1. 核心区别总结表
| 特性 | Text 类型 | Keyword 类型 |
|---|---|---|
| 分词 (Analysis) | 会分词 (Analyzed)。会被解析器拆分成独立的词项 (tokens)。 | 不分词 (Not Analyzed)。作为一个整体存储。 |
| 主要用途 | 全文检索 (Full-text search)。如搜索文章内容、产品描述。 | 精确匹配、过滤、排序、聚合。如 ID、状态码、标签、邮箱。 |
| 查询方式 | 通常使用 match 查询。支持模糊匹配、相关性打分。 |
通常使用 term 查询。必须完全一致(包括大小写)。 |
| 排序与聚合 | 不支持 (默认禁用 Fielddata,因为消耗大量堆内存)。 | 支持 (使用 Doc Values,性能高)。 |
| 存储结构 | 倒排索引 (Inverted Index)。 | 倒排索引 + 列式存储 (Doc Values)。 |
| 大小写敏感 | 通常不敏感 (标准分析器会将词转为小写)。 | 敏感 (原样存储)。 |
2. 详细原理解析
Text 类型 (全文检索)
- 处理过程:当数据存入
Text字段时,ES 会使用分析器(Analyzer)对字符串进行处理。- 例如:输入
"Apple Store"。 - 标准分析器处理后:生成两个词项
["apple", "store"](转小写、去标点、拆分)。
- 例如:输入
- 搜索:当你搜索
"Apple"时,ES 会去倒排索引中查找apple,能找到文档。 - 缺点:因为原文本被拆散了,所以无法直接用来做排序(Sorting)或聚合(Aggregation)。如果强行开启
fielddata来支持排序,会占用极高的内存,容易导致 OOM(内存溢出)。
Keyword 类型 (结构化数据)
- 处理过程:数据存入
Keyword字段时,ES 不做任何修改,原样存储。- 例如:输入
"Apple Store"。 - 存储结果:
"Apple Store"这一整个字符串。
- 例如:输入
- 搜索:
- 搜索
"Apple Store"-> 匹配。 - 搜索
"Apple"-> 不匹配。 - 搜索
"apple store"(小写) -> 不匹配 (除非原数据就是小写)。
- 搜索
- 优势:
Keyword类型默认开启 Doc Values(一种列式存储结构,存在磁盘上),这使得它在排序、聚合(如统计每个品牌的销量)和脚本计算时速度非常快且内存友好。
3. 代码示例
假设我们有一个博客文章的索引:
Mapping 定义:
json
PUT /blog_posts
{
"mappings": {
"properties": {
"title": {
"type": "text" // 标题:用于全文搜索
},
"status": {
"type": "keyword" // 状态:用于精确过滤(如 "published", "draft")
}
}
}
}
插入数据:
json
POST /blog_posts/_doc/1
{
"title": "Elasticsearch is fast",
"status": "PUBLISHED"
}
场景 A:搜索 Title (Text)
json
// 使用 match 查询,不区分大小写,分词匹配
GET /blog_posts/_search
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
// 结果:命中文档。因为 "Elasticsearch" 被分词并匹配到了索引中的 "elasticsearch"。
场景 B:搜索 Status (Keyword)
json
// 错误示范:大小写不匹配
GET /blog_posts/_search
{
"query": {
"term": {
"status": "published"
}
}
}
// 结果:未命中。因为 Keyword 存储的是 "PUBLISHED",而你搜的是 "published"。
// 正确示范
GET /blog_posts/_search
{
"query": {
"term": {
"status": "PUBLISHED"
}
}
}
// 结果:命中。
4. 最佳实践:多字段 (Multi-fields)
在实际开发中,我们经常遇到既需要全文检索,又需要排序/聚合的字段(例如:文章标题、人名)。
此时,可以使用 ES 的 Multi-fields 功能,将同一个字段映射为两种类型。
Mapping 定义:
json
PUT /products
{
"mappings": {
"properties": {
"product_name": {
"type": "text", // 主字段:用于搜索
"fields": {
"raw": { // 子字段:命名为 raw (也可以叫 keyword)
"type": "keyword" // 类型为 keyword,用于排序和聚合
}
}
}
}
}
}
如何使用:
- 搜索时:使用
product_name。json"match": { "product_name": "iPhone" } - 排序/聚合时:使用
product_name.raw。json"sort": [ { "product_name.raw": { "order": "asc" } } ]
总结
- Text:也就是“文章”,用来搜内容的。
- Keyword:也就是“标签/ID”,用来筛选、排序、统计的。
- 两者都要:使用
fields开启多字段映射。