Fork me on GitHub

ElasticSearch| mapping

 

Type可以理解为关系型数据库的Table,那每个字段的数据类型是如何定义的呢?

实际上每个Type中的字段是什么数据类型,由mapping定义,如果我们在创建Index的时候,没有设定mapping,系统会自动根据一条数据的格式来推断出该数据对应的字段类型,

mapping除了自动定义,还可以手动定义,但是只能对新加的、没有数据的字段进行定义,一旦有了数据就无法再做修改了。

实际上每个type中的字段是什么数据类型,由mapping定义。

但是如果没有设定mapping系统会自动,根据一条数据的格式来推断出应该的数据格式。

作用:

定义数据库中的表的结构的定义,通过mapping来控制索引存储数据的设置

  1. 定义Index下的字段名(Field Name)
  2. 定义字段的类型,比如数值型、字符串型、布尔型等
  3. 定义倒排索引相关的配置,比如documentId、记录position、打分等

获取索引mapping; 不进行配置时,自动创建的mapping

请求: GET /movie_index/_mapping

响应: key-word是分词类型

 

1. Dynamic Mapping与常见字段

Mapping类似数据库中的schema的定义,作用如下:

  • 定义索引中字段的名称;
  • 定义字段的数据类型,例如字符串,数字,布尔...
  • 字段,倒排索引的相关配置,(Analyzed  or  Not Analyzed, Analyzer)

Mapping会把JSON文档映射成Lucene所需要的扁平格式;

一个Mapping属于一个索引的Type

  • 每个文档都属于一个Type
  • 一个Type有一个Mapping定义
  • 7.0开始,不需要在Mappiing定义中指定type信息

字段的数据类型

① 简单类型

  • Text / Keyword
  • Date
  • Integer / Floating
  • Boolean
  • IPv4 & IPv6

② 复杂类型 - 对象和嵌套对象

  对象类型 / 嵌套类型

③ 特殊类型

  geo_point & geo_shape / percolator 

Dynamic Mapping

  • 在写入文档时候,如果索引不存在,会自动创建索引;
  • Dynamic Mapping的机制,使得我们无需手动定义Mappings,Elaticsearch会自动根据文档信息,推算出字段的类型;
  • 但是有时候会推算的不对,例如地理位置信息;
  • 当类型如果设置不对时,会导致一些功能无法正常运行,例如Range查询。

 

 

   

类型的自动识别

  

具体推断类型如下:

  • true/false → boolean
  • 1020  →  long
  • 20.1 → float
  • “2018-02-01” → date
  • “hello world” → text +keyword

 默认只有text会进行分词,keyword是不会分词的字符串。

 

ES 依靠json文档字段类型来实现自动识别字段类型,支持的类型

  mapping中的字段类型一旦设定后,禁止修改

原因:Lucene实现的倒排索引生成后不允许修改(提高效率)

 如果要修改字段的类型,需要从新建立索引,然后做reindex操作

dynamic设置

  1. true:允许自动新增字段(默认的配置)
  2. False:不允许自动新增字段,但是文档可以正常写入,无法对字段进行查询操作
  3. strict:文档不能写入(如果写入会报错)

Index属性,控制当前字段是否索引,默认为true,即记录索引,false不记录,即不可以搜索,比如:手机号、身份证号等敏感信息,不希望被检索

例1:
#写入文档,查看 Mapping
PUT mapping_test/_doc/1
{
  "firstName":"Chan",
  "lastName": "Jackie",
  "loginDate":"2018-07-24T10:29:48.103Z"
}


#查看 Mapping文件, 将firstName字符串推断为了text,并加了一个keyword;把loginDate字符串推断为了一个Date
GET mapping_test/_mapping

#Delete index
DELETE mapping_test

例2:
#dynamic mapping,推断字段的类型
PUT mapping_test/_doc/1
{
    "uid" : "123",
    "isVip" : false,
    "isAdmin": "true",
    "age":19,
    "heigh":180
}

#查看 Dynamic,将uid,isAdmin等字符推断为了text并加了一个keyword,将isVip布尔推断为了boolean,将age、height推断为了long类型
GET mapping_test/_mapping

当插入一个文档时,如果索引不存在,ES会自动根据文档的类型生成一个mapping文件;

能否更改Mapping的字段类型

两种情况:

  ① 新增加字段

      • Dynamic设为true,一旦有新增字段的文档写入,Mapping也同时被更新;
      • Dynamic设为false,Mapping不会被更新,意味着新增字段的数据无法被索引,但是信息会出现在_source中。
      • Dynamic设为Strict,文档写入失败

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

      • Lucene实现的倒排索引,一旦生成后,就不允许修改。

  如果希望改变字段类型,必须Reindex API,重建索引。

原因:

  如果修改了字段的数据类型,会导致已被索引的属于无法被搜索;

  但是如果是增加新的字段,就不会有这样的影响。

控制Dynamic Mappings| true flase strict 

  

