重生之我们在ES顶端相遇第5章-常用字段类型

思维导图

前置

在第4章,我们提到了 keyword(一笔带过)。在本章,我们将介绍 ES 的字段类型。全面的带大家了解 ES 各个字段类型的使用场景。

字段类型

ES 支持以下字段类型(仅介绍开发中常用,更多内容请自行阅读 官方文档)。

Keyword
基本介绍

手动设置字段类型为 keyword

PUT /test3
{
  "mappings": {
    "properties": {
      "tags": {
        "type": "keyword"
      }
    }
  }
}

写入数据

PUT /test3/_doc/1
{
  "tags": "hello world"
}

keyword 其实就是字符串,输入什么,存储就是什么。

适用场景

keyword 适用于 排序、聚合、term(精确查询) 查询场景中。

例如

GET /test3/_search
{
  "query": {
    "term": {
      "tags": {
        "value": "hello"
      }
    }
  }
}
查询优化

有 2 个对查询优化重要的点:

  1. 数字类型(int, long)如果不需要使用范围查询,则建议设置为 keyword
  2. term 查询在 keyword 上的搜索速度总是快于数字类型。
Text
基本介绍

与 keyword 相对的则是 text。在第三章,我们介绍了全文搜索 match 的用法。你可能会好奇,为啥默认写入的数据就可以使用全文搜索。因为当输入是无规则字符串时,字段类型就是 text。(别着急,默认的字段类型,一会我们就会详细介绍)

手动设置字段类型为 text

# 先删除索引
DELETE test3

PUT /test3
{
  "mappings": {
    "properties": {
      "tags": {
        "type": "text"
      }
    }
  }
}
适用场景

text 适用场景:全文搜索

text 字段会对输入进行分词。

例如

PUT /test3/_doc/1
{
  "tags": "hello world"
}

tags 会被分词存储为 hello、world 2个词。

当然,具体被分词为什么,其实跟我们设置的分词器有关(后续讲解,这里先有个概念)。

不适用场景

text 不适用场景:排序、聚合、脚本。

如果你在 text 字段上,进行排序、聚合,或者脚本操作,都会收到以下异常。
Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [name] in order to load field data by uninverting the inverted index. Note that this can use significant memory.

例如:

GET /test3/_search
{
  "sort": [
    {
      "tags": {
        "order": "desc"
      }
    }
  ]
}

# 聚合
GET /test3/_search
{
  "size": 0,
  "aggs": {
    "popular_tags": {
      "terms": {
        "field": "tags"
      }
    }
  }
}

# 脚本操作
GET /test3/_search
{
  "query": {
    "script": {
      "script": "doc['tags'].value == 'hello'"
    }
  }
}
illegal_argument_exception 异常解决方式

要解决该异常,有2种方法

  1. 使用多字段类型,即在该字段上面再建一个 keyword 类型(强烈建议)

    DELETE test3

    PUT test3
    {
    "mappings": {
    "properties": {
    "tags": {
    "type": "text",
    "fields": {
    "keyword": {
    "type": "keyword"
    }
    }
    }
    }
    }
    }

排序、聚合时,则使用 tags.keyword。需要全文索引时,依然可以使用 tags 字段。

GET /test3/_search
{
  "sort": [
    {
      "tags.keyword": {
        "order": "desc"
      }
    }
  ]
}
  1. text 字段上启用 fielddata(不建议!不建议!不建议!)

    DELETE test3

    PUT /test3/_mapping
    {
    "properties": {
    "tags": {
    "type": "text",
    "fielddata": true
    }
    }
    }

PS:在 text 字段上启用 fielddata,会消耗非常大的内存!!!

Date

手动指定字段类型为 date

PUT /test3
{
  "mappings": {
    "properties": {
      "ctime": {
        "type": "date"
      }
    }
  }
}

未指定 format 参数时,默认的值为 strict_date_optional_time||epoch_millis

该默认值接收以下数据

# 秒时间戳
PUT /test3/_doc/1
{
  "ctime": 1721135125
}
# 毫秒时间戳
PUT /test3/_doc/2
{
  "ctime": 1721135125000
}
# datetime
PUT /test3/_doc/3
{
  "ctime":"2024-07-16T12:10:30Z"
}
# date
PUT /test3/_doc/4
{
  "ctime": "2024-07-15"
}

# 对数据排序
GET test3/_search
{
  "sort": { "ctime": "asc"} 
}
format 参数

我们可以手动指定允许的数据格式。例如

PUT /test3
{
  "mappings": {
    "properties": {
      "ctime": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis||epoch_second"
      }
    }
  }
}
存储时间仅为秒时间戳

如果时间为秒时间戳,可以考虑使用 epoch_second

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "ctime": {
        "type":   "date",
        "format": "strict_date_optional_time||epoch_second"
      }
    }
  }
}

PUT /test3/_doc/1
{
  "ctime": 1721135125
}

# 以下查询,时间会被格式化
GET test3/_search
{
  "fields": [ {"field": "ctime"}]
}
数字类型

