ElasticSearch之Mapping(映射)介绍

408 阅读11分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

一、映射作用

​ 为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成全文本(Full-text)或精确(Exact-value)的字符串值,Elasticsearch需要知道每个字段里面都包含什么数据类型。这些类型和字段的信息存储在映射中。创建索引的时候,可以预先定义字段的类型以及相关属性,相当于定义数据库字段的属性

  • 定义Index下的字段名(Field Name)
  • 定义字段的类型,比如数值型、字符串型、布尔型等
  • 定义倒排索引相关的配置,比如是否索引、记录position等

在 ES 早期版本,一个索引下是可以有多个 Type ,从 7.0 开始,一个索引只有一个 Type,也可以说一个 Type 有一个 Mapping 定义。

二、映射分类

  • 静态映射

    静态映射就是像MySQL一样在创建表的时候对各个字段的属性进行设置。

  • 动态映射

    文档中碰到一个以前没见过的字段时,动态映射可以自动决定该字段的类型,并对该字段添加映射

三、数据类型

  • 字符串型:text、keyword(不会分词)注:在 ES 5.x 之后 string 类型已经不再支持了

  • 数值型:long、integer、short、byte、double、float、half_float、scaled_float等

    数字类型的字段在满足需求的前提下应当尽量选择范围较小的数据类型,字段长度越短,搜索效率越高,对于浮点数,可以优先考虑使用 scaled_float 类型,该类型可以通过缩放因子来精确浮点数,例如 12.34 可以转换为 1234 来存储。

  • 日期类型:date

    格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17时间戳(和 1970-01-01 00:00:00 UTC 的差值),单位毫秒或者秒即使是格式化的日期字符串,ES 底层依然采用的是时间戳的形式存储

    创建日期类型索引

    PUT /range_test3
    {
      "mappings": {
        "properties": {
            "create_date": {
              "type": "date", 
              "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
            }
          }
      }
    }
    
  • 布尔类型:boolean

  • 二进制类型:binary

    二进制类型 binary 接受 BASE64 编码的字符串,默认 store 属性为 false,并且不可以被搜索。

