Elasticsearch从入门到放弃:瞎说Mapping

前面咱们聊了 Elasticsearch 的索引、搜索和分词器,今天再来聊另外一个基础内容—— Mapping。web

Mapping 在 Elasticsearch 中的地位至关于关系型数据库中的 schema,它能够用来定义索引中字段的名字、定义字段的数据类型,还能够用来作一些字段的配置。从 Elasticsearch 7.0开始,Mapping 中不在意须要定义 type 信息了,具体缘由能够看官方的解释。数据库

字段的数据类型

咱们刚刚提到 Mapping 中能够定义字段的数据类型,这多是 Mapping 最经常使用的功能了,因此咱们先来看看 Elasticsearch 都支持哪些数据类型。数组

  • 简单类型:text、keyword、date、long、double、boolean、ip
  • 复杂类型:对象类型、嵌套类型
  • 特殊类型:用于描述地理位置的 geo_point、geo_shape

Elasticsearch 支持的数据类型远不止这些,因为篇幅缘由,这里就不一一列举了。我找几个工做中常见的来介绍一下。微信

首先就是字符串了,Elasticsearch 中的字符串有 text 和 keyword 两种。其中 text 类型的字符串是能够被全文检索的,它会被分词器做用,session

PUT my_index
{
  "mappings": {
    "properties": {
      "full_name": {
        "type":  "text"
      }
    }
  }
}

在设置字段类型为 text 时,还能够利用一些参数对这个字段进行更进一步的定制。app

index:标记这个字段是否能被搜索,默认是 true编辑器

search_analyzer:被搜索时所使用的分词器,默认使用 setting 中设置的分词器flex

fielddata:字段是否容许在内存中进行排序、聚合,默认是 falseurl

meta:关于字段的一些元数据spa

像一些id、邮箱、域名这样的字段,咱们就须要使用 keyword 类型了。由于 keyword 类型能够支持排序、聚合,而且只能支持精确查询。

有些同窗可能会把 ID 设置为数字类型,这也是没问题的,数字类型和 keyword 各有各的好处,使用数字类型能够进行范围查找,而使用 keyword 类型则有更高的查询效率。具体用哪一种还要看使用场景。

日期类型在 Elasticsearch 中有三种表现形式

  1. 能够格式化成日期类型的字符串,如 "2020-07-26""2015/01/01 12:10:30"这样的
  2. 毫秒级时间戳用 long 类型表示
  3. 秒级时间戳用 integer 类型表示

在 Elasticsearch 内部,日期类型是以 long 类型的毫秒级时间戳存储的,时区使用的是0时区。

咱们能够自定义时间格式,默认使用的是strict_date_optional_time||epoch_millis

「strict_date_optional_time_nanos」是通用的日期格式解析,至少要包含年份,若是要包含时间,则用T分隔,例如yyyy-MM-dd'T'HH:mm:ss.SSSSSSZyyyy-MM-dd

若是想要同时支持多种日期格式,可使用format字段

PUT my_index
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format""yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

Mapping参数

刚才咱们提到配置 Mapping 的日期格式的参数format,Mapping 还提供了不少其余的参数。

  • analyzer
  • boost
  • coerce
  • copy_to
  • doc_values
  • dynamic
  • eager_global_ordinals
  • enabled
  • fielddata
  • fields
  • format
  • ignore_above
  • ignore_malformed
  • index_options
  • index_phrases
  • index_prefixes
  • index
  • meta
  • normalizer
  • norms
  • null_value
  • position_increment_gap
  • properties
  • search_analyzer
  • similarity
  • store
  • term_vector

咱们来介绍几个经常使用的字段。

fields

首先是fields,它可使同一个字段经过不一样的方式实现不一样的目的。

例如,咱们能够对一个字符串字段设置为text类型,用于全文检索,同时能够利用fields设置为keyword类型,用于排序和聚合。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "city": {
        "type""text",
        "fields": {
          "raw": {
            "type":  "keyword"
          }
        }
      }
    }
  }
}

查询时咱们就可使用city进行全文检索,使用city.raw进行排序和聚合。

GET my-index-000001/_search
{
  "query": {
    "match": {
      "city""york" 
    }
  },
  "sort": {
    "city.raw""asc" 
  },
  "aggs": {
    "Cities": {
      "terms": {
        "field""city.raw" 
      }
    }
  }
}

enabled

有些时候,咱们只想把某个字段做为数据存储来使用,并不须要用来作搜索,这时,咱们就能够将这个字段禁用掉,字段被禁用之后,它所保存的值也不受 mapping 指定的类型控制。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "user_id": {
        "type":  "keyword"
      },
      "last_updated": {
        "type""date"
      },
      "session_data": { 
        "type""object",
        "enabled"false
      }
    }
  }
}

上面的例子中,咱们禁用掉了 session_data 这个字段,这时,你既能够往 session_data 字段中存储 JSON 格式的数据,也能够存储非 JSON 格式的数据。

除了针对于单个字段的禁用之外,咱们还能够直接禁用掉整个 mapping。咱们来从新建立一个index

PUT my-index-000002
{
  "mappings": {
    "enabled"false 
  }
}

这时,文档全部的字段都不会被索引,只是用来存储。

须要注意的是,不管是具体字段中仍是整个 mapping 的 enabled 属性都不能够被修改,由于一旦设置为 false,Elasticsearch 就不会对字段进行索引了,也不会校验数据的合法性,若是产生了脏数据之后再设置为 true,就会形成程序错误。