在复习一下,如果不需要范围查询,建议使用 keyword 存储(后续在进阶篇会讲原理)。

scaled_float

ES 除了支持常见的数字类型。如:long、integer、short、byte、double、float

还针对浮点数,有一个优化的类型 scaled_float

如果我们能够得知我们的浮点数最多有多少个小数点。使用该类型,在空间存储上会比浮点数更好。

PUT /test3
{
  "mappings": {
    "properties": {
      "sf": {
        "type": "scaled_float",
        "scaling_factor": 100
      }
    }
  }
}

上面的意思为:存储时,* 100。即,将浮点数变为整数。

Boolean
PUT /test3
{
  "mappings": {
    "properties": {
      "enable": {
        "type": "boolean"
      }
    }
  }
}
  • false, "false", "" (empty string) 均被认为是 false

  • true, "true" 均被认为是 true

    POST /test3/_doc
    {
    "enable": false
    }
    POST /test3/_doc
    {
    "enable": "false"
    }
    POST /test3/_doc
    {
    "enable": ""
    }
    POST /test3/_doc
    {
    "enable": true
    }
    POST /test3/_doc
    {
    "enable": "true"
    }

    GET /test3/_search
    {
    "query": {
    "term": {
    "enable": {
    "value": false
    }
    }
    }
    }

Object

写入一个 manager 对象

PUT test3/_doc/1
{ 
  "region": "US",
  "manager": { 
    "age":     30,
    "name": { 
      "first": "John",
      "last":  "Smith"
    }
  }
}

在 ES 内部,该文档被索引为一个简单的键值对列表,大致如下

{
  "region":             "US",
  "manager.age":        30,
  "manager.name.first": "John",
  "manager.name.last":  "Smith"
}

例如,我们可以查询 manager.age=30 的文档

GET /test3/_search
{
  "query": {
    "term": {
      "manager.age": {
        "value": 30
      }
    }
  }
}

上述文档的显式映射如下

PUT /test3
{
  "mappings": {
    "properties": { 
      "region": {
        "type": "keyword"
      },
      "manager": { 
        "properties": {
          "age":  { "type": "integer" },
          "name": { 
            "properties": {
              "first": { "type": "text" },
              "last":  { "type": "text" }
            }
          }
        }
      }
    }
  }
}
Array
  1. 不支持混合数据类型的数组

    POST /test3/_doc
    {
    "arr": ["12", 12, false]
    }

  2. 无法查询数组中的每个对象

    PUT test3/_doc/1
    {
    "group" : "fans",
    "user" : [
    {
    "first" : "John",
    "last" : "Smith"
    },
    {
    "first" : "Alice",
    "last" : "White"
    }
    ]
    }

    查询 user.first=Alice & user.last=White。你可能会使用以下写法,但实际上并不能正确工作

    GET test3/_search
    {
    "query": {
    "bool": {
    "must": [
    { "match": { "user.first": "Alice" }},
    { "match": { "user.last": "Smith" }}
    ]
    }
    }
    }

如果你的索引结构是这么设计的,并且有这样的需求,可能需要考虑下如何做优化了。例如,将表铺平。

PS:虽然 nested 嵌套类型可以解决该问题,但开发中会尽可能的把数据结构铺平,从而避免使用 nested 嵌套类型。这里不对 nested 过多介绍,因为开发中真的很不推荐使用。

  1. 开发中仅推荐基本数据类型数组,不推荐对象数组,如果你有第2 点描述的查询需求

    PUT test3
    {
    "mappings": {
    "properties": {
    "arr": {
    "type": "keyword"
    }
    }
    }
    }

    PUT test3/_doc/1
    {
    "arr": ["1", "2", "3"]
    }

    GET test3/_search
    {
    "query": {
    "term": {
    "arr": {
    "value": "1"
    }
    }
    }
    }

相关推荐
小宋102110 分钟前
高性能分布式搜索引擎Elasticsearch详解
大数据·elasticsearch·搜索引擎
不是笨小孩i9 小时前
Git常用指令
大数据·git·elasticsearch
+码农快讯+12 小时前
Git入门学习(1)
git·学习·elasticsearch
码爸16 小时前
java 执行es中的sql
java·sql·elasticsearch
学习使我快乐——玉祥18 小时前
es查询语法
大数据·elasticsearch·搜索引擎
徐*红21 小时前
Elasticsearch 8.+ 版本查询方式
大数据·elasticsearch
码爸21 小时前
flink 例子(scala)
大数据·elasticsearch·flink·scala
txtsteve1 天前
es由一个集群迁移到另外一个集群es的数据迁移
大数据·elasticsearch·搜索引擎
工作中的程序员1 天前
ES 索引或索引模板
大数据·数据库·elasticsearch
Lill_bin1 天前
深入理解ElasticSearch集群:架构、高可用性与数据一致性
大数据·分布式·elasticsearch·搜索引擎·zookeeper·架构·全文检索