概念

Mapping(映射)用来定义文档包含的字段名、字段数据类型以及文档如何存储和索引这些字段的规则

显式映射 & 动态映射

显式映射

显式映射以完全控制字段的存储和索引方式。
显式映射的意义:

  • 哪些字符串字段应该作为全文字段(text)处理。
  • 哪些字段包含数字、日期或地理位置。
  • 日期值的格式。
动态映射

当Elasticsearch在文档中检测到新字段时,会自动确定字段的数据类型,并自动把新字段添加到映射的过程称为动态映射。

ES映射类型

简单数据类型

字符串
text:会分词,不支持聚合
keyword:不会分词,将全部内容作为一个词条,支持聚合、排序
数值
long、integer、short、byte、double、float、half_float、scaled_float
布尔
类型值boolean。接受true、false值,也支持字符串"true"、"false" 或空字符串"",空字符串表示false。在聚合操作时,返回boolean类型使用1和0作为key,使用字符串“true”和“false”作为key_as_string。
二进制
类型值binary。接收一个Base64编码的二进制值。该类型定义的字段不可搜索,且默认不存储。
范围类型
integer_range、float_range、long_range、double_range、date_range
日期

JSON层次结构的类型

数组:[]
对象:{}

"mappings": {
    "properties": {
      "id":{
        "type":"long"
      },
      "name":{
        "properties": {
          "firstname":{"type":"keyword"},
          "lastname":{"type":"keyword"}
        }
      }
    }
  }

nested:直观的说Nested实际上就是Object的数组,但与对象数组实际存储方式又不同,下面有介绍
flattened
join为同一索引中的文档定义父子关系

特定类型

range
ip
version
geo_shape
completion

映射操作API

添加映射

PUT index7/_mapping
{
  "properties":{
    "name":{
      "type":"keyword"
    },
    "age":{
      "type":"integer"
    },
    "description":{
      "type":"text",
      "analyzer": "ik_max_word"
    }
  }
}

查询映射

GET index7/_mapping

添加字段

PUT index7/_mapping
{
  "properties":{
    "classid":{
      "type":"integer"
    },
    "description":{
      "type":"text"
    }
  }
}

Mapping参数

映射参数(mapping parameters)用于字段类型的映射,每个字段类型有一个或多个映射参数搭配使用。

analyzer

analyzer参数指定一个用于索引text字段时的文本分析的分析器,如果search_analyzer参数没有覆盖,该分析器也用于搜索时的文本分析。
只有text字段支持analyzer参数。

search_analyzer

查询时使用指定的分析器,默认情况下,搜索时将使用参数analyzer定义的分析器,从而保持与索引时一样的分析器

boost

增强匹配权重,默认1.0。通过设置boost参数的值,改变相关性得分的计算,进而可以影响单个字段相比于其他字段在查询时自动提升。如:title的匹配权重大于content匹配权重

PUT my_index_01
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "boost": 2
      },
      "content": {
        "type": "text"
      }
    }
  }
}
coerce

默认true。表示尝试清除脏数据值,以符合字段的数据类型,例如,如果字段数据类型为整数类型,那么输入的字符串强制转换为数字,浮点数将被截断。如果coerce参数设置为false,那么遇到脏数据不会进行强制转换,而是直接抛出异常。
通过配置参数index.mapping.coerce,可以进行索引级别的禁用coercion,类型级别的coercion作用优先级大于索引级别

PUT my_index_01
{
  "settings": {
    "index.mapping.coerce": false
  },
  "mappings": {
    "properties": {
      ...
    }
  }
}
copy_to

将多个字段的值复制到组合字段中。然后可以将这个组合字段作为单个字段进行查询。如果经常搜索多个字段,可以通过copy_to参数来搜索更少的字段,进而提高搜索速度,如:first_name和last_name,复制到full_name

