Elasticsearch入门(3)-mapping

2,323 阅读3分钟

前言

掘友们好久不见,在之前的文章中,我们了解到了elasticsearch的查找相关操作方法和一些原理,本章节将带来elasticsearch的mapping详细配置和用法,主要分为

  1. dynamic mapping机制
  2. 显示mapping和相关参数
  3. 多字段特性及mapping中自定义analyzer

Dynamic mapping

mapping

  • mapping类似于数据库中的schema的定义,作用如下
    • 定义索引中的字段的名称
    • 定义字段的数据类型,例如字符串,数字,布尔
    • 字段,倒排索引的相关配置(Analyzed or Not Analyzed,Analyzer)
  • Mapping会把JSON文档映射成Lucene所需要的扁平格式
  • 一个Mapping属于一个索引的Type
    • 每个文档都属于一个Type
    • 一个Type有一个Mapping定义
    • 7.0开始,不需要在Mapping定义中指定Type信息

1. 是什么

  • 在写入文档时候,如果索引不存在,会自动创建索引
  • Dynamic Mapping的机制,使得我们无需手动定义Mappings.Elasticsearch会根据文档信息,推算出字段的类型
  • 会遇到推算的不对的情况,例如地理位置信息
  • 当类型设置不对时,会导致一些功能无法正常运行,例如Range查询
# 查看Mapping,get ${indexname}/_mappings
GET movies/_mappings

2. 类型的自动识别

JSON类型 Elasticsearch
字符串 匹配成日期格式,设置成Date
配置数字设置为float或long,该选项默认关闭
设置为text,并且增加keyword子字段
布尔值 boolean
浮点数 float
整数 long
对象 Object
数组 由第一个非空数值的类型所决定
空值 忽略

demo

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

# 查看Mapping文件,查看mapping推算
GET mapping_test/_mapping

查看结果如图,我们可以发现es对输入的json进行了相应的类型转换

dynamic mapping

3. 更改Mapping的字段类型

  • 两种情况下能够修改
    • 新增加字段
      • Dynamic 设置为true时,一旦有新增字段的文档写入,Mapping也同时被更新
      • Dynamic设为false,当有新的文档加入,并且新的文档包含新的字段,Mapping不会被更新,意味着新增字段的数据无法被索引,但是信息会出现在_source中
      • Dynamic设置为Strict,文档写入失败
    • 对已有字段,一旦已有数据写入,就不再支持修改字段定义
      • es基于Lucene实现的倒排索引,一旦生成后,就不允许修改
      • 如果希望改变字段类型,必须Reindex API,重建索引
  • 原因
    • 如果修改了字段的数据类型,会导致已被索引的属于无法被搜索
    • 对于新增字段,不存在该影响(新增字段时建立新字段索引)

4. 控制Dynamic mappings

dynamic设置对应功能

“true" "false" "strict"
文档可索引 ×
字段可索引 × ×
Mapping被更新 × ×
  • 当dynamic被设置为false时候,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃
  • 当设置为Strict模式时候,数据写入直接出错

4.1 true

文档可索引,字段可索引,mapping被更新

# 默认mapping支持dynamic,写入的文档中加入新的字段
PUT dynamic_mapping_test/_doc/1
{
  "newFileld":"someValue" 
}
# 搜索
POST dynamic_mapping_test/_search
{
  "query": {
    "match": {
      "newFileld": "someValue"
    }
  }
}
GET dynamic_mapping_test/_mapping

true mapping

4.2 false

文档可索引,字段不可索引,mapping不更新

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

# 新增 anotherField
PUT dynamic_mapping_test/_doc/2
{
  "anotherField":"anotherValue"
}

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

false mapping

# mapping文件查询
GET dynamic_mapping_test/_mapping

mapping文件

4.3 strict

文档不可索引,字段不可索引,mapping不更新

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

# 写入数据,http code 400
PUT dynamic_mapping_test/_doc/3
{
  "lastField":"lastValue"
}

strict

显示mapping和参数介绍

1. 如何显示定义

PUT docName
{
    “mapping”:{
        # define your mapping here
    }
}

2. 自定义mapping的建议

  • 可以参考API手册,纯手写
  • 为了减少输入的工作量,减少出错概率,可以依照以下步骤
    • 创建一个临时的index,写入一些样本数据
    • 通过访问Mapping API获得该临时文件的动态Mapping定义
    • 修改后使用,参考该配置创建自己的索引
    • 删除临时索引

3. index

  • Index属性:控制当前字段是否被索引,默认为true,如果设置为false,字段不可被索引