属性名字说明
text用于全文索引,该类型的字段将通过分词器进行分词,最终用于构建索引
keyword不分词,适合简短、结构化字符串,例如主机名、姓名、商品名称等,可以用于过滤、排序、聚合检索,也可以用于精确查询
long有符号64-bit integer:-2^63 ~ 2^63 - 1
integer有符号32-bit integer,-2^31 ~ 2^31 - 1
short有符号16-bit integer,-32768 ~ 32767
byte有符号8-bit integer,-128 ~ 127
double64-bit IEEE 754 浮点数
float32-bit IEEE 754 浮点数
half_float16-bit IEEE 754 浮点数
booleantrue,false
datewww.elastic.co/guide/en/el…
binary该类型的字段把值当做经过 base64 编码的字符串,默认不存储,且不可搜索
  • 范围类型:integer_range、float_range、long_range、double_range、date_range(64-bit 无符号整数,时间戳(单位:毫秒))、ip_range(IPV4 或 IPV6 格式的字符串)

    创建范围索引测试

    PUT /range_test2
    {
      "mappings": {
        "properties": {
            "count": {
              "type": "integer_range"
            },
            "create_date": {
              "type": "date_range", 
              "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
            }
          }
      }
    }
    

    添加数据

    POST /range_test2/_doc/1
    {
      "count": {
        "gte": 1,
        "lte": 100
      },
      "create_date": {
        "gte": "2019-02-01",
        "lte": "2019-03-30"
      }
    }
    

    检索

    其中5 在 1-100之间可以被检索出来

    GET /range_test2/_doc/_search
    {
        "query":{
            "term":{
                "count":5
            }
        }
    }
    
  • 复合类型

    复合类型主要有对象类型(object)嵌套类型(nested)

    • 对象类型

    JSON 字符串允许嵌套对象,一个文档可以嵌套多个、多层对象。可以通过对象类型来存储二级文档,不过由于 Lucene 并没有内部对象的概念,ES 会将原 JSON 文档扁平化,例如文档:

    PUT my_index/_doc/1
    { 
      "region": "US",
      "manager": { 
        "age":     30,
        "name": { 
          "first": "John",
          "last":  "Smith"
        }
      }
    }
    PUT my_index
    {
      "mappings": {
        "properties": {
            "region": {
              "type": "keyword"
            },
            "manager": { 
              "properties": {
                "age":  { "type": "integer" },
                "name": { 
                  "properties": {
                    "first": { "type": "text" },
                    "last":  { "type": "text" }
                  }
                }
              }
           }
        }
      }
    }
    

    其中域manager就是一个对象类型,其中的name是它的子对象。对于对象类型,缺省设置“type”为”object”,因此不用显式定义“type”。

    对于上面的对象类型,ES在索引时将其转换为"manager.age", "manager.name.first" 这样扁平的key,因此查询时也可以使用这样的扁平key作为域来进行查询。

    • 嵌套类型(nested)

      嵌套类型可以看成是一个特殊的对象类型,可以让对象数组独立检索,例如文档

      "user" : [ 
          {
            "first" : "John",
            "last" :  "Smith"
          },
          {
            "first" : "Alice",
            "last" :  "White"
          }
       ]
      

      如果使用动态映射,会被ES索引为如下形式:

      "user.first" : [ "alice", "john" ],
      "user.last" :  [ "smith", "white" ]
      

      这样的索引形式在查询时会丢失对象中”first”与“last”之间的关联关系。 如果将user映射为如下形式:

      "user": {
          "type": "nested" 
      }
      

      ES在索引时会保留对象域之间的关联关系,在查询时找对正确的对象。 如使用如下查询则找不到任何命中对象(不存在“Alice Smith”这个对象):

      {
        "query": {
          "nested": {
            "path": "user",
            "query": {
              "bool": {
                "must": [
                  { "match": { "user.first": "Alice" }},
                  { "match": { "user.last":  "Smith" }} 
                ]
              }
            }
          }
        }
      }
      
  • 地理数据类型

    地理数据类型可用于LBS的应用,包括:

    • geo_point类型:可用于存储某个地理坐标的经纬度。

    • geo_shape类型:用于存储地理多边形形状

      示例如下:

    // location为geo_point类型
    "location": { 
        "lat": 41.12,
        "lon": -71.34
    }
    
  • 特殊数据类型

    • ip类型:用于表示IPv4与IPv6的地址
    • completion类型:提供自动输入关联完成功能,如常见的baidu搜索框。
    • token_count类型:用于计算字符串token的长度,使用时需提供"analyzer"定义。
    • percolate类型:定义为percolate类型的字段会被ES分析为一个查询并保存下来,并可用在后继对文档的查询中。Percolate可以理解为一个预置的查询。
    • alias类型:定义一个已存在域的别名。
    • join类型:该类型定义了文档对象之间的父子关系(可定义多层,形成一颗层次树),即同一索引中多个文档对象可以存在依赖关系,如互联网应用常见的博客文章与回复,问题与回答之间的关系
  • 数组类型

    Elasticsearch不提供专门的数组类型。但任何字段,都可以包含多个相同类型的数值。

    # 数组类型
    PUT users/_doc/1
    {
      "name":"onebird",
      "interests":"reading"
    }
     
    PUT users/_doc/1
    {
      "name":"twobirds",
      "interests":["reading","music"]
    }
     
    POST users/_search
    {
      "query": {
            "match_all": {}
        }
    }
     
    # interests字段还是text类型
    GET users/_mapping
    

四、自定义mapping介绍

创建索引时,自定义mapping类型

PUT /my_store
{
  "mappings": {
    "properties": {
        "price": {
          "type": "integer"
        },
        "productID": {
          "type": "keyword"
        }
      }
  }
}

#查看Mapping映射
GET /my_store/_mappings