PUT my_index_01
{
  "mappings": {
    "properties": {
      "first_name": {
        "type": "text",
        "copy_to": "full_name" 
      },
      "last_name": {
        "type": "text",
        "copy_to": "full_name" 
      },
      "full_name": {
        "type": "text"
      }
    }
  }
}
dynamic

参数dynamic,可以在文档和对象级别控制动态映射,参数dynamic可以设置为下列三种值:
(1)true:将新的检测到的字段添加到映射中。(默认)
(2)false:忽略新检测到的字段。这些字段将不会被索引,因此不能被搜索,但仍然会出现在返回命中的_source字段中。这些字段将不会添加到映射中,新的字段必须显式添加。
(3)strict:如果检测到新字段,则抛出异常并拒绝索引文档。新字段必须显式地添加到映射中。

fields

fields参数用于提供多字段。使得字段具有多种类型的功能。fields不会修改原始字段_source的值。

PUT my_index_01
{
  "mappings": {
    "properties": {
      "city": {
        "type": "text",
        "fields": {
          "raw": { 
            "type":  "keyword"
          }
        }
      }
    }
  }
}
PUT my_index_01/_doc/1
{"city":"New York"}
PUT my_index_01/_doc/2
{"city":"York"}
GET my_index_01/_search
{
  "query": {
    "match": {
      "city": "york" 
    }
  },
  "sort": {
    "city.raw": "asc" 
  },
  "aggs": {
    "Cities": {
      "terms": {
        "field": "city.raw" 
      }
    }
  }
}

多字段用于多分析器,示例如下:

PUT my_index_01
{
  "mappings": {
    "properties": {
      "text": { //1
        "type": "text",
        "fields": {
          "english": { //2
            "type":     "text",
            "analyzer": "english"
          }
        }
      }
    }
  }
}

PUT my_index_01/_doc/1
{ "text": "quick brown fox" } 
PUT my_index_01/_doc/2
{ "text": "quick brown foxes" } 
GET my_index_01/_search
{
  "query": {
    "multi_match": {
      "query": "quick brown foxes",
      "fields": [ //3
        "text",
        "text.english"
      ],
      "type": "most_fields" //3
    }
  }
}

注释1:text字段使用standard分析器。
注释2:text.english字段使用english分析器。
注释3:通过text和text.english字段合并得分。

format

通过format参数设置日期格式,参数值需要符合日期格式语法。自定义格式操作如下:

PUT my_index_01
{
  "mappings": {
    "properties": {
      "date": {
        "type": "date",
        "format": "yyyy-MM-dd"
      }
    }
  }
}
ignore_above

超过长度的字符串内容将不会被索引

properties

类型映射、object字段和nested字段包含的子字段称为属性(properties)。这些属性可以是任何数据类型,包括object和nested。属性可以在通过下列方式添加:
(1)通过在创建索引时显式地定义它们。
(2)通过在使用PUT Mapping API添加或更新映射类型时显式地定义它们。
(3)动态地索引包含新字段的文档。
创建索引时显式地定义示例如下:

PUT my_index_01
{
  "mappings": {
    "properties": { 
      "manager": {
        "properties": { 
          "age":  { "type": "integer" },
          "name": { "type": "text"  }
        }
      },
      "employees": {
        "type": "nested",
        "properties": { 
          "age":  { "type": "integer" },
          "name": { "type": "text"  }
        }
      }
    }
  }
}
store

单独存储属性值。默认对字段值进行索引以使其可搜索,但不单独存储它们,但是已存储在_source字段中
默认情况下,字段值被索引以使其可搜索,但不会被存储。这意味着可以查询字段,但不能检索原始字段值。通常这并不重要。字段值已经是_source字段的一部分,该字段默认存储。 如果您只想检索单个字段或几个字段的值,而不是整个_source的值,则可以使用source filtering来实现。
在某些情况下,文本的内容可能过大,我们不想将所有文本内容存储在_source字段,但有些字段内容又希望能被查找到,在这种情况下,可以使用store参数。例如,如果有一个有标题的文档,一个日期,和一个非常大的内容字段,我们只想要获取标题和日期内容,但又不想要内容字段,操作示例如下:

PUT my_index_01
{
  "mappings": {
    "_source": {
      "enabled": false
    },
    "properties": {
      "title": {
        "type": "text",
        "store": true 
      },
      "date": {
        "type": "date",
        "store": true 
      },
      "content": {
        "type": "text"
      }
    }
  }
}

ignore_malformed
index
index_options
index_phrases
index_prefixes
meta
norms
null_value
position_increment_gap
similarity
term_vector
doc_values
eager_global_ordinals
enabled
fielddata
search_quote_analyzer

其他

multi-field
multi_field 多域类型允许你对同一个值以映射的方式定义成多个基本类型 core_types . 这个非常有用,比如,如果你定义一个 string 类型的字段,你需要这个字段的分词一会是 analyzed ,但是有时候又希望该字段是 not_analyzed 类型的,通过使用 multi_field 就可以很方便的解决这个问题. 下面来看个例子:

{
    "tweet" : {
        "properties" : {
            "name" : {
                "type" : "multi_field",
                "fields" : {
                    "name" : {"type" : "string", "index" : "analyzed"},
                    "untouched" : {"type" : "string", "index" : "not_analyzed"}
                }
            }
        }
    }
}

上面的例子,显示了我们是如何定义一个名为 name 的字段, 它的数据类型是 string 字符类型, 该字段映射了两次(实际物理上产生了2个索引字段),其中一个是以 name 的名称 定义为 analyzed 分词类型,另外一个定义成了名称为 untouched 的 not_analyzed 类型,即不分词处理.
字段访问
当使用 multi_field mapping定义之后, fields里面的和字段名称和外部的字段名称相同的字段定义会被当做该mult-field的默认字段(因为一个multi类型字段会被拆分成多个字段,所以,会有一个默认值),我们可以通过直接名称 name 或者使用 tweet.name 格式的方式来指定字段.
其它定义的不同名称的字段也可以通过使用特点的导航来指定(即使用“.”符合来分割),如: name.untouched, 或者还带上类型名称 tweet.name.untouched.

Nested和Object List有什么区别?
ES原生支持Object类型,也就是任意字段都可以是个对象,而ES又是所有字段都是多值,也就是都可以是list。那么在ES中Nested和Object List又是什么关系呢?
这就要从Object说起了。Object虽然是个对象,但是实际存储时是在当前文档里打平存储的。如上那个例子,如果只有一个user,那么在真实索引中实际上是下面这样的

{
  "user.first" : "John",
  "user.last" : "Smith"
}

而如果是个list,那么就成了

{
  "user.first" : ["John","Alice"],
  "user.last" : ["Smith","White"]
}

因为建索引时打平,因此检索时ES就无法知道到底是John Smith还是John White了。因此引入了Nested结构。
Nested将list里的每个doc单独变成子文档进行存储,因此在查询时就可以知道具体的结构信息了。
Nested因为是单独的子文档存储,因此在使用时,直接用 a.b.c 是无法访问的,需要将其套在nested查询里。除此之外,和其他的查询并无差异。
Nested案例:

PUT /my_index_01
{
    "mappings" : {
        "properties" : {
            "my_object" : {
                "type" : "nested"
            }
        }
    }
}
PUT /my_index_01/_doc/1
{
    "my_object" : [
      {"name": "blue", "count": 10},
      {"name": "red", "count": 1},
      {"name": "yellow", "count": 20}
    ]
}
# Nested查询
GET /my_index_01/_search
{
    "query":  {
        "nested" : {
            "path" : "my_object",
            "query" : {
                "bool" : {
                    "must" : [
                      { "match" : {"my_object.name" : "blue"} },
                      { "range" : {"my_object.count" : {"gt" : 5}} }
                    ]
                }
            },
            "score_mode" : "avg"
        }
    }
}

参考:
https://www.jianshu.com/p/6b42a242c704