映射Mapping

2019-09-08 23:01发布

(一)映射

映射是定义文档及其包含的字段如何存储和索引的过程,例如,使用映射来定义:

  • 哪些字符串字段应该被视为全文字段。
  • 哪些字段包含数字、日期或地理位置。
  • 日期值的格式。
  • 用于控制动态添加字段的映射的自定义规则。

映射类型

每个索引都有一个映射类型,它决定文档将如何被索引。

映射类型具有:

元字段

  • 元字段用于自定义如何处理文档的关联元数据,元字段的示例包括文档的_index_type_id_source字段。

字段或属性

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

(二)字段数据类型

每个字段都有一个数据type

为不同的目的以不同的方式索引相同的字段通常是有用的,例如,字符串字段可以作为全文搜索的text字段索引,也可以作为排序或聚合的keyword字段索引,或者,你可以使用标准分析器、英语分析器和法语分析器索引字符串字段。

这就是多字段的目的,大多数数据类型通过fields参数支持多字段。

(三)防止映射爆炸的设置

在索引中定义太多字段会导致映射爆炸,这会导致内存错误和难以恢复的情况,这个问题可能比预期的要普遍。例如,考虑这样一种情况,其中插入的每个新文档都引入了新字段,这在动态映射中非常常见,每当文档包含新字段时,这些字段就会出现在索引的映射中。这对于少量数据来说并不令人担心,但是随着映射的增长,这可能会成为一个问题,以下设置允许你限制可以手动或动态创建的字段映射的数量,以防止错误的文档导致映射爆炸:

index.mapping.total_fields.limit

  • 索引中字段的最大数目,字段和对象映射以及字段别名都属于此限制,默认值是1000

index.mapping.depth.limit

  • 字段的最大深度,用内部对象的数量来度量,例如,如果所有字段都在根对象级别定义,则深度为1,如果有一个对象映射,则深度为2,依此类推,默认值是20

index.mapping.nested_fields.limit

  • 索引中不同嵌套映射的最大数目,默认为50

index.mapping.nested_objects.limit

  • 跨所有嵌套类型的单个文档中嵌套JSON对象的最大数量,默认为10000

index.mapping.field_name_length.limit

  • 设置字段名称的最大长度,默认值是Long.MAX_VALUE(没有限制),这个设置实际上并没有解决映射爆炸的问题,但是如果你想限制字段长度,那么它仍然是有用的,通常不需要设置这个设置,默认值没有问题,除非用户开始添加大量具有非常长的名称的字段。

(四)动态映射

字段和映射类型在使用之前不需要定义,多亏了动态映射,新的字段名将自动添加,只需索引文档,可以向顶级映射类型、内部对象和嵌套字段添加新字段。

可以配置动态映射规则来定制用于新字段的映射。

(五)显式的映射

你对数据的了解超过了Elasticsearch所能猜到的,因此,尽管开始动态映射可能很有用,但在某个时候,你可能希望指定自己的显式映射。

你可以在创建索引并向 现有索引添加字段时创建字段映射。

更新现有字段映射

除非有文档记录,否则无法更新现有字段映射,更改映射将意味着已经索引的文档无效。相反,应该使用正确的映射创建一个新索引,并将数据重新索引到该索引中,如果只希望重命名字段而不更改其映射,那么引入别名字段可能是有意义的。

使用显式映射创建索引

可以在创建索引时指定映射,如下所示:

PUT /my-index
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}

将字段添加到现有映射

可以使用put mapping API将一个或多个新字段添加到现有索引。

以下示例将employee-idkeyword类型并且 index参数为false。这意味着该employee-id字段的值已存储但未编入索引且不可搜索。

PUT /my-index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

(六)查看索引mapping

GET /my-index/_mapping

返回如下:

{
  "my-index" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "employee-id" : {
          "type" : "keyword",
          "index" : false
        },
        "name" : {
          "type" : "text"
        }
      }
    }
  }
}

查看指定字段mapping

GET /my-index/_mapping/field/employee-id

返回如下

{
  "my-index" : {
    "mappings" : {
      "employee-id" : {
        "full_name" : "employee-id",
        "mapping" : {
          "employee-id" : {
            "type" : "keyword",
            "index" : false
          }
        }
      }
    }
  }
}

