【ES】ElasticSearch 映射(mapping)

[TOC]

一、定义

映射(mapping)即是模式定义(schema definition)。一个映射定义了字段类型,每个字段的数据类型,以及字段被 Elasticsearch 处理的方式。映射还用于设置关联到类型上的元数据。

可以说,映射就是对索引库中索引的字段名称及其数据类型进行定义,类似于 mysql 中的表结构信息。

映射可以分为动态映射和显式映射。

1.1、动态映射 (dynamic mapping)

在关系数据库中,需要事先创建数据库,然后在该数据库实例下创建数据表,然后才能在该数据表中插入数据。而 ElasticSearch中 不需要事先定义映射(Mapping),文档写入 ElasticSearch 时,会根据文档字段自动识别类型,这种机制称之为动态映射。

# 动态映射:如果 mapping 没有创建,elasticsearch 将会根据写入的数据的 key 自行创建相应的 mapping,并写入数据
curl -H "Content-Type: application/json" -XPUT localhost:9200/sports/basketball/123 -d '
{
    "date": "2018-05-20",
    "title": "Cavaliers",
    "content": "They are real Cavaliers tonigiht",
    "author_id": 520
}'

# 可以使用 _mapping 查询索引的 mapping
curl -XGET localhost:9200/sports/basketball/_mapping?pretty
# 返回结果
{
  "sports" : {
    "mappings" : {
      "basketball" : {
        "properties" : {
          "author_id" : {
            "type" : "long"
          },
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "date" : {
            "type" : "date"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

1.2、显式映射(explicit mappings)

在 ElasticSearch 中也可以事先定义好映射,包含文档的各个字段及其类型等,这种方式称之为显式映射。

# 静态映射,手动设置
curl -H "Content-Type: application/json" -XPUT localhost:9200/books?pretty -d '
{
    "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 0
    },
    "mappings": {
        "novel": {
            "properties": {
                "title": {
                    "type": "text"
                },
                "name": {
                    "type": "text",
                    "analyzer": "standard",
                    "search_analyzer": "standard"
                },
                "publish_date": {
                    "type": "date"
                },
                "price": {
                    "type": "double"
                },
                "number": {
                    "type": "integer"
                }
            }
        }
    }
}'

ES 通过猜测来确定字段类型,动态映射将会很有用,但如果需要对某些字段添加特殊属性(如:定义使用其它分词器、是否分词、是否存储等),就必须显式映射

二、数据类型及支持属性

2.1、核心类型(Core datatype)

字符串string,string 类型包含 textkeyword。ELasticsearch 5.X 之后的字段类型不再支持 string,由 text 或 keyword 取代。

  • text:当一个字段是要被全文搜索的,比如 Email 内容、产品描述,应该使用 text 类型。设置 text 类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text 类型的字段不用于排序,很少用于聚合(termsAggregation 除外)。
  • keyword:该类型不需要进行分词,可以被用来检索过滤、排序和聚合,keyword 类型的字段只能通过精确值搜索到(不可用 text 分词后的模糊检索)。

整数byte, short, integer, long

浮点数float, double

布尔boolean

日期date,JSON 本身并没有日期数据类型,在 ES 中的日期类型可以是:

  • 类似 2015-01-01 or 2015/01/01 12:10:30 的字符串
  • long 类型的毫秒级别的时间戳
  • int 类型的秒级别的时间戳
  • 日期类型默认会被转换为 UTC 并且转换为毫秒级别的时间戳的 long 类型存储。日期类型如果不指定 format ,将会以默认格式表示。

二进制型:`binary``,二进制类型以 Base64 编码方式接收一个二进制值,二进制类型字段默认不存储,也不可搜索。

text 类型以外,其他类型必须要进行精确查询,因为除 text 外其他类型不会进行分词。

# 新增
curl -H "Content-Type: application/json" -XPOST localhost:9200/books/novel/1 -d '
{
    "name": "being alive",
    "number": "1000",
    "price": 17.99,
    "publish_date": "2018-05-20",
    "title": "being Live"
}'
# 新增
curl -H "Content-Type: application/json" -XPOST localhost:9200/books/novel/2 -d '
{
    "name": "baby",
    "number": "100",
    "price": 27.99,
    "publish_date": "2018-05-16",
    "title": "a girl is a baby"
}'


# date 类型不会进行分词,必须精确查询,此时查询不出数据
curl -XGET localhost:9200/books/novel/_search?q=publish_date:2018
# 返回结果
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 0,
        "max_score": null,
        "hits": []
    }
}

# date 类型不会进行分词,必须精确查询,此时可以查询出数据
curl -XGET localhost:9200/books/novel/_search?q=publish_date:2018-05-20
# 返回结果
{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 1,
        "hits": [
            {
                "_index": "books",
                "_type": "novel",
                "_id": "1",
                "_score": 1,
                "_source": {
                    "name": "being alive",
                    "number": "1000",
                    "price": 17.99,
                    "publish_date": "2018-05-20",
                    "title": "being Live"
                }
            }
        ]
    }
}

# text 会进行分词,此时可以查出数据
curl -XGET localhost:9200/books/novel/_search?q=title:girl
# 返回结果
{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "books",
                "_type": "novel",
                "_id": "2",
                "_score": 0.2876821,
                "_source": {
                    "name": "baby",
                    "number": "100",
                    "price": 27.99,
                    "publish_date": "2018-05-16",
                    "title": "a girl is a baby"
                }
            }
        ]
    }
}

2.2、复合类型(Complex datatypes)

2.2.1、对象

JSON 文档是有层次结构的,一个文档可能包含其他文档,如果一个文档包含其他文档,那么该文档值是对象类型,其数据类型是对象,ES 默认把文档的属性 type设置为 object,即 "type":"object"。

在 ES 内部,"对象" 被索引为一个扁平的键值对。

curl -H "Content-Type: application/json" -XPOST localhost:9200/archive/person/1?pretty -d '
{
    "region": "US",
    "manager": {
        "age": 30,
        "name": {
            "first": "John",
            "last": "Smith"
        }
    }
}

默认情况下,上述文档类型被索引为以点号命名的数据结构,把层次结构展开之后,数据结构是由扁平的key/value对构成:

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

2.2.2、数组

在 ElasticSearch 中,没有专门的数组(Array)数据类型,是开箱即用的(out of box),不需要进行任何配置,就可以直接使用。

在默认情况下,任意一个字段都可以包含 0 或多个值,这意味着每个字段默认都是数组类型,只不过,数组类型的各个元素值的数据类型必须相同:

  • 字符串数组: [ "one", "two" ]
  • 整型数组: [ 1, 2 ]
  • 数组数组: [ 1, [ 2, 3 ]] which is the equivalent of [ 1, 2, 3 ]
  • 对象数组: [ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }]

注意:

  • 动态添加数据时,数组的第一个值的类型决定整个数组的类型
  • 混合数组类型是不支持的,比如:[1,”abc”]
  • 数组可以包含 null 值,空数组 [ ] 会被当做 missing field 对待

对象数组,在 ES 内部将会被转换为 "多值" 的扁平数据类型:

curl -H "Content-Type: application/json" -XPOST localhost:9200/lib/club/1?pretty -d '
{
    "group": "fans",
    "user": [
        {
            "first": "John",
            "last": "Smith"
        },
        {
            "first": "Alice",
            "last": "White"
        }
    ]
}'
# 在 es 内部将转为
{
    "group": "fans",
    "user.first": [
        "alice",
        "john"
    ],
    "user.last": [
        "smith",
        "white"
    ]
}

这将导致每个数组元素(对象)内部的字段关联性丢失:

curl -H "Content-Type: application/json" -XGET localhost:9200/lib/club/_search?pretty -d '
{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user.first": "John"
                    }
                },
                {
                    "match": {
                        "user.last": "White"
                    }
                }
            ]
        }
    }
}'

字段 user.first 和 user.last 被展开成数组字段,但是,这样展开之后,单个文档内部的字段之间的关联就会丢失,在该例中,展开的文档数据丢失 first 和 last 字段之间的关联,比如,Alicewhite 的关联就丢失了。

# 文档被命中,显然,John Smith 、Alice White 两组字段它们内在的关联性都丢失了
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "lib",
        "_type" : "club",
        "_id" : "1",
        "_score" : 0.5753642,
        "_source" : {
          "group" : "fans",
          "user" : [
            {
              "first" : "John",
              "last" : "Smith"
            },
            {
              "first" : "Alice",
              "last" : "White"
            }
          ]
        }
      }
    ]
  }
}

2.2.3、嵌套数据

嵌套数据类型是对象数据类型的特殊版本,它允许对象数组中的各个对象被索引,数组中的各个对象之间保持独立,能够对每一个文档进行单独查询。嵌套数据类型保留文档的内部之间的关联。

ElasticSearch 引擎内部使用不同的方式处理嵌套数据类型和对象数组的方式,对于嵌套数据类型,ElasticSearch 把数组中的每一个嵌套文档(Nested Document)索引为单个文档,这些文档是隐藏(Hidden)的,文档之间是相互独立的,但是,保留文档的内部字段之间的关联,使用嵌套查询(Nested Query)能够独立于其他文档而去查询单个文档。

在创建嵌套数据类型的字段时,需要设置字段的type属性为nested。

curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index?pretty -d '
{
    "mappings": {
        "my_type": {
            "properties": {
                "group": {
                    "type": "keyword"
                },
                "user": {
                    "type": "nested",
                    "properties": {
                        "first": {
                            "type": "keyword"
                        },
                        "last": {
                            "type": "keyword"
                        }
                    }
                }
            }
        }
    }
}'

为嵌套字段赋予多个值,ElasticSearch 自动把字段值转换为数组类型:

curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index/my_type/1 -d '
{
    "group": "fans",
    "user": [
        {
            "first": "John",
            "last": "Smith"
        },
        {
            "first": "Alice",
            "last": "White"
        }
    ]
}'

在 ElasticSearch 内部,嵌套的文档(Nested Documents)被索引为很多独立的隐藏文档(separate documents),这些隐藏文档只能通过嵌套查询(Nested Query)访问。每一个嵌套的文档都是嵌套字段(文档数组)的一个元素。嵌套文档的内部字段之间的关联被 ElasticSearch 引擎保留,而嵌套文档之间是相互独立的。

嵌套查询用于查询嵌套对象,执行嵌套查询执行的条件是:嵌套对象被索引为单个文档,查询作用在根文档(Root Parent)上。嵌套查询由关键字“nested”指定:

{
    "nested": {
        "path": "obj1",
        "query": {...}
    }
}
  • path 参数:指定嵌套字段的文档路径,根路径是顶层的文档,通过点号“.”来指定嵌套文档的路径;
  • query 参数:在匹配路径(参数 path)的嵌套文档上执行查询,query参数指定对嵌套文档执行的查询条件。
curl -H "Content-Type: application/json" -XGET localhost:9200/my_index/my_type/_search?pretty -d '
{
    "query": {
        "nested": {
            "path": "user",
            "query": {
                "bool": {
                    "must": [
                        {
                            "match": {
                                "user.first": "John"
                            }
                        },
                        {
                            "match": {
                                "user.last": "White"
                            }
                        }
                    ]
                }
            }
        }
    }
}'

在该例中,ElasticSearch 引起保留 Alice 和 White 之间的关联,而 John 和 White 之间是没有任何关联的:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}

2.3、地理位置类型(Geo datatypes)

地理位置信息类型用于存储地理位置信息的经纬度,地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo_point

curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index1?pretty -d '
{
    "mappings": {
        "my_type1": {
            "properties": {
                "location": {
                    "type": "geo_point"
                }
            }
        }
    }
}'

location 被声明为 geo_point 后,就可以索引包含了经纬度信息的文档了。经纬度信息的形式可以是字符串,数组或者对象。

# 明确以 lat 和 lon 作为属性的对象;
curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index1/my_type1/1?pretty -d '
{
    "text": "Geo-point as an object",
    "location": {
        "lat": 41.12,
        "lon": -71.34
    }
}'

# 以半角逗号分割的字符串形式 "lat,lon"
curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index1/my_type1/2?pretty -d '
{
    "text": "Geo-point as a string",
    "location": "41.12,-71.34"
}'

curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index1/my_type1/3?pretty -d '
{
    "text": "Geo-point as a geohash",
    "location": "drm3btev3e86"
}'

# 数组形式表示 [lon,lat]
curl -H "Content-Type: application/json" -XPUT localhost:9200/my_index1/my_type1/4?pretty -d '
{
    "text": "Geo-point as an array",
    "location": [
        -71.34,
        41.12
    ]
}'

有四种地理坐标点相关的过滤方式可以用来选中或者排除文档:

  • geo_bounding_box::

    找出落在指定矩形框中的坐标点

  • geo_distance::

    找出与指定位置在给定距离内的点

  • geo_distance_range::

    找出与指定点距离在给定最小距离和最大距离之间的点

  • geo_polygon::

    找出落在多边形中的点。这个过滤器使用代价很大

curl -H "Content-Type: application/json" -XGET localhost:9200/my_index1/my_type1/_search?pretty -d '
{
    "query": {
        "geo_bounding_box": {
            "location": {
                "top_left": {
                    "lat": 42,
                    "lon": -72
                },
                "bottom_right": {
                    "lat": 40,
                    "lon": -70
                }
            }
        }
    }
}'

2.4、特定类型(Specialised datatypes)

IPv4 类型(IPv4 datatype):ip 用于IPv4 地址

Completion 类型(Completion datatype):completion 提供自动补全建议

Token count 类型(Token count datatype):token_count 用于统计做子标记的字段的index数目,该值会一直增加,不会因为过滤条件而减少

mapper-murmur3 类型:通过插件,可以通过 _murmur3_ 来计算 index 的哈希值

附加类型(Attachment datatype):采用 mapper-attachments 插件,可支持_attachments_ 索引,例如 Microsoft office 格式,Open Documnet 格式, ePub,HTML 等

三、映射 parameters

https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html#mapping-params

analyzer

分词器,如果修改分词器,要重建索引,因为分词的内容是放到倒排索引中的,如果不重建索引,是无法修改倒排索引中内容的,所以选择分词器就比较重要,比如中文分词使用 ik_smart 还是 ik_max_word。

normalizer

用于解析前的标准化配置,比如把所有的字符转化为小写。

index

index 选项控制是否索引字段值。 它接受 true 或 false,默认为 true。 未编制索引的字段不可查询。

null_value

无法索引或搜索空值 null。 当字段设置为 null(或空数组或空值数组)时,它被视为该字段没有值。

null_value 参数允许使用指定的值替换显式空值,以便可以对其进行索引和搜索。

四、参考资料

1.ES 权威指南
2.https://blog.csdn.net/zx711166/article/details/81667862
3.https://blog.csdn.net/ZYC88888/article/details/83059040

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容