Elasticsearch——mapping

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" : "",
      "position" : 0
    },
    {
      "token" : "to",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "",
      "position" : 1
    },
    {
      "token" : "analyze",
      "start_offset" : 8,
      "end_offset" : 15,
      "type" : "",
      "position" : 2
    },
    {
      "token" : "70",
      "start_offset" : 16,
      "end_offset" : 18,
      "type" : "",
      "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)