PUT users
{
  "mappings":{
    "properties":{
      "firstName":{
        "type":"text"
      },
      "lastName":{
        "type":"text"
      },
      "mobile":{
        "type":"text",
        "index":false
      }
    }
  }
}

index

4. Index Options

对于倒排索引的建立,es提供了四种配置

  • 四种不同级别的Index Options配置,可以控制倒排索引记录的内容
    • docs - 记录doc id
    • freqs - 记录doc id和term frequencies
    • positions - 记录doc id / term frequencies / term postion
    • offsets - 记录 doc id / term frequencies / term position / character offsets
  • Text 类型默认记录positions,其他默认为docs
  • 记录内容越多,占用存储空间越大

5. null_value

有些时候插入的字段时null,如果需要对Null值实现搜素,可以在mapping文件中将“null_value”指定为“NULL”

GET users/_search?q=mobile:NULL

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

空值

注意:

  • 只有Keyword类型支持Null_Value

6. copy_to设置

_all在es7中已被废弃,如要实现 _all 的功能,可用copy_to所替代

# 这个设定的意思就是说,你在索引一个文档时候,它如果包含了firstName和lastName,都会把值拷贝到fullName上,当需要查询的时候,就可以用fullName查询了
PUT users
{
  "mappings":{
    "properties":{
      "firstName":{
        "type":"text",
        "copy_to": "fullName"
      },
      "lastName":{
        "type":"text",
        "copy_to": "fullName"
      }
    }
  }
}

查询时:GET users/_search?q=fullName:(Jack Ma)

copy to

特点:

  • 能满足一些特定的搜索需求
  • copy_to将字段的数值拷贝到目标字段,实现类似_all的作用
  • copy_to的目标字段不出现在_source中

7. 数组类型

  • Elasticsearch中不提供专门的数组类型,但是任何字段,都可以包含多个相同类类型的数值
PUT users/_doc/1
{
    "name":"zhangsan",
    "interests":"reading"
}
PUT users/_doc/2
{
    "name":"lisi",
    "interests":["reading","coding"]
}

添加数据

查看mapping文件,可发现interests类型依然是text

查看mapping

多字段特性及Mapping中配置自定义Analyzer

1. 多字段类型

比如一个默认的text字段,es都会给它加上keyword字段类型,但有时候会给特定字段加上子字段,指定analyzer

  • 多字段特性
    • 厂商名字实现精确匹配
      • 增加一个keyword字段
    • 使用不同的Analyzer
      • 不同语音
      • 拼音字段的搜索
      • 支持为搜索和索引指定不同的analyzer