出现一个新的文档(包含新的字段),设为true时,文档可以被索引进ES,字段可以被索引搜索到,Mapping也会被更新;其他情况见上面表格。

当Dynamic设为false时候,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃;

当Dynamic设为Strict模式时候,数据写入直接出错。

#----------------------默认Mapping支持dynamic,写入的文档中加入新的字段----------------------
PUT dynamic_mapping_test/_doc/1
{
  "newField":"someValue"
}

#该字段可以被搜索,数据也在_source中出现
POST dynamic_mapping_test/_search
{
  "query":{
    "match":{
      "newField":"someValue"
    }
  }
}

#------------------------------------修改为dynamic false-------------------------
PUT dynamic_mapping_test/_doc/_mapping
{
  "dynamic": false
}

#新增 anotherField
PUT dynamic_mapping_test/_doc/10
{
  "anotherField":"someValue"
}


#该字段不可以被搜索,因为dynamic已经被设置为false
POST dynamic_mapping_test/_search
{
  "query":{
    "match":{
      "anotherField":"someValue"
    }
  }
}

#查看索引的Mapping文件, 并没有刚刚新新增的字段anotherField
GET dynamic_mapping_test/_mapping 
#查看新增的字段出现在_source中
GET dynamic_mapping_test/_doc/10 



#----------------------------------修改为strict------------------------------------
PUT dynamic_mapping_test/_doc/_mapping
{
  "dynamic": "strict"
}

#写入数据出错,HTTP Code 400
PUT dynamic_mapping_test/_doc/12
{
  "lastField":"value"
}

DELETE dynamic_mapping_test

 

2. 显示Mapping设置与常见参数

自定义Mapping的建议

① 可以参考API手册,纯手写;

② 为了减少输入的工作量,减少出错概率,可以依照以下步骤:

  • 创建一个临时的index,写入一些样本数据
  • 通过访问Mapping API获得该临时文件的动态Mapping定义
  • 修改后佣,使用该配置创建你的索引
  • 删除临时索引

控制当前字段是否被索引

例如用户的手机号就可以设置为false,节省磁盘的开销

 

 

常见参数

Index Options

 

DELETE users

#将mobile的 index 值设置为false 
PUT users
{
    "mappings" : {
      "doc":{
      "properties" : {
        "firstName" : {
          "type" : "text"
        },
        "lastName" : {
          "type" : "text"
        },
        "mobile" : {
          "type" : "text",
          "index": false
        }
      }
    }
  }
}

PUT users/doc/1
{
  "firstName":"Ruan",
  "lastName": "Yiming",
  "mobile": "12345678"
}

#查询报错 "reason": "Cannot search on field [mobile] since it is not indexed." 
POST /users/doc/_search
{
  "query": {
    "match": {
      "mobile":"12345678"
    }
  }
}

 

null_value

 

 

#设定Null_value

DELETE users
PUT users
{
    "mappings" : {
      "doc":{
      "properties" : {
        "firstName" : {
          "type" : "text"
        },
        "lastName" : {
          "type" : "text"
        },
        "mobile" : {
          "type" : "keyword",
          "null_value": "NULL"
        }
      }
    }
  }
}

PUT users/doc/1
{
  "firstName":"Ruan",
  "lastName": "Yiming",
  "mobile": null
}

#在_source中搜索到mobie的值为null 
GET users/_search
{
  "query": {
    "match": {
      "mobile":"NULL"
    }
  }

}

 

copy_to设置

#设置 Copy to
DELETE users
PUT users
{
  "mappings": {
    "doc": {
      "properties": {
      "firstName":{
        "type": "text",
        "copy_to": "fullName"
      },
      "lastName":{
        "type": "text",
        "copy_to": "fullName"
      }
    }
  }
 }
}
PUT users/doc/1
{
  "firstName":"Ruan",
  "lastName": "Yiming"
}
# 查询,但是并没有fullName这个字段,使用了copy_to;但是 _source中并不存在fullName,它只在查询中可以指定 
GET users/_search?q=fullName:(Ruan Yiming)

POST users/_search
{
  "query": {
    "match": {
       "fullName":{
        "query": "Ruan Yiming",
        "operator": "and"
      }
    }
  }
}

 

数组类型

#数组类型
PUT users/doc/1
{
  "name":"onebird",
  "interests":"reading"
}
#更新文档 
PUT users/doc/1
{
  "name":"twobirds",
  "interests":["reading","music","sleeping"]
}
##"interests" : [
#            "reading",
#            "music",
#            "sleeping"
#          ]

POST users/_search
{
  "query": {
        "match_all": {}
    }
}
#查看mapping看到interests的类型 "type" : "text" 还是text 
GET users/_mapping

多字段类型

为索引设置mapping文件时,可以增加一个子字段,比如text类型的,都会默认添加一个keyword的字段;

多字段属性,可以为特定字段如用英文的方式进行分词,增加子字段指定用拼音的方式进行分词。

Index Template 

集群上的索引会越来越多,例如,为日志每天创建一个索引;