(七)移除映射类型

在Elasticsearch 7.0.0或更高版本中创建的索引不再接受 _default_映射。在6.x中创建的索引将继续像以前一样在Elasticsearch 6.x中运行。在7.0中的API中不推荐使用类型。Elasticsearch 8.x不再支持在请求中指定类型。

什么是映射类型?

自Elasticsearch首次发布以来,每个文档都存储在单个索引中并分配了单个映射类型。映射类型用于表示要编制索引的文档或实体的类型,例如 twitter索引可能具有user类型和tweet类型。

每种映射类型都可以有自己的字段,因此user类型可能有 full_name字段,user_name字段和email字段,而 tweet类型可以有content字段,tweeted_at字段和user类型user_name字段。

每个文档都有一个_type包含类型名称的元字段,通过在URL中指定类型名称,搜索可以限制为一种或多种类型:

GET twitter/user,tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

_type字段与文档组合_id生成_uid 字段,因此具有不同类型但_id相同的文档可以存在于单个索引中。

映射类型也用于在文档之间建立 父子关系,因此question类型的文档可以是answer类型文档的父文档。

为什么要移除映射类型?

最初,我们谈到了一个“ 索引 ”类似于SQL数据库中的“ 数据库 ”,而“ 类型 ”等同于 “ 表”

这是一个糟糕的比喻,导致错误的假设。在SQL数据库中,表彼此独立。一个表中的列与另一个表中具有相同名称的列无关。映射类型中的字段不是这种情况。

在Elasticsearch索引中,不同映射类型中具有相同名称的字段在内部由相同的Lucene字段支持。换句话说,使用上面的示例,user类型中的user_name字段存储在与tweet类型中的user_name字段完全相同的字段中,两个 user_name字段在两种类型中必须具有相同的映射(定义)。

最重要的是,在同一索引中存储具有很少或没有共同字段的不同实体会导致稀疏数据并干扰Lucene有效压缩文档的能力。

出于这些原因,将Elasticsearch中删除映射类型。

映射类型替代方法

每种文档类型独立索引

第一种方法是每个文档类型都有一个索引。可以将推文索引在tweets索引中,用户索引在user中,而不是将推文和用户存储在单个twitter索引中。索引完全相互独立,因此索引之间不存在字段类型冲突。

这种方法有两个好处:

  • 数据更可能是密集的,因此受益于Lucene中使用的压缩技术。
  • 用于全文搜索评分的术语统计更可能是准确的,因为同一索引中的所有文档都代表单个实体。

每个索引的大小可以根据其包含的文档数量进行适当调整:users可以使用较少数量的主分片和tweets使用较大数量的主分片。

自定义类型字段

当然,群集中可以存在多少个主分片是有限制的,因此您可能不希望仅为几千个文档的集合浪费整个分片。在这种情况下,您可以实现自己的自定义type 字段,该字段的工作方式与旧的相似_type

我们来看上面的usertweet例子:

PUT twitter
{
  "mappings": {
    "user": {
      "properties": {
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" }
      }
    },
    "tweet": {
      "properties": {
        "content": { "type": "text" },
        "user_name": { "type": "keyword" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

PUT twitter/user/kimchy
{
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/tweet/1
{
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

可以使用自定义字段“type”替换如下:

PUT twitter
{
  "mappings": {
      "properties": {
        "type": { "type": "keyword" }, 
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" },
        "content": { "type": "text" },
        "tweeted_at": { "type": "date" }
      }
  }
}

PUT twitter/_doc/user-kimchy
{
  "type": "user", 
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/_doc/tweet-1
{
  "type": "tweet", 
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "user_name": "kimchy"
        }
      },
      "filter": {
        "match": {
          "type": "tweet" 
        }
      }
    }
  }
}

没有映射类型的父/子

旧版本中,通过将一个映射类型设置为父级,将一个或多个其他映射类型设置为子级来表示父子关系。没有类型,我们就不能再使用这种语法了。除了表示文档之间关系的方式已更改为使用新jjoin字段段之外,父子特征将继续像以前一样。

移除映射类型的计划

这对我们的用户来说是一个很大的变化。更改将如下所示:

Elasticsearch 5.6.0

  • index.mapping.single_type: true在索引上设置将启用将在6.0中强制执行的单索引类型行为。
  • 父子的join字段替换可用于在5.6中创建的索引。

Elasticsearch 6.x

  • 在5.x中创建的索引将继续在6.x中运行,就像在5.x中一样。
  • 在6.x中创建的索引仅允许每个索引使用单一类型。任何名称都可以用于该类型,但只能有一个名称。首选类型名称是_doc,因此索引API具有与它们在7.0中相同的路径: PUT {index}/_doc/{id}POST {index}/_doc
  • _type名称可以不再与合并_id,形成_uid 领域。该_uid领域已成为该_id领域的别名。
  • 新索引不再支持旧式的父/子,而应该使用该join字段
  • _default_映射类型已弃用。
  • 在6.8中,索引创建,索引模板和映射API支持查询字符串参数(include_type_name),该参数指示请求和响应是否应包含类型名称。它默认为true,并且应设置为显式值以准备升级到7.0。未设置include_type_name 将导致弃用警告。没有显式类型的索引将使用虚拟类型名称_doc

Elasticsearch 7.x

  • 弃用请求中指定类型。例如,索引文档不再需要文档type。新索引API 用于PUT {index}/_doc/{id}显式ID和POST {index}/_doc自动生成的ID。请注意,在7.0中,它_doc是路径的永久部分,并表示端点名称而不是文档类型。
  • include_type_name在索引创建参数,指标模板,mapping API中将默认为false
  • _default_映射类型被去除。

Elasticsearch 8.x

  • 不再支持在请求中指定类型。
  • include_type_name参数已删除。


(八)将多类型索引迁移到单一类型

重新索引API可用于多类型索引转换成单纤型索引。以下示例可用于Elasticsearch 5.6或Elasticsearch 6.x. 在6.x中。

第一个示例将twitter索引拆分为tweets索引和 users索引:

PUT users
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT tweets
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "user"
  },
  "dest": {
    "index": "users",
    "type": "_doc"
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet"
  },
  "dest": {
    "index": "tweets",
    "type": "_doc"
  }
}

自定义类型字段

下一个示例添加自定义type字段并将其设置为_type原始值。_id会加上type以防止存在任何具有冲突ID的不同类型的文档:

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}


POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

(九)7.0版无类型API

在Elasticsearch 7.0中,每个API都支持无类型请求,指定类型将产生弃用警告。

索引API

索引创建,索引模板和映射API支持新的include_type_name URL参数,该参数指定请求和响应中的映射定义是否应包含类型名称。6.8版该参数默认为true,以匹配在映射中使用类型名称的7.0之前的行为。 7.0版它默认为false,将在8.0版中删除。

查看与Elasticsearch交互的一些示例,并将此选项设置为false

PUT index?include_type_name=false
{
  "mappings": {
    "properties": { 
      "foo": {
        "type": "keyword"
      }
    }
  }
}

PUT index/_mappings?include_type_name=false
{
  "properties": { 
    "bar": {
      "type": "text"
    }
  }
}

GET index/_mappings?include_type_name=false

返回如下:

{
  "index": {
    "mappings": {
      "properties": { 
        "foo": {
          "type": "keyword"
        },
        "bar": {
          "type": "text"
        }
      }
    }
  }
}
}

在7.0中,必须使用{index}/_doc自动生成_id和{index}/_doc/{id}使用显式id 的路径调用索引API 。

类型也不应再出现在请求体中:

PUT index/_doc/1
{
  "foo": "baz"
}

GET index/_doc/1

POST index/_update/1
{"doc" : {"foo" : "qux"}
}

GET /index/_source/1

POST _bulk
{ "index" : { "_index" : "index", "_id" : "3" } }
{ "foo" : "baz" }
{ "index" : { "_index" : "index", "_id" : "4" } }
{ "foo" : "qux" }

搜索API

在调用搜索API _search_msearch,[content_hide][content_hide]或者_explain,类型不应包含在URL中。此外,该_type字段不应用于查询,聚合或脚本。


登录 后发表评论
0条评论
还没有人评论过~