2. Exact value v.s Full Text

  • Exact value
    • 精确值,包括数据/日期/具体的一个字符串(例如“Apple Store")
    • Elasticsearch中的keyword
  • Full Text
    • 全文本,非结构化的文本数据
    • Elasticsearch中的text

Exact value v.s Full Text

如图,exact value箭头标注的值,对待这种数值,很多情况下我们要将这种数值作为精确值来看待,不需要对它进行分词。

但消息中的message需要对它进行分词处理,以查到一些关键词

2.1 Exact Values不需要被分词

在elasticsearch中,exact value不需要做分词处理

  • Elasticsearch为每一个字段创建一个倒排索引

    • Exact Value在索引时,不需要做特殊的分词处理

exact value

3. 自定义分词

  • 当Elasticsearch自带的分词器无法满足时,可以自定义分词器,通过自组合不同的组件实现
    • Character Filter
    • Tokenizer
    • Token Filter

3.1 Character Filter

  • 在Tokenizer之前对文本进行处理,例如增加删除及替换字符,可以配置多个Character Filters,会影响倒排索引Tokenizer的positon和offset信息

  • 一些自带的Character Filters

    • HTML strip - 去除html标签

      #剥除不必要的html标签
      POST _analyze
      {
        "tokenizer": "keyword",
        "char_filter": ["html_strip"],
        "text":"<b>hello world</b>"
      }
      
    • Mapping - 字符串替换

      # 使用char filter进行替换,将中划线替换为下划线
      POST _analyze
      {
        "tokenizer": "standard",
        "char_filter": [
            {
              "type":"mapping",
              "mappings":["- => _"]
            }
          ],
          "text":"123-456, I-test! test-990 555-789-1114"
      }
      
      # char filter替换表情符号,如把表情替换为单词
      POST _analyze
      {
        "tokenizer": "standard",
        "char_filter": [
            {
              "type":"mapping",
              "mappings":[":) => happy",":( => sad"]
            }
          ],
          "text": ["I am felling :)","Felling :( today"]
      }
      
    • Pattern replace - 正则匹配替换

      # 正则方式替换,替换http
      GET _analyze
      {
        "tokenizer": "standard",
        "char_filter": [
            {
              "type":"pattern_replace",
              "pattern":"http://(.*)",
              "replacement":"$1"
            }
          ],
          "text": "http://www.elastic.co"
      }
      

3.2 Tokenizer

  • 将原始的文本按照一定的规则,切分为词(term or token)

  • elasticsearch内置的Tokenizers包括

    • whitespace/standard/uax_url_email/pattern(正则)/keyword(不做处理,直接索引)/path hierarchy(路径处理)

      # tokenizer按照文件路径
      POST _analyze
      {
        "tokenizer": "path_hierarchy",
        "text": "/usr/jackMa/a/b/c/d/e"
      }
      
  • 可以用java开发插件,实现自己的Tokenizer(期待后续试验····)

3.3 Token filters

  • 将Tokenizer输出的单词(term),进行增加,修改,删除

  • 自带的Token Filters

    • Lowercase / stop / synonym(添加近义词)

      # TOKEN FILTERS
      # whitespace与stop,is去除
      GET _analyze
      {
        "tokenizer": "whitespace",
        "filter": ["stop"],
        "text": ["The stone is knocked"]
      }
      
      
      # remove 加入lowercase后,小写处理,然后The被当成stopword删除
      GET _analyze
      {
        "tokenizer": "whitespace",
        "filter": ["lowercase","stop"],
        "text": ["The man sitting on the Chair is my classmate!"]
      }
      

3.4 设置一个custom Analyzer

创建索引时,定义custom Analyzer

custom Analyzer

#如何自定义Analyzer

# 先删除之前创建的
DELETE my_index
# 可以在创建索引的时候指定setting,首先我们定义分词器,其中的char_filter和tokenizer还有filter中的English_stop都是自定义的
PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer":{
        "type":"custom",
        "char_filter":["emoticons"],
        "tokenizer":"punctuation",
        "filter":[
          "lowercase",
          "english_stop"
          ]
      }
      }, 
      "tokenizer": {
        "punctuation":{
          "type":"pattern",
          "pattern":"[ .,!?]"
        }
      },
      "char_filter": {
        "emoticons":{
          "type":"mapping",
          "mappings":[
              ":) => _happy_",
              ":( => _sad_"
            ]
        }
      },
      "filter": {
        "english_stop":{
          "type":"stop",
          "stopwords":"_english_"
        }
      }
    }
  }
}

针对创建的analyzer进行测试

# 测试自定义的analyzer
POST my_index/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text":"I'm a :) person, and you?"
}

分词结果:

analyzer result

3.4.1 方法论

自定义分析器标准格式是:

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": { ... custom character filters ... },//字符过滤器
            "tokenizer": { ... custom tokenizers ... },//分词器
            "filter": { ... custom token filters ... }, //词单元过滤器
            "analyzer": { ... custom analyzers ... }
        }
    }
}

实例:

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": {
                "&_to_and": {
                    "type": "mapping",
                    "mappings": [ "&=> and "]
            }},
            "filter": {
                "my_stopwords": {
                    "type": "stop",
                    "stopwords": [ "the", "a" ]
            }},
            "analyzer": {
                "my_analyzer": {
                    "type": "custom",
                    "char_filter": [ "html_strip", "&_to_and" ],
                    "tokenizer": "standard",
                    "filter": [ "lowercase", "my_stopwords" ]
            }}
}}}

# 比如自定义好的analyzer名字是my_analyzer,在此索引下的某个新增字段应用此分析器
PUT /my_index/_mapping
{
   "properties":{
        "username":{
             "type":"text",
              "analyzer" : "my_analyzer"
         },
        "password" : {
          "type" : "text"
        }
    
  }
}
# 插入数据
PUT /my_index/_doc/1
{
  "username":"The quick & brown fox ",
   "password":"The quick & brown fox "


}
# username采用自定义分析器my_analyzer,password采用默认的standard分析器
# 验证
GET /index_v1/_analyze
{
  "field":"username",
  "text":"The quick & brown fox"
}

GET /index_v1/_analyze
{
  "field":"password",
  "text":"The quick & brown fox"
}

小结

本篇对elasticsearch的mapping机制进行了大篇幅的介绍和讲解,具体的代码部分不用死机硬背,再实际操练时候,结合 Ctrl+/ 的快捷键,可以快速查看官网的文档,互相结合,理解效率更高。


觉得不错,请点个赞吧

zan