使用多个索引可以更好的管理数据,提高性能;

  • logs-2019-05-01
  • logs-2019-05-02
  • log-2019-05-03

Index Templates - 帮助你设定Mappings和Settings,并按照一定的规则,自动匹配到新创建的索引之上。

  • 模板仅在一个索引被创建时,才会产生作用。修改模板不会影响已创建的索引;
  • 可以设定多个索引模板,这些设置会被“merge” 在一起;
  • 可以指定“order” 的数值,控制“merging” 的过程。

两个Index Templates

左边的索引模是为数据各设置一个分片一个副本;

右边的索引模板是只要以 test开头的索引主分片数1个,二个副本,(如果字符串符合日期格式的就转换为date类型,在这个模板中把这个功能关闭,同时如果数字的探索设为true)

 

 Index Template的工作方式

当一个索引被新创建时

  • 应用Elasticsearch默认的settings和mappings
  • 应用order数值低的Index Template中的设定
  • 应用order高的Index Template中的设定,之前的设定会被覆盖
  • 应用创建索引时,用户指定的Settings和Mappings,并覆盖之前模板中的设定。
#数字字符串被映射成text,日期字符串被映射成日期
PUT ttemplate/_doc/1
{
    "someNumber":"1",
    "someDate":"2019/01/01"
}
# # someNumber字符类型,推断为text, 外加一个keyword; someDate 字符日期类型会推断为date类型  GET ttemplate
/_mapping
{
  "ttemplate" : {
    "mappings" : {
      "_doc" : {
        "properties" : {
          "someDate" : {
            "type" : "date",
            "format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
          },
          "someNumber" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}
View Code
#Create a default template
PUT _template/template_default
{
  "index_patterns": ["*"],
  "order" : 0,
  "version": 1,
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas":1
  }
}


PUT /_template/template_test
{
    "index_patterns" : ["test*"],
    "order" : 1,
    "settings" : {
        "number_of_shards": 1,
        "number_of_replicas" : 2
    },
    "mappings" : {
      "_doc": {
        "date_detection": false,
        "numeric_detection": true
      }
    }
}

#查看template信息
GET /_template/template_default

#通过通配符查看所有的template 
GET /_template/temp*

#写入新的数据,index以test开头
PUT testtemplate/_doc/1
{
    "someNumber":"1",
    "someDate":"2019/01/01"
}
#可以看到日期字符型的类型变成了 text;someNumber "1"字符型数字 变成long
GET testtemplate/_mapping

GET testtemplate/_settings


#创建一个索引, 查看是否匹配到索引模板了 
PUT testmy
{
    "settings":{
        "number_of_replicas":5
    }
}
##往索引中插入一个文档 ,看number_of_replicas是几个
PUT testmy/_doc/1
{
  "key":"value"
}

# 发现匹配到了template_test,  "number_of_replicas" : "5" overwrite了
GET testmy/_settings 

 

Dynamic Template

前边index template应用于所有的index上面,dynamic template应用于具体的索引上面。

根据Elasticsearch识别的数据类型,结合字段名称,来动态设定字段类型

  • 所有的字符串类型都设定成keyword,或者关闭keyword字段
  • is开头的字段都设置成boolean
  • long_开头的都设置成long类型

Dynamic Template 

 

 匹配规则参数

 

 

#Dynaminc Mapping 根据类型和字段名
DELETE my_index
 
PUT my_index/_doc/1
{
  "firstName":"Ruan",
  "isVIP":"true"
}
##自动推断firstName为text  keyword, isVIP为 text   keyword
GET my_index/_mapping
DELETE my_index

PUT my_index
{
  "mappings": {
    "_doc": {
      "dynamic_templates": [
            {
        "strings_as_boolean": {
          "match_mapping_type":   "string",
          "match":"is*",
          "mapping": {
            "type": "boolean"
          }
        }
      },
      {
        "strings_as_keywords": {
          "match_mapping_type":   "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
 }
}

PUT my_index/_doc/1
{
  "firstName":"Ruan",
  "isVIP":"true"
}
##匹配模板, firstName为keyword, isVIP为 boolean
GET my_index/_mapping

DELETE my_index

#结合路径, 设置一个新的template, 把name底下除了middle 都会把值copy到full_name上去,text类型的
PUT my_index
{
  "mappings": {
    "_doc": {
       "dynamic_templates": [
      {
        "full_name": {
          "path_match":   "name.*",
          "path_unmatch": "*.middle",
          "mapping": {
            "type":       "text",
            "copy_to":    "full_name"
          }
        }
      }
    ]
  }
 }
}


PUT my_index/_doc/1
{
  "name": {
    "first":  "John",
    "middle": "Winston",
    "last":   "Lennon"
  }
}
#检索文档 ,在full_name上查询就可以查询到相应的结果了
GET my_index/_search?q=full_name:John

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-01-24 16:47  kris12  阅读(141)  评论(0编辑  收藏  举报
levels of contents