美文网首页
Elasticsearch——mapping

Elasticsearch——mapping

作者: 小波同学 | 来源:发表于2020-11-02 01:36 被阅读0次

    Mapping简介

    mapping 是用来定义文档及其字段的存储方式、索引方式的策略,主要作用如下:

    • 定义index下的字段名。
    • 定义字段类型,比如数值型、浮点型、布尔型等。
    • 定义倒排索引相关的设置,比如是否索引、记录position等。

    也就是说映射决定了Elasticsearch在建立倒排索引、进行检索时对文档采取的相关策略,如数字类型、日期类型、文本类型等等。

    需要注意的是:检索时用到的分析策略,要和建立索引时的分析策略相同,否则将导致数据不准确。

    ES对不同的类型有不同的存储和检索策略

    full text 全文检索

    • 比如: 对full text型的数据类型(如text),在索引时,会经过各类处理 (包括分词、normalization(时态转换、单复数的转换、同义词转换、大小写转换、缩写)等处理),才会建立到索引数据中,更深入的话NPL自然语义处理。

    exact value 精确匹配

    • 再比如: 对exact value(如date),在索引的分词阶段,会将整个value作为一个关键词建立到倒排索引中。

    Mapping Type

    每个索引都拥有唯一的 mapping type,用来决定文档将如何被索引。mapping type由下面两部分组成

    • Meta-fields
      元字段用于自定义如何处理文档的相关元数据。 元字段的示例包括文档的_index,_type,_id和_source字段。

    • Fields or properties
      映射类型包含与文档相关的字段或属性的列表。

    数据类型

    核心数据类型

    • 字符串型:text、keyword
    • 数值型:long、integer、short、byte、double、float、half_float、scaled_float
    • 日期类型:date
    • 布尔类型:boolean
    • 二进制类型:binary
    • 范围类型:integer_range、float_range、long_range、double_range、date_range

    复杂数据类型

    • 数组类型:array
    • 对象类型:object
    • 嵌套类型:nested object

    地理位置数据类型

    • geo_point
    • geo_shape

    专用类型

    • 记录:ip
    • 实现自动补全:completion
    • 记录分次数:token_count
    • 记录字符串hash值:murmur3
    • percolator
    • join

    多字段特性 multi-fields

    允许对同一个字段采用不同的配置,比如分词,常见例子如对人名实现拼音搜索,只需要在人名中新增一个字段为pinyin即可


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

    针对同一字段支持多种字段类型可以更好地满足我们的搜索需求,例如一个string类型的字段可以设置为text来支持全文检索,与此同时也可以让这个字段拥有keyword类型来做排序和聚合,另外我们也可以为字段单独配置分词方式,例如"analyzer": "ik_max_word"

    text 类型

    text类型的字段用来做全文检索,例如邮件的主题、淘宝京东中商品的描述等。这种字段在被索引存储前先进行分词,存储的是分词后的结果,而不是完整的字段。text字段不适合做排序和聚合。如果是一些结构化字段,分词后无意义的字段建议使用keyword类型,例如邮箱地址、主机名、商品标签等。

    常有参数包含以下

    • analyzer:用来分词,包含索引存储阶段和搜索阶段(其中查询阶段可以被search_analyzer参数覆盖),该参数默认设置为index的analyzer设置或者standard analyzer。
    • index:是否可以被搜索到,默认是true。
    • fields:Multi-fields允许同一个字符串值同时被不同的方式索引,例如用不同的analyzer使一个field用来排序和聚类,另一个同样的string用来分析和全文检索。
    • search_analyzer:这个字段用来指定搜索阶段时使用的分词器,默认使用analyzer的设置。
    • search_quote_analyzer:搜索遇到短语时使用的分词器,默认使用search_analyzer的设置。
    • store:是否在source之外存储,每个文档索引后会在ES中保存一份原始文档,存放在source中,一般情况下不需要设置store为true,因为在source中已经有一份原始文档了。

    keyword 类型

    keyword用于索引结构化内容(例如电子邮件地址,主机名,状态代码,邮政编码或标签)的字段,这些字段被拆分后不具有意义,所以在es中应索引完整的字段,而不是分词后的结果。

    通常用于过滤(例如在博客中根据发布状态来查询所有已发布文章),排序和聚合。keyword只能按照字段精确搜索,例如根据文章id查询文章详情。如果想根据本字段进行全文检索相关词汇,可以使用text类型。

    PUT my_index
    {
      "mappings": {
        "properties": {
          "tags": {
            "type":  "keyword"
          }
        }
      }
    }
    

    常有参数包含以下

    • index:是否可以被搜索到,默认是true。
    • fields:Multi-fields允许同一个字符串值同时被不同的方式索引,例如用不同的analyzer使一个field用来排序和聚类,另一个同样的string用来分析和全文检索。
    • null_value:如果该字段为空,设置的默认值,默认为null。
    • ignore_above:设置索引字段大小的阈值。该字段不会索引大小超过该属性设置的值,默认为2147483647,代表着可以接收任意大小的值。但是这一值可以被PUT Mapping Api中新设置的ignore_above来覆盖这一值。

    date类型

    支持排序,且可以通过format字段对时间格式进行格式化。
    json中没有时间类型,所以在es在规定可以是以下的形式:

    • 一段格式化的字符串,例如"2015-01-01"或者"2015/01/01 12:10:30"
    • 一段long类型的数字,指距某个时间的毫秒数,例如1420070400001
    • 一段integer类型的数字,指距某个时间的秒数

    object类型

    mapping中不用特意指定field为object类型,因为这是它的默认类型。

    json类型天生具有层级的概念,文档内部还可以包含object类型进行嵌套。例如:

    PUT my_index/_doc/1
    { 
      "region": "US",
      "manager": { 
        "age":     30,
        "name": { 
          "first": "John",
          "last":  "Smith"
        }
      }
    }
    

    在es中上述对象会被按照以下的形式进行存储:

    {
      "region":             "US",
      "manager.age":        30,
      "manager.name.first": "John",
      "manager.name.last":  "Smith"
    }
    

    对象数组:

    {
        "authors": [
            {"age": 26 , "name": "jock white"},
            {"age": 55 , "name": "tom jones"},
            {"age": 39 , "name": "kitty smith"}
        ]
    }
    

    对象数组在es中的存储格式:

    {
        "authors.age": [26, 55, 39],
        "authors.name": [jock, white, tom, jones, kitty, smith]
    }
    

    mapping可以对不同字段进行不同的设置

    PUT my_index
    {
      "mappings": {
        "properties": { 
          "region": {
            "type": "keyword"
          },
          "manager": { 
            "properties": {
              "age":  { "type": "integer" },
              "name": { 
                "properties": {
                  "first": { "type": "text" },
                  "last":  { "type": "text" }
                }
              }
            }
          }
        }
      }
    }
    

    nest类型

    nest类型是一种特殊的object类型,它允许object可以以数组形式被索引,而且数组中的某一项都可以被独立检索。

    而且es中没有内部类的概念,而是通过简单的列表来实现nest效果,例如下列结构的文档:

    PUT my_index/_doc/1
    {
      "group" : "fans",
      "user" : [ 
        {
          "first" : "John",
          "last" :  "Smith"
        },
        {
          "first" : "Alice",
          "last" :  "White"
        }
      ]
    }
    

    上面格式的对象会被按照下列格式进行索引,因此会发现一个user中的两个属性值不再匹配,alice和white失去了联系

    {
      "group" :        "fans",
      "user.first" : [ "alice", "john" ],
      "user.last" :  [ "smith", "white" ]
    }
    

    range类型

    支持以下范围类型:

    类型 范围
    integer_range -2的31次2的31次-1.
    float_range 32位单精度浮点数
    long_range -2的63次2的63次-1.
    double_range 64位双精度浮点数
    date_range unsigned 64-bit integer milliseconds
    ip_range ipv4和ipv6或者两者的混合

    使用范例为:

    PUT range_index
    {
      "settings": {
        "number_of_shards": 2
      },
      "mappings": {
        "properties": {
          "age_range": {
            "type": "integer_range"
          },
          "time_frame": {
            "type": "date_range", 
            "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
          }
        }
      }
    }
    
    PUT range_index/_doc/1?refresh
    {
      "age_range" : { 
        "gte" : 10,
        "lte" : 20
      },
      "time_frame" : { 
        "gte" : "2015-10-31 12:00:00", 
        "lte" : "2015-11-01"
      }
    }
    
    注:term是查询时对关键字不分词,keyword是索引时不分词。

    自定义Mapping

    自定义Mapping的api如下:


    • Mapping中的字段类型一旦设定后,禁止直接修改,原因是:
      • Lucence实现的倒排索引生成后不允许修改,
    • 除非重新建立新的索引,然后做reindex操作。
    • 允许新增字段。
    • 通过dynamic参数来控制字段的新增:
      • true(默认)允许自动新增字段。
      • false不允许自动新增字段,但是文档可以正常写入,但无法对字段进行查询等操作。
      • strict 文档不能写入,报错。

    copy_to

    index

    • 控制当前字段是否索引,默认为true,即记录索引,false不记录,即不可搜索。
    • 当身份证号和手机号这样的敏感信息被搜索的时候,可以设置为false,这样就可以不用建立倒排索引节省空间。

    index_options

    • index_options用于控制倒排索引记录的内容,有如下4中配置:

      • 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。
    • 记录内容越多,占用空间越大。

    index_options的设定如下所示:


    null_value

    • 当字段遇到null值时的处理策略,默认为null,即空值,此时es会忽略该值。可以通过设定该值设定字段的默认值。

    dynamic Mapping

    es可以自动识别文档字段类型,从而降低用户使用成本,如下所示:


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

    JSON类型 ES类型
    null 忽略
    boolean boolean
    浮点类型 float
    整数 long
    object object
    array 由第一个非null值得类型决定
    string 匹配为日期,则设为date类型(默认开启)
    匹配为数字的话设为float或long类型(默认关闭)
    设为text类型,并附带keyword的子字段

    日期的自动识别可以自行配置日期格式,以满足各种需求

    • 默认是["strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"]
    • strict_date_optional_time是ISO datetime的格式,完整格式类似如下面:
      • YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
    • dynamic_date_formats 可以自定义日期类型
    • date_detection可以关闭日期自动识别的机制

    字符串是数字时,默认不会自动识别为整型,因为字符串中出现数字时完全合理的

    • numeric_detection可以开启字符串中数字的自动识别,如下所示:


    Dynamic Template

    允许根据es自动识别的数据类型、字段名等来动态设定字段类型,可以实现如下效果:

    • 所有字符串类型都设定为keyword类型,即默认不分词
    • 所有以message开头的字段都设定为text类型,即分词
    • 所有以long_开头的字段都设定为long类型
    • 所有自动匹配为double类型的都设定为float类型,以节省空间

    匹配规则一般有如下几个参数:

    • match_mapping_type匹配es自动识别的字段类型,如boolean,long,string等
    • match,unmatch匹配字段名
    • path_match,path_unmatch匹配路径
    以message开头的字段都设定为text类型
    double类型的都设定为float类型,节省空间

    自定义mapping的建议

    自定义Mapping的操作步骤如下:

    • 1、写入一条文档到es的临时索引中,获取es自动生成的mapping
    • 2、修改步骤1得到的mapping,自定义相关配置
    • 3、使用步骤2的mapping创建实际所需索引

    索引模板

    英文为Index Template,主要用于在新建索引时自动应用预先设定的配置,简化索引创建的操作步骤。

    • 可以设定索引的配置和mapping。
    • 可以有多个模板,根据order设置,order大的覆盖小的配置。

    type底层结构及弃用原因

    type是什么

    type,是一个index中用来区分类似的数据的,类似的数据,但是可能有不同的fields,而且有不同的属性来控制索引建立、分词器.

    field的value,在底层的lucene中建立索引的时候,全部是opaque bytes类型,不区分类型的。

    lucene是没有type的概念的,在document中,实际上将type作为一个document的field来存储,即type,es通过type来进行type的过滤和筛选。

    es中不同type存储机制

    一个index中的多个type,实际上是放在一起存储的,因此一个index下,不能有多个type重名,而类型或者其他设置不同的,因为那样是无法处理的。

    {
       "goods": {
            "mappings": {
                "electronic_goods": {
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "price": {
                            "type": "double"
                        },
                        "service_period": {
                            "type": "string"
                        }            
                    }
                },
                "fresh_goods": {
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "price": {
                            "type": "double"
                        },
                        "eat_period": {
                            "type": "string"
                        }
                    }
                }
            }
        }
    }
    
    PUT /goods/electronic_goods/1
    {
        "name": "小米空调",
        "price": 1999.0,
        "service_period": "one year"
    }
    
    PUT /goods/fresh_goods/1
    {
        "name": "澳洲龙虾",
        "price": 199.0,
        "eat_period": "one week"
    }
    

    es文档在底层的存储是这样子的

    {
        "goods": {
            "mappings": {
                "_type": {
                    "type": "string",
                    "index": "false"
                },
                "name": {
                    "type": "string"
                }
                "price": {
                    "type": "double"
                }
                "service_period": {
                    "type": "string"
                },
                "eat_period": {
                    "type": "string"
                }
            }
        }
    }
    

    底层数据存储格式

    {
        "_type": "electronic_goods",
        "name": "小米空调",
        "price": 1999.0,
        "service_period": "one year",
        "eat_period": ""
    }
    {
        "_type": "fresh_goods",
        "name": "澳洲龙虾",
        "price": 199.0,
        "service_period": "",
        "eat_period": "one week"
    }
    

    type弃用原因

    • 同一索引下,不同type的数据存储其他type的field 大量空值,造成资源浪费。
    • 不同类型数据,要放到不同的索引中。
    • es9中,将会彻底删除type。

    分词器Analyzer

    作用:切分词语,normalization提升recall召回率。
    recall,召回率:搜索的时候,增加能够搜索到的结果的数量。

    分词器是Elasticsearch中专门处理分词的组件,英文为Analyzer,其组成如下:

    • Character Filters
      针对原始文本进行处理,比如去除html特殊标记符。

    • Tokenizer
      将原始文本按照一定规则切分为单词。

    • Token Filters
      针对Tokenizer处理的单词进行在加工,比如转小写,删除或新增等处理。
      stop word,停用词:了、的、呢等。

    一个分词器很重要,将一段文本进行各种处理,最后处理好的结果才会拿去建立倒排索引。

    测试分词:

    GET /_analyze
    {
      "analyzer":"standard",
      "text":"Text to analyze 70"
    }
    

    返回值:

    {
      "tokens" : [
        {
          "token" : "text",
          "start_offset" : 0,
          "end_offset" : 4,
          "type" : "<ALPHANUM>",
          "position" : 0
        },
        {
          "token" : "to",
          "start_offset" : 5,
          "end_offset" : 7,
          "type" : "<ALPHANUM>",
          "position" : 1
        },
        {
          "token" : "analyze",
          "start_offset" : 8,
          "end_offset" : 15,
          "type" : "<ALPHANUM>",
          "position" : 2
        },
        {
          "token" : "70",
          "start_offset" : 16,
          "end_offset" : 18,
          "type" : "<NUM>",
          "position" : 3
        }
      ]
    }
    

    token:实际存储term关键字。
    position:在此词条原文本中的位置。
    start_offset/end_offset:字符在原始字符串中的位置。

    分词器最佳实践

    因为后续的keywordtext设计分词问题,这里给出分词最佳实践。即索引时用ik_max_word,搜索时分词器用ik_smart,这样索引时最大化的将内容分词,搜索时更精确的搜索到想要的结果。

    例如我想搜索的是小米手机,我此时的想法是想搜索出小米手机的商品,而不是小米音响、小米洗衣机等其他产品,也就是说商品信息中必须只有小米手机这个词。

    参考:
    https://www.cnblogs.com/haixiang/p/12040272.html

    https://www.cnblogs.com/shoufeng/p/10648835.html

    相关文章

      网友评论

          本文标题:Elasticsearch——mapping

          本文链接:https://www.haomeiwen.com/subject/cmrpvktx.html