Mapping中的字段类型一旦设定后,禁止直接修改,原因如下:

  • Lucene实现的倒排索引生成后不允许修改

重新建立新的索引,然后做reindex(索引重建)操作

允许新增字段

通过dynamic参数来控制字段的新增

  • true(默认)允许自动新增字段
  • false不允许自动新增字段,但是文档可以正常写入,但无法对字段进行查询等操作
  • strict文档不能写入,报错

dynamic属性和索引字段可变性的规则:

truefalsestrict
文档可索引yesyesno
字段可索引yesnono
Mapping被更新yesnono

五、mapping常见参数

JSON是JS对象序列化的字符串,ES接收一个JSON字符串形式的文档对象,本质上是存入一个JS对象,JS定义了对象,数组,字符串,数字,布尔型和null等数据类型。   ES中的域数据类型可视为对JS对象数据类型的扩展,如join,区间类型等都表示为js对象。  在定义域映射时,ES定义了相关的映射参数,这里简单列举并描述,详细信息可以官方文档

04.ElasticSearch支Mapping(映射)介绍01.png

mappings 中field定义选择

"field": {  
    "type":  "text", //文本类型  

    "index": "false"// ,设置成false,字段将不会被索引  

    "analyzer":"ik"//指定分词器  

    "boost":1.23//字段级别的分数加权  

    "doc_values":false//对not_analyzed字段,默认都是开启,analyzed字段不能使用,对排序和聚合能提升较大性能,节约内存,如果您确定不需要对字段进行排序或聚合,或者从script访问字段值,则可以禁用doc值以节省磁盘空间:

    "fielddata":{"loading" : "eager" }//Elasticsearch 加载内存 fielddata 的默认行为是 延迟 加载 。 当 Elasticsearch 第一次查询某个字段时,它将会完整加载这个字段所有 Segment 中的倒排索引到内存中,以便于以后的查询能够获取更好的性能。

    "fields":{"keyword": {"type": "keyword","ignore_above": 256}} //可以对一个字段提供多种索引模式,同一个字段的值,一个分词,一个不分词  

    "ignore_above":100 //超过100个字符的文本,将会被忽略,不被索引

    "include_in_all":ture//设置是否此字段包含在_all字段中,默认是true,除非index设置成no选项  

    "index_options":"docs"//4个可选参数docs(索引文档号) ,freqs(文档号+词频),positions(文档号+词频+位置,通常用来距离查询),offsets(文档号+词频+位置+偏移量,通常被使用在高亮字段)分词字段默认是position,其他的默认是docs  

    "norms":{"enable":true,"loading":"lazy"}//分词字段默认配置,不分词字段:默认{"enable":false},存储长度因子和索引时boost,建议对需要参与评分字段使用 ,会额外增加内存消耗量  

    "null_value":"NULL"//设置一些缺失字段的初始化值,只有string可以使用,分词字段的null值也会被分词  

    "position_increament_gap":0//影响距离查询或近似查询,可以设置在多值字段的数据上火分词字段上,查询时可指定slop间隔,默认值是100  

    "store":false//是否单独设置此字段的是否存储而从_source字段中分离,默认是false,只能搜索,不能获取值  

    "search_analyzer":"ik"//设置搜索时的分词器,默认跟ananlyzer是一致的,比如index时用standard+ngram,搜索时用standard用来完成自动提示功能  

    "similarity":"BM25"//默认是TF/IDF算法,指定一个字段评分策略,仅仅对字符串型和分词类型有效  

    "term_vector":"no"//默认不存储向量信息,支持参数yes(term存储),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 对快速高亮fast vector highlighter能提升性能,但开启又会加大索引体积,不适合大数据量用  
} 

