zoukankan      html  css  js  c++  java
  • Elasticsearch实践(三):Mapping

    版本:Elasticsearch 6.2.4。

    Mapping类似于数据库中的表结构定义,主要作用如下:

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

    Mapping完整的内容可以分为四部分内容:

    • 字段类型(Field datatypes)
    • 元字段(Meta-Fields)
    • Mapping参数配置(Mapping parameters)
    • 动态Mapping(Dynamic Mapping)

    自动Mapping

    如果没有手动设置Mapping,Elasticsearch默认会自动解析出类型,且每个字段以第一次出现的为准。

    下面我们先看一下Elasticsearch默认创建的Mapping是什么样的。

    首先我们创建一个索引:

    PUT /user/
    

    查询索引信息:

    GET /user
    

    结果:

    {
      "user": {
        "aliases": {},
        "mappings": {},
        "settings": {
          "index": {
            "creation_date": "1540044686190",
            "number_of_shards": "5",
            "number_of_replicas": "1",
            "uuid": "_K5b8w7jRiuthf7QeQZhdw",
            "version": {
              "created": "5060299"
            },
            "provided_name": "user"
          }
        }
      }
    }
    

    增加一条数据:

    PUT /user/doc/1
    {
      "name":"Allen Yer",
      "job":"php",
      "age":22
    }
    
    PUT /user/doc/2
    {
      "name":"Allen Yer",
      "job":0,
      "age":22
    }
    

    查询数据是否新增成功:

    GET /user/doc/_count
    

    结果:

    {
      "count": 2,
      "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
      }
    }
    

    count为2,说明新增成功。然后我们查询下 mapping :

    {
      "user": {
        "mappings": {
          "doc": {
            "properties": {
              "age": {
                "type": "long"
              },
              "job": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          }
        }
      }
    }
    

    发现自动为每个字段设置了类型:

    • name: text类型,另外额外增加了name.keyword字段,keyword类型;
    • job:text类型,另外额外增加了job.keyword字段,keyword类型;虽然第二次数据新增是数字类型,但还是以第一次为主;
    • age:long类型。

    大家可以把索引删掉,将新增数据调整为先新增第2条,再新增第一条,发现报错了:

    DELETE /user
    
    PUT /user/doc/2
    {
      "name":"Allen Yer",
      "job":0,
      "age":22
    }
    
    PUT /user/doc/1
    {
      "name":"Allen Yer",
      "job":"php",
      "age":22
    }
    

    报错:

    {
      "error": {
        "root_cause": [
          {
            "type": "mapper_parsing_exception",
            "reason": "failed to parse [job]"
          }
        ],
        "type": "mapper_parsing_exception",
        "reason": "failed to parse [job]",
        "caused_by": {
          "type": "number_format_exception",
          "reason": "For input string: "php""
        }
      },
      "status": 400
    }
    

    也能说明以第一次为主以字段第一次的值类型为准。这也说明了默认创建mapping可能不是我们想要的,这就需要手动创建mapping,好处有:

    • 提前指定字段(通过设置甚至可以达到禁止自动增加字段的效果)
    • 合理设置字段类型,防止分词过多或者解析不合理。分词过大会导致磁盘空间占用大。

    手动创建mapping

    这次我们删掉mapping,并手动创建一个:

    DELETE /user
    
    PUT /user/
    {
        "mappings": {
          "doc": {
            "properties": {
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "age": {
                "type": "long",
                "index": false
              },
              "job": {
                "type": "keyword"
              },
              "intro":{
                "type":"text"
              },
              "create_time": {
                "type": "date", 
                "format": "epoch_second"
            }
            }
         }
      }
    }
    

    字段类型说明:

    • name:text类型,会进行分词,支持模糊检索。
    • name.keyword : 这相当于是嵌套了一个字段,keyword类型,只能精确匹配,不支持分词。超过256字符长度不索引,也就没法搜索到。
    • age:long类型,支持精确匹配。
    • job:keyword类型,只能精确匹配,不支持分词。
    • intro:text类型,会进行分词,支持模糊检索。
    • create_time:date类型,支持10位时间戳。

    注意:mapping生成后是不允许修改(包括删除)的。所以需要提前合理的的定义mapping。

    字段类型

    Elasticsearch支持文档中字段的许多不同数据类型:

    普通数据类型

    字符串类型

    textkeyword2种 。其中 text 支持分词,用于全文搜索;keyword 不支持分词,用于聚合和排序。在旧的ES里这两个类型由string表示。

    如果安装了IK分词插件,我们可以为text类型指定IK分词器。一般来说,对于字符串类型,如果:

    1. 模糊搜索+精确匹配,一般是name或者title字段:
    "name": {
            "type": "text",
            "analyzer": "ik_smart",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
    
    1. 模糊搜索,一般是内容详情字段:
    "content": {
            "type": "text",
            "analyzer": "ik_smart"
          }
    
    1. 精确匹配:
    "name": {
            "type": "keyword"
          }
    
    1. 不需要索引:
    "url": {
            "type": "keyword",
            "index": false
          }
    

    数字类型

    支持 long,integer,short,byte,double,float,half_float,scaled_float。具体说明如下:

    • long
      带符号的64位整数,其最小值为-2^63,最大值为(2^63)-1

    • integer
      带符号的32位整数,其最小值为-2^31,最大值为(23^1)-1

    • short
      带符号的16位整数,其最小值为-32,768,最大值为32,767。

    • byte
      带符号的8位整数,其最小值为-128,最大值为127。

    • double
      双精度64位IEEE 754浮点数。

    • float
      单精度32位IEEE 754浮点数。

    • half_float
      半精度16位IEEE 754浮点数。

    • scaled_float
      缩放类型的的浮点数。需同时配置缩放因子(scaling_factor)一起使用。

    对于整数类型(byte,short,integer和long)而言,我们应该选择这是足以使用的最小的类型。这将有助于索引和搜索更有效。

    对于浮点类型(float、half_float和scaled_float),-0.0+0.0是不同的值,使用term查询查找-0.0不会匹配+0.0,同样range查询中上边界是-0.0不会匹配+0.0,下边界是+0.0不会匹配-0.0

    其中scaled_float,比如价格只需要精确到分,price57.34的字段缩放因子为100,存起来就是5734。优先考虑使用带缩放因子的scaled_float浮点类型。

    示例:

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "status": {
              "type": "byte"
            },
            "year": {
              "type": "short"
            },
            "id": {
              "type": "long"
            },
            "price": {
              "type": "scaled_float",
              "scaling_factor": 100
            }
          }
        }
      }
    }
    

    日期类型

    类型为 date

    JSON本身是没有日期类型的,因此Elasticsearch中的日期可以是:

    • 包含格式化日期的字符串。
    • 一个13位long类型表示的毫秒时间戳( milliseconds-since-the-epoch)。
    • 一个integer类型表示的10位普通时间戳(seconds-since-the-epoch)。

    在Elasticsearch内部,日期类型会被转换为UTC(如果指定了时区)并存储为long类型表示的毫秒时间戳。

    日期类型可以使用使用format自定义,默认缺省值:"strict_date_optional_time||epoch_millis"

    "postdate": {
          "type": "date",
          "format": "strict_date_optional_time||epoch_millis"
        }
    

    format 有很多内置类型,这里列举部分说明:

    • strict_date_optional_time, date_optional_time
      通用的ISO日期格式,其中日期部分是必需的,时间部分是可选的。例如 "2015-01-01"或"2015/01/01 12:10:30"。
    • epoch_millis
      13位毫秒时间戳
    • epoch_second
      10位普通时间戳

    其中strict_开头的表示严格的日期格式,这意味着,年、月、日部分必须具有前置0。

    更多日期格式详见:https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-date-format.html#mapping-date-format

    当然也可以自定义日期格式,例如:

    "postdate":{
          "type":"date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
        }
    

    注意:如果新文档的字段的值与format里设置的类型不兼容,ES会返回失败。示例:

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "date": {
              "type": "date",
              "format":"epoch_millis"
            }
          }
        }
      }
    }
    
    PUT my_index/_doc/1
    {
      "date":1543151405000
    }
    PUT my_index/_doc/2
    {
      "date":1543151405
    }
    PUT my_index/_doc/3
    {
      "date":"2018-11-25 21:10:43"
    }
    GET my_index/_doc/_search
    

    第3条数据插入失败,因为只接受长整数的时间戳,字符串类型的日期是不匹配的。第2条的值只有10位数,虽然值是不正确的,但是在epoch_millis的取值范围内,所以也是成功的。

    布尔类型

    类型为 boolean

    二进制类型

    类型为 binary

    范围类型

    integer_range,float_range,long_range,double_range,date_range

    复杂类型

    • 数组数据类型
      在ElasticSearch中,没有专门的数组(Array)数据类型,但是,在默认情况下,任意一个字段都可以包含0或多个值,这意味着每个字段默认都是数组类型,只不过,数组类型的各个元素值的数据类型必须相同。在ElasticSearch中,数组是开箱即用的(out of box),不需要进行任何配置,就可以直接使用。,例如:

    字符型数组: [ "one", "two" ]
    整型数组:[ 1, 2 ]
    数组型数组:[ 1, [ 2, 3 ]] 等价于[ 1, 2, 3 ]

    • 对象数据类型
      object 对于单个JSON对象。JSON天生具有层级关系,文档可以包含嵌套的对象。

    • 嵌套数据类型
      nested 对于JSON对象的数组

    Geo数据类型

    • 地理点数据类型
      geo_point 对于纬度/经度点

    • Geo-Shape数据类型
      geo_shape 对于像多边形这样的复杂形状

    专用数据类型

    • IP数据类型
      ip 用于IPv4和IPv6地址

    • 完成数据类型
      completion 提供自动完成的建议

    • 令牌计数数据类型
      token_count 计算字符串中的标记数

    • mapper-murmur3
      murmur3 在索引时计算值的哈希值并将它们存储在索引中

    • 过滤器类型
      接受来自query-dsl的查询

    • join 数据类型
      为同一索引中的文档定义父/子关系

    多字段

    为不同目的以不同方式索引相同字段通常很有用。例如,string可以将字段映射为text用于全文搜索的keyword字段,以及用于排序或聚合的字段。或者,您可以使用standard分析仪, english分析仪和 french分析仪索引文本字段。

    元字段

    _all

    该字段用于在没有指定具体字段的情况下进行模糊搜索,可以搜索全部字段的内容。

    原理是将所有字段的内容视为字符串,拼在一起放在一个_all字段上,但这个字段默认是不被存储的,可以被搜索。在query_stringsimple_query_string查询(Kibana搜索框用的这种查询方式)默认也是查询_all字段。

    6.x 版本被默认关闭。

    相关设置:

    PUT my_index
    {
      "mappings": {
        "my_type": {
          "_all": {
            "enabled": true,
            "store": false
          },
          "properties": {}
        }
      },
      "settings": {
        "index.query.default_field": "_all" 
      }
    }
    

    上述配置在5.x版本是默认配置:

    • 默认开启 _all 字段
    • 默认不存储 _all 字段
    • 默认搜索 _all 字段

    如果从CPU性能及磁盘空间考虑,可以考虑可以完全禁用或基于每个字段自定义_all字段。

    假设_all字段被禁用,则URI搜索请求、 query_stringsimple_query_string查询将无法将其用于查询。我们可以将它们配置为其他字段:通过定义 index.query.default_field 属性。

    _source

    这个字段用于存储原始的JSON文档内容,本身不会被索引,但是搜索的时候被返回。如果没有该字段,虽然还能正常搜索,但是返回的内容不知道对应的是什么。

    示例:

    GET /user/doc/_search?q=name
    

    结果:

    {
      "took": 4,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
      },
      "hits": {
        "total": 1,
        "max_score": 0.2876821,
        "hits": [
          {
            "_index": "user",
            "_type": "doc",
            "_id": "1",
            "_score": 0.2876821,
            "_source": {
              "name": "this is test name",
              "age": 22,
              "job": "java",
              "intro": "the intro can not be searched by singal",
              "intro2": "去朝阳公园",
              "create_time": 1540047542
            }
          }
        ]
      }
    }
    

    搜索结果就包含_source字段,存储的是原始文档内容。如果被禁用,只知道有匹配内容,但是无法知道返回的是什么。所以需要谨慎关闭该字段。

    如果想禁用该字段,可以在创建Mapping的时候,设置_:

    {
      "mappings": {
        "_doc": {
          "_source": {
            "enabled": false
          }
        }
      }
    }
    

    _type

    ElasticSearch里面有 index 和 type 的概念:index称为索引,type为文档类型,一个index下面有多个type,每个type的字段可以不一样。这类似于关系型数据库的 database 和 table 的概念。

    但是,ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。所以后来ElasticSearch团队想去掉type,于是在6.x版本为了向下兼容,一个index只允许有一个type。

    该字段再在6.0.0中弃用。在Elasticsearch 6.x 版本中创建的索引只能包含单个type。在5.x中创建的含有多个type的索引将继续像以前一样在Elasticsearch 6.x中运行。type 将在Elasticsearch 7.0.0中完全删除。

    详见:https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html

    参考

    1、Mapping | Elasticsearch Reference [6.4] | Elastic
    https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html
    2、Elasticsearch 6.x Mapping设置 - 小旋锋
    https://mp.weixin.qq.com/s/pBPH-wccdY1oslpqGgAHBg
    3、整理的es中的mapping方面的内容 - 辛星,前进的路上. - CSDN博客
    https://blog.csdn.net/xinguimeng/article/details/51588583
    4、[译]ElasticSearch数据类型--string类型已死, 字符串数据永生 - 牧曦之晨 - SegmentFault 思否
    https://segmentfault.com/a/1190000008897731
    5、ElasticSearch的_all域 | 学步园
    https://www.xuebuyuan.com/2053874.html
    6、图解Elasticsearch中的_source、_all、store和index属性 - 1.01^365=37.78 (Lucene、ES、ELK开发交流群: 370734940) - CSDN博客
    https://blog.csdn.net/napoay/article/details/62233031
    7、Elasticsearch - 自动检测及动态映射Dynamic Mapping - 上善若水,水善利万物而不争。 - CSDN博客
    https://blog.csdn.net/xifeijian/article/details/51090338
    8、Field datatypes | Elasticsearch Reference [6.2] | Elastic
    https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-types.html

  • 相关阅读:
    Intellij使用心得(四) -- 导入Eclipse的代码格式化文件
    idea安装插件plugin(主要针对网络连接不上的情况)
    IDEA破解方法以及快捷键大全
    eclipse+maven搭建自己web系统的骨架,解决自带骨架加载无限慢的问题
    用Eclipse创建Maven多模块项目
    Maven实战--- dependencies与dependencyManagement的区别
    redis之有序集合类型(Zset)——排行榜的实现
    做一个完整的Java Web项目需要掌握的技能
    经典面试题:用户反映你开发的网站访问很慢可能会是什么原因
    linux 免密码 使用sudo 直接使用root权限执行命令
  • 原文地址:https://www.cnblogs.com/52fhy/p/10017518.html
Copyright © 2011-2022 走看看