null_value

null 在 Elasticsearch 中是不能够被索引或搜索的,这里咱们所说的 null 并非狭义上某种语言的 null,而是全部的空值。例如全部值都是 null 的数组,总之,这里的定义就是没有值。

对于有须要搜索空值的业务怎么办呢?Elasticsearch 为咱们提供了 null_value 这个参数,它能够指定一个值,搜索时使用这个值来替代空值。

举个栗子

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "status_code": {
        "type":       "keyword",
        "null_value""NULL" 
      }
    }
  }
}

咱们给 status_code 字段设置了 null_value"NULL"。这里须要注意, null_value 的类型必须与要查找的数据类型相同,若是在这个例子中 status_code 的类型是long,那么就不能把null_value 设置为 "NULL"

dynamic

对于新增长的字段:

  • dynamic 设置为 true 时,一旦有新增字段的文档写入,Mapping 也会被更新
  • dynamic 设置为 false 时,Mapping 不会被更新,新增字段没法被索引,但信息会出如今 _source
  • dynamic 设置为 strict 时,文档写入失败

对于已有的字段,一旦已经有数据写入,就再也不支持修改字段定义

Dynamic Mapping

咱们在建立索引时,能够不用手动写 Mappings, Elasticsearch 会帮咱们自动识别出字段的类型。咱们称之为 Dynamic Mapping。不过有时推算的可能不是很准确。

Elasticsearch 自动识别类型是基于 JSON 的。数据类型的对应关系以下(表格来自 elastic 官网)

「JSON data type」 「Elasticsearch data type」
null No field is added.
true or false boolean field
floating point number float field
integer long field
object object field
array Depends on the first non-null value in the array.
string Either a date field (if the value passes date detection), a double or long field (if the value passes numeric detection) or a text field, with a keyword sub-field.

Elasticsearch 支持的字段映射的数据类型在这个文档中,除了这些,其余的类型映射都须要显示的指定了。

关于日期类型,默认是能够映射的,可是 Elasticsearch 只能识别几种格式的日期yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis。若是关掉了 date_detection 开关,那么就只能识别为字符串了。

PUT my-index-000001
{
  "mappings": {
    "date_detection"false
  }
}

固然,你也能够根据须要本身指定要识别的日期格式,只须要使用 dynamic_date_formats 参数便可。

PUT my-index-000001
{
  "mappings": {
    "dynamic_date_formats": ["MM/dd/yyyy"]
  }
}

Elasticsearch 还提供了一种把字符串型的数字识别为数字的能力,它是由 numeric_detection 开关控制的。

PUT my-index-000005
{
  "mappings": {
    "numeric_detection"true
  }
}

PUT my-index-000005/_doc/1
{
  "my_float":   "1.0"
  "my_integer""1" 
}

在这个例子中,my_float 会被识别为 float 类型,而 my_integer 会被识别为 long 类型。

Dynamic template

dynamic template 容许咱们自定义 mapping ,并应用到具体索引上。dynamic template 的定义通常是这样的

  "dynamic_templates": [
    {
      "my_template_name": { 
        ...  match conditions ... 
        "mapping": { ... } 
      }
    },
    ...
  ]

my_template_name 能够是任意字符串。

match conditions 包括match_mapping_type, match, match_pattern, unmatch, path_match, path_unmatch 这几种。

mapping 就是指匹配到的字段应该使用怎样的 mapping。下面咱们介绍几种 match conditions

match_mapping_type

咱们先来看一个简单的例子

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "integers": {
          "match_mapping_type""long",
          "mapping": {
            "type""integer"
          }
        }
      },
      {
        "strings": {
          "match_mapping_type""string",
          "mapping": {
            "type""text",
            "fields": {
              "raw": {
                "type":  "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}

这里咱们有两个模版,其一是使用 integer 类型来代替 long 类型,其二是将字符串类型映射为 keyword

match 和 unmatch

这两个比较简单,match 是指匹配到模式的字段, unmatch 是表示不匹配的字段。

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "longs_as_strings": {
          "match_mapping_type""string",
          "match":   "long_*",
          "unmatch""*_text",
          "mapping": {
            "type""long"
          }
        }
      }
    ]
  }
}

在这个例子中,咱们须要的是 long_ 开头的字符串,不须要 _text结尾的字符串字段。

除了以上三种以外,其余的就是 match_pattern 用来进行正则匹配,path_matchpath_unmatch 则是表示字段所在路径的是否匹配。

另外 dynamic template 还支持两种变量替换,分别是 {name}{dynamic_type}。其实 name 就是字段名,dynamic_type 就是检测出的字段类型。

总结

关于 Elasticsearch 的 mapping 咱们就先聊这些,我认为 mapping 的配置是一个须要经验的事情,当你处理的 case 愈来愈多以后,就能比较轻松的知道如何更好的配置 mapping 了。此外,mapping 的许多字段和参数文中都没有涉及,对于我而言,大部分都是用到了现查文档,不过也仍是建议你们看一看文档,起码遇到问题时能知道大概查找文档的一个方向。这样就会比身边人强很多。

往期精彩回顾
Elasticsearch从入门到放弃:索引基本使用方法
Elasticsearch从入门到放弃:文档CRUD要牢记
Elasticsearch从入门到放弃:分词器初印象
Elasticsearch从入门到放弃:再聊搜索


本文分享自微信公众号 - 代码洁癖患者(Jackeyzhe2018)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索