总结:

  • 与域数据格式及约束相关的参数:normalizer,format,ignore_above,ignore_malformed,coerce
  • 与索引相关的参数:index,dynamic,enabled
  • 与存储策略相关的参数:store, fielddata,doc_values
  • 分析器相关参数:analyzer,search_analyzer
  • 其它参数:boost,copy_to,null_value   对于这些参数的描述主要基于笔者的理解,可能有不准确之处。实际上这些参数与ES的实现机制(如存储结构,索引结构密切有关),只能在实际应用中去慢慢体会。

1. analyzer 分词器(重点)和 search_analyzer

PUT /my_index
{
  "mappings": {
    "properties": {
      "text": { 
        "type": "text",
        "fields": {
          "english": { 
            "type":     "text",
            "analyzer": "english",
            "search_analyzer": "english" 
          }
        }
      }
    }
  }
}
#使用_analyze  测试分词器
GET my_index/_analyze 
{
  "field": "text",
  "text": "The quick Brown Foxes."
}
 
GET my_index/_analyze 
{
  "field": "text.english",
  "text": "The quick Brown Foxes."
}

2. index

index,可用于设置字段是否被索引,默认为true,false即为不可搜索。在下述例子中,mobile字段将不能被搜索到。

# index属性控制 字段是否可以被索引
PUT user_test
{
  "mappings": {
    "properties": {
      "firstName":{
        "type": "text"
      },
      "lastName":{
        "type": "text"
      },
      "mobile" :{
        "type": "text",
        "index": false
      }
    }
  }
}

3. index_options参数

index_options的作用是用于控制倒排索引记录的内容,有如下四种配置:

docs:只记录doc id freqs:记录doc id 和term frequencies positions:记录doc id、 term frequencies和term position offsets:记录doc id、 term frequencies、term position、character offsets

text类型的默认配置为positions,其他默认为docs。记录的内容越多,占据的空间越大。

04.ElasticSearch支Mapping(映射)介绍02.png

4. null_value设置

需要对Null值实现搜索时使用。只有keyword类型才支持设定null_value

# 设定Null_value
DELETE users
PUT users
{
    "mappings" : {
      "properties" : {
        "firstName" : {
          "type" : "text"
        },
        "lastName" : {
          "type" : "text"
        },
        "mobile" : {
          "type" : "keyword",
          "null_value": "NULL"
        }
      }
    }
}
 
PUT users/_doc/1
{
  "firstName":"Zhang",
  "lastName": "Fubing",
  "mobile": null
}
 
PUT users/_doc/2
{
  "firstName":"Zhang",
  "lastName": "Fubing2"
}
 
# 查看结果,有且仅有_id为2的记录
GET users/_search
{
  "query": {
    "match": {
      "mobile":"NULL"
    }
  }
}

5.copy_to

这个属性用于将当前字段拷贝到指定字段。

  1. _all在7.x版本已经被copy_to所代替
  2. 可用于满足特定场景
  3. copy_to将字段数值拷贝到目标字段,实现类似_all的作用
  4. copy_to的目标字段不出现在_source中
DELETE user_test
 
#设置 Copy to
PUT user_test
{
  "mappings": {
    "properties": {
      "firstName":{
        "type": "text",
        "copy_to": "fullName"
      },
      "lastName":{
        "type": "text",
        "copy_to": "fullName"
      }
    }
  }
}
 
PUT user_test/_doc/1
{
  "firstName":"Ruan",
  "lastName": "Yiming"
}
 
POST user_test/_search?q=fullName:(Ruan Yiming)

六、精确值和全文本

精确值(Exact Values) vs 全文本(Full Text)

精确值:包括数字、日期、具体的字符串(如“192.168.0.1”) Elasticsearch中类型为keyword,索引时,不需要做特殊的分词处理 全文本:非结构化的文本数据 Elasticsearch中类型为text,索引时,需要对其进行分词处理 如下结构的数据,我们可以大致判断出哪些是精确值,哪些是全文本。其中的200、info、debug都是精确值。而message的内容为全文本。

{
    "code": 200,
    "message": "this is a error item, you can change your apollo config !",
    "content": {
        "tags": [
            "info",
            "debug"
        ]
}

符:

参考文章链接