4. 映射与分析:映射(Mapping)概念、字段类型详解、动态映射与显式映射、分析器(Analyzer)原理

好,咱们进入第四章。这一章可以说是 Elasticsearch 的「地基」——映射和分析。说白了,映射就是告诉 ES 你的数据长什么样,分析器则是决定怎么把文本切碎、洗干净。这两块搞不明白,后面写查询语句很容易翻车。我自己刚接触 ES 时,就因为在映射上偷了懒,结果线上数据一多,查询慢得让人抓狂。

4.1 映射(Mapping)到底是什么?

映射,你可以把它理解成数据库里的「表结构定义」。它规定了每个字段的数据类型、是否被索引、用什么分析器等等。ES 收到一条 JSON 文档后,会根据映射来决定怎么存、怎么搜。

举个例子:

PUT /my_index
{
  "mappings": {
    "properties": {
      "title":    { "type": "text" },
      "price":    { "type": "double" },
      "created_at": { "type": "date" }
    }
  }
}

这段代码创建了一个索引,明确告诉 ES:title 是文本,price 是浮点数,created_at 是日期。嗯,这就是最基础的显式映射。

我的习惯:生产环境里,我从来不用纯动态映射。虽然省事,但字段类型猜错了,后面改起来代价极高。尤其是日期字段,ES 经常把 "2024-01-01" 和 "1234567890" 这种时间戳搞混。

4.2 字段类型详解

ES 的字段类型比关系型数据库丰富得多。我挑几个最常用的说说,剩下的你查官方文档就行。

4.2.1 核心类型

类型 说明 适用场景
text 全文文本,会被分析器分词 文章标题、正文内容
keyword 精确值,不分词,用于排序、聚合 标签、邮箱、状态码
integer/long 整数类型 年龄、数量
double/float 浮点数类型 价格、评分
boolean 布尔值 是否上架、是否删除
date 日期类型,支持多种格式 创建时间、更新时间
object 嵌套 JSON 对象 地址信息、用户信息
nested 独立索引的嵌套对象数组 订单明细、评论列表

这里有个坑,我必须要说:text 和 keyword 的区别。text 会被分词,适合搜索;keyword 保持原样,适合精确匹配和聚合。很多新手把手机号设成 text,结果搜 "138" 能搜出一堆不相关的号码,这就是没搞清楚类型。

4.2.2 多字段(multi-fields)

实际项目中,一个字段往往既要全文搜索,又要精确匹配。ES 提供了多字段特性:

PUT /my_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

这样,title 用于搜索,title.keyword 用于排序和聚合。我在做电商搜索时,商品名称就是这么配的,效果很好。

4.3 动态映射与显式映射

ES 有两种映射方式:动态映射和显式映射。说白了,一个自动,一个手动。

4.3.1 动态映射

当你往一个不存在的索引里写入文档时,ES 会自动推断字段类型。比如:

POST /my_index/_doc/1
{
  "name": "张三",
  "age": 25,
  "is_vip": true
}

ES 会自动把 name 设为 text,age 设为 long,is_vip 设为 boolean。听起来很方便对吧?但我要泼盆冷水:动态映射只适合开发环境或日志场景。生产环境里,字段类型猜错是常有的事。我曾经遇到过,一个存 IP 地址的字段被动态映射成了 text,导致后面做范围查询时完全没法用。

注意:动态映射一旦生成,就不能直接修改字段类型。除非你重建索引。所以,重要业务请务必使用显式映射。

4.3.2 显式映射

显式映射就是你自己定义每个字段的类型和属性。比如:

PUT /my_index
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "age": {
        "type": "integer"
      },
      "is_vip": {
        "type": "boolean"
      },
      "created_at": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

我个人建议,所有核心业务索引都用显式映射。虽然前期多花几分钟写配置,但后面能省下大量排坑的时间。

4.4 分析器(Analyzer)原理

分析器是 ES 搜索的灵魂。它负责把一段文本拆成一个个词条(term),方便后续的倒排索引和搜索匹配。

4.4.1 分析器的组成

一个分析器由三部分组成:

  1. 字符过滤器(Character Filters):预处理文本,比如去掉 HTML 标签、替换特殊字符。
  2. 分词器(Tokenizer):把文本切成词条。这是核心步骤。
  3. 词项过滤器(Token Filters):对切好的词条做二次处理,比如转小写、去停用词、加同义词。

举个例子,分析 "I love Elasticsearch":

  • 字符过滤器:没特殊处理,原样输出。
  • 分词器(standard):按空格和标点切分,得到 ["I", "love", "Elasticsearch"]。
  • 词项过滤器(lowercase):全部转小写,得到 ["i", "love", "elasticsearch"]。

最终存入倒排索引的就是 ["i", "love", "elasticsearch"]。你想想看,如果搜索时输入 "Elasticsearch",经过同样的分析流程变成 "elasticsearch",就能匹配上了。

4.4.2 内置分析器

ES 自带了一些常用分析器:

分析器名称 说明 示例输入 → 输出
standard 标准分析器,按单词边界切分,转小写 "Hello World" → ["hello", "world"]
simple 按非字母字符切分,转小写 "Hello-World" → ["hello", "world"]
whitespace 仅按空格切分,不转小写 "Hello World" → ["Hello", "World"]
keyword 不切分,原样输出 "Hello World" → ["Hello World"]
pattern 按正则表达式切分 可自定义分隔符

4.4.3 自定义分析器

内置分析器不够用时,你可以自己组装。比如中文分词,我常用 IK 分词器:

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "ik_smart",
          "filter": ["lowercase", "stop"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

这里我自定义了一个分析器,用 IK 分词器做中文分词,再转小写、去停用词。我在做新闻搜索时就是这么配的,搜索「中华人民共和国」能精准匹配到相关文章,而不是切成「中华」「人民」「共和国」三个孤立的词。

核心要点:分析器决定了搜索的「召回率」和「精准度」。分析器选得好,搜索体验就好;选得不好,要么搜不到,要么搜出一堆垃圾。

4.4.4 测试分析器

ES 提供了一个很方便的 API 来测试分析器效果:

POST /_analyze
{
  "analyzer": "standard",
  "text": "Elasticsearch is awesome"
}

返回结果会显示每个词条及其位置。我每次自定义分析器后,都会先用这个 API 跑一遍,确认分词结果符合预期。嗯,这个小习惯帮我避免过好几次线上事故。

4.5 映射与分析器的关系

映射和分析器是紧密配合的。映射里指定了某个 text 字段用哪个分析器,而分析器决定了这个字段怎么被分词、怎么被搜索。说白了,映射是「骨架」,分析器是「血肉」。

举个例子,如果你在映射里给 title 字段指定了 analyzer: "ik_smart",那么索引时和搜索时都会用这个分析器。除非你额外指定 search_analyzer,否则两者保持一致。

避坑指南:我曾经犯过一个错——索引时用了一个分析器,搜索时用了另一个。结果导致「苹果手机」这个词,索引时被切成了「苹果」「手机」,搜索时却被切成了「苹果手机」一个整体。怎么搜都搜不全。后来我统一了索引和搜索的分析器,问题才解决。

4.6 小结

这一章我们聊了映射的概念、字段类型、动态与显式映射,还有分析器的原理。说白了,映射是告诉 ES 怎么存,分析器是告诉 ES 怎么切。两者配合好了,搜索才能又快又准。

下一章,我们会深入搜索语法,看看怎么用 DSL 写出高效的查询语句。到时候你会发现,映射和分析器配得好,写查询简直行云流水。