ElasticSearch 7的映射

本文详细介绍了Elasticsearch 7中映射的概念和重要性,包括显式映射创建、映射基本类型、映射数组、映射对象、管理嵌套对象以及通过联接字段管理子文档。映射定义了搜索和处理文档的方式,影响索引和搜索效果。文章通过实例演示了如何创建和管理映射,强调了正确配置映射对提升索引质量和性能的重要性。
摘要由CSDN通过智能技术生成

映射是Elasticsearch中一个非常重要的概念,因为它定义了搜索的方式
引擎应处理文档及其字段。
搜索引擎执行以下两个主要操作:

  • 索引:这是接收文档并对其进行处理的操作,并且将其存储在索引中
  • 搜索:这是从索引中检索数据的操作

这两个部分是严格连接的; 索引步骤中的错误导致不必要或丢失的搜索结果。
Elasticsearch在索引级别具有显式映射。 索引时,如果映射是未提供,则创建一个默认值,并从数据字段中猜测结构文件组成的。 然后,此新映射将自动传播到所有群集节点。
默认类型映射具有合理的默认值,但是当您要更改时,他们的行为或自定义索引的其他几个方面(存储,忽略,完成等),您需要提供一个新的映射定义。
我们将研究记录映射的所有可能的映射字段类型组成。
本章将介绍以下:

  • 使用显式映射创建
  • 映射基本类型
  • 映射数组
  • 映射对象
  • 管理嵌套对象
  • 使用联接字段管理子文档

2.1 使用显式映射创建

如果我们将索引视为SQL世界中的数据库,则映射类似于表定义。
Elasticsearch能够了解您所使用的文档的结构索引(反射)并自动创建映射定义(明确映射创建)。

1.做好准备

需要一个正在运行的Elasticsearch安装,要执行这些命令,可以使用任何HTTP客户端,例如curl(https://curl.haxx.se/),postman(https://www.getpostman.com/)或其他类似的平台。 我建议使用Kibana控制台提供更好的代码完成功能Elasticsearch的字符转义。

2.怎么做…

您可以通过在Elasticsearch中添加新文档来显式创建映射。 对于
为此,我们将执行以下步骤:

1.像这样创建一个索引:

PUT test

获得回复如下:

{
 "acknowledged" : true,
 "shards_acknowledged" : true,
 "index" : "test"
 }

2.将文档放入索引,如以下代码所示:

PUT test/_doc/1
{
    "name":"Paul", 
    "age":35
}

获得回复如下:

{
 "_index" : "test",
 "_type" : "_doc",
 "_id" : "1",
 "_version" : 1,
 "result" : "created",
 "_shards" : {
 "total" : 2,
 "successful" : 1,
 "failed" : 0
 },
 "_seq_no" : 0,
 "_primary_term" : 1
}

3.使用以下代码获取映射:

GET test/_mapping

4.Elasticsearch自动创建的结果映射应为如下:

{
  "test" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

5.要删除索引,可以调用以下命令:

DELETE test

获得回复如下:

{
 "acknowledged" : true
}

3.这个怎么运作…

第一个命令行创建一个索引,在此我们将配置类型/映射和插入文件。
第二个命令在索引中插入一个文档(我们将看到索引的创建在第3章,基本操作中的创建索引得文档操作在第3章,基本操作中的索引文档操作介绍)。

在文档索引阶段,Elasticsearch在内部检查_doc类型存在,否则将动态创建一个。
Elasticsearch读取映射字段的所有默认属性,并开始处理它们如下:

  • 如果该字段已经存在于映射中,并且该字段的值为有效(匹配正确的类型),Elasticsearch无需更改当前的映射。
  • 如果该字段已经存在于映射中,但是该字段的值为同的类型,它将尝试升级字段类型(即从整数升级为长)。 如果类型不兼容,则会引发异常,并且索引处理失败。
  • 如果不存在该字段,它将尝试自动检测字段的类型。 它更新
    具有新字段映射的映射。
4.还有更多…

在Elasticsearch中,按类型分隔文档是逻辑的,而不是物理的。 Elasticsearch核心引擎透明地对此进行管理。 从物理上来说,所有文件
类型使用相同的Lucene索引,因此它们之间没有完全分隔的,类型的概念纯属逻辑,由Elasticsearch强制执行。 用户不是对此内部管理感到困扰,但在某些情况下,数量的记录,这会影响读写性能记录,因为所有记录都存储在相同的索引文件中。

每个文档都有一个唯一的标识符,称为索引的UID,该标识符存储在
文档的特殊_uid字段。 这是通过将_id的文档类型。 在我们的示例中,_uid将是_doc#1。

可以在索引时间提供_id,也可以由_id自动分配Elasticsearch(如果缺少)。

创建或更改映射类型时,Elasticsearch自动传播将更改映射到集群中的所有节点,以便将所有分片对齐处理该特定类型

image-20200913133346038每个索引只能包含一个类型。 类型的名称Elasticsearch的先前版本可能会有所不同。 因为类型是在7.x中已弃用,最佳做法是调用_doc类型。

2.2 映射基本类型

使用显式映射可以更快地开始提取数据,使用无模式方法,而不必担心字段类型。 因此,取得更好的索引结果和性能,则需要手动定义映射。
微调映射带来了一些优势,例如:

  • 减少磁盘上的索引大小(禁用自定义功能字段)
  • 仅对需要的字段建立索引(一般加快)
  • 预处理数据以进行快速搜索或实时分析(例如构面)
  • 正确定义一个字段是否必须使用多个标记分析或被视为单个令牌

Elasticsearch允许您使用具有配置的广泛基础字段。

1.怎么做…

让我们使用类似eBay的商店的车间订单的半现实示例:

1.首先,我们定义一个表格:

NameTypeDescription
ididentifier订单识别码
datedate(time)订购日期
customer_idid reference客户编号
namestring商品的名称
quantityinteger商品的数量
pricedouble商品的价格
vatdouble商品的增值税
sentboolean订单状态

2.我们的订单记录必须转换为Elasticsearch映射定义如下:

#前提是索引存在,不然会报错,解决办法:PUT test
PUT test/_mapping
{
 "properties" : {
 "id" : {"type" : "keyword"},
 "date" : {"type" : "date"},
 "customer_id" : {"type" : "keyword"},
 "sent" : {"type" : "boolean"},
 "name" : {"type" : "keyword"},
 "quantity" : {"type" : "integer"},
 "price" : {"type" : "double"},
 "vat" : {"type" : "double", "index":"false"}
 }
}

# 如果索引不存在就创建索引及其对应映射
PUT test
{
  "mappings": { 
  "properties": {
    "id":{"type": "keyword"},
    "date":{"type": "date"},
    "customer_id":{"type": "long"},
    "name":{"type": "keyword"},
    "quantity":{"type": "integer"},
    "price":{"type": "double"},
    "vat":{"type": "double","index":"false"},
    "sent":{"type": "boolean"}
  }
 }
}  

现在,可以将映射放入索引中了。 我们将在在第4章“基本操作”中,将映射放入索引配置中。

2.这个怎么运作…

字段类型必须映射到Elasticsearch基本类型之一,并且有关的选项
需要添加字段必须如何编制索引。

下表是映射类型的参考:

TypeES-TypeDescription
String, VarCharkeyword这是一个不可标记的文本字段:CODE001
String, VarChar,Texttext这是一个需要标记的文本字段:一个漂亮的文本
Integerinteger这是一个整数(32位):1、2、3或4
longlong这是一个长值(64位)
floatfloat这是一个浮点数(32位):1.2或4.5
doubledouble这是一个浮点数(64位)
booleanboolean这是一个布尔值:true或false
date/datetimedate这是日期或日期时间值:2013-12-25,2013-12-25T22:21:20
bytes/binarybinary这包括一些用于二进制的字节数据,例如文件或字节流

根据数据类型,可以向Elasticsearch给出明确的指令处理以进行更好的管理时。 最常用的选项是如下:

  • store(默认为false):标记要存储在单独索引中的字段片段以便快速检索。 存储字段会消耗磁盘空间,但会减少磁盘空间计算是否需要从文档中提取(即在脚本中)和聚合)。 此选项的可能值为false和true。在聚合中,存储的字段比其他字段快。
  • index:定义是否应为字段建立索引。 的此参数的可能值为true和false。 索引字段不是可搜索的(默认为true)。
  • null_value:如果字段为null,则定义默认值。
  • boost:用于更改字段的重要性(默认为1.0)。
  • search_analyzer:这定义了要在搜索过程中使用的分析器。如果未定义,则使用父对象的分析器(默认为null)。
  • analyser:这将设置要使用的默认分析器(默认为null)。
  • include_in_all:这标志着当前字段要在特殊索引中
    _all字段(包含所有字段的串联文本的字段)(默认真正)。
  • norms:控制Lucene规范。此参数用于更好得分查询。如果该字段仅用于过滤,则最佳做法是禁用它以减少资源使用(对于分析字段默认为true,对于not_analyzed的结果为false)。
  • copy_to:这允许您将一个字段的内容复制到另一个实现功能,类似于_all字段。
  • ignore_above:这使您可以跳过更大的索引字符串比它的价值。这对于处理字段以进行精确过滤非常有用,聚合和排序。它还可以防止单个术语令牌变得太大并防止由于Lucene术语字节长度而导致的错误限制为32766(默认2147483647)。

image-20200913133346038Boost仅在术语级别上工作,因此主要用于术语,术语,并匹配查询。

3.还有更多…

在Elasticsearch的先前版本中,字符串的标准映射为字符串。 在版本5.x中,不建议使用字符串映射并将其迁移到关键字和文本映射。

  • 在Elasticsearch 6.x版中,如使用显式映射创建配置中所示,字符串的显式推断类型是多字段映射:默认处理为文本。 此映射允许文本查询(即是,字词,匹配和跨度查询)。 在使用中提供的示例中显式映射创建配置,这就是名称。
  • 关键字子字段用于关键字映射。 该字段可以使用用于精确的术语匹配以及聚合和排序。 在这个例子中使用显式映射创建食谱中提供的内容,所引用的字段为关键字名称。

仅适用于文本映射的另一个重要参数是term_vector(组成字符串的术语的向量。请参阅Lucene文档以了解有关更多详细信息,请参见http://lucene.apache.org/core/6_1_0/core/org/apache/lucene/index/Terms.html)。

term_vector可以接受以下值:

  • no:这是默认值,跳过术语向量
  • yes:这是存储字词向量
  • with_offsets:这是带有标记偏移量(开始,结束的术语向量)
    字符块中的位置)
  • with_positions:用于存储令牌在术语中的向量位置
  • with_positions_offsets:存储所有项向量数据

词项向量允许快速突出显示,但由于存储其他文本信息而占用磁盘空间。 最佳做法是仅在需要突出显示的字段(例如标题或文档内容)中激活。

2.3 映射数组

数组或多值字段在数据模型中非常常见(例如多个电话号码,地址,姓名,别名等),但本机不支持传统的SQL解决方案。
在SQL中,多值字段要求创建必须连接的附件表收集所有值,导致记录基数降低时的影响性能很大

1.怎么做…

1.每个字段都自动作为数组进行管理。 例如,存储标签对于文档,映射将如下所示:

{
 "properties" : {
 "name" : {"type" : "keyword"},
 "tag" : {"type" : "keyword", "store" : "yes"},
 ...
 }
}

2.此映射对两个文档都有效。 以下是文档1的代码:

{"name": "document1", "tag": "awesome"}

3.以下是document2的代码:

{"name": "document2", "tag": ["cool", "awesome", "amazing"] }
2.这个怎么运作…

Elasticsearch透明地管理数组:如果声明一个a,则没有区别,由于其Lucene核心性质,因此是单一值或多值。

字段的多值是在Lucene中管理的,因此您可以将它们添加到具有相同字段名称的文档。 对于具有SQL背景的人来说,行为可能很奇怪,但这是NoSQL世界中的关键点,因为它减少了对联接查询的需求,并创建了不同的表来管理多值。 嵌入式对象数组的行为与简单字段相同。

2.4 映射对象

对象是基本结构(类似于SQL中的记录)。 Elasticsearch扩展了传统上使用对象,因此允许递归嵌入对象。

1.怎么做…

我们可以使用来重写上一个示例配置中找到的映射代码项目数据:

PUT test

PUT test/_mapping
{
 "properties" : {
 "id" : {"type" : "keyword"},
 "date" : {"type" : "date"},
 "customer_id" : {"type" : "keyword", "store" : "yes"},
 "sent" : {"type" : "boolean"},
 "item" : {
 "type" : "object",
 "properties" : {
 "name" : {"type" : "text"},
 "quantity" : {"type" : "integer"},
 "price" : {"type" : "double"},
 "vat" : {"type" : "double"}
    } 
  }
 }
}
或者
PUT test/_mapping?include_type_name=true
{
 "properties" : {
 "id" : {"type" : "keyword"},
 "date" : {"type" : "date"},
 "customer_id" : {"type" : "keyword", "store" : "yes"},
 "sent" : {"type" : "boolean"},
 "item" : {
 "type" : "object",
 "properties" : {
 "name" : {"type" : "text"},
 "quantity" : {"type" : "integer"},
 "price" : {"type" : "double"},
 "vat" : {"type" : "double"}
    } 
  }
 }
}


遇见问题:
Types cannot be provided in put mapping requests, unless the include_type_name parameter is set to t...
这个是因为elasticsearch7.0 之后不支持type导致的…

原因是由于写法是低版本的elasticsearch的,高版本要求传入一个include_type_name参数,值为true。所以加上一个参数即可。如下:

PUT 192.168.2.121:9200/cs_company/doc/_mapping?include_type_name=true

2.这个怎么运作…

Elasticsearch使用本机JSON,因此可以映射每个复杂的JSON结构进去。当Elasticsearch解析对象类型时,它将尝试提取字段和进程它们作为其定义的映射。如果不是,它将使用以下命令学习对象的结构反射。对象的最重要属性如下:

  • properties:这是字段或对象的集合(我们可以考虑它们作为SQL世界中的列)。
  • enabled:确定是否应处理对象。如果设置为false,对象中包含的数据未编制索引,因此无法被搜索(默认为true)。
  • dynamic:这允许Elasticsearch向对象添加新的字段名称使用对插入数据值的反映。如果设置为false,当您尝试索引包含新字段类型的对象时,该对象将可能被拒绝。如果将其设置为strict,则当对象中存在新的字段类型时,引发错误,跳过索引过程。动态参数使您可以安全地更改文档结构(默认true)。
  • include_in_all:这会将对象值添加到特殊的_all字段中(用于汇总所有文档字段的文本)(默认为true)。

最常用的属性是properties,它允许您映射Elasticsearch字段中的对象。
禁用文档的索引部分会减小索引的大小;但是,那无法搜索数据。换句话说,最终在磁盘上的文件较小,但是功能上要付出代价。

2.5 管理嵌套对象

有一种特殊类型的嵌入式对象:嵌套对象。 这样可以解决问题与Lucene索引架构有关,其中嵌入式对象的所有字段被视为单个对象。 在搜索过程中,在Lucene中无法区分值和同一多值中的不同嵌入对象数组。
如果考虑上一个订购示例,则无法区分商品名称和数量与相同的查询相同,就像Lucene将它们放在相同的Lucene中一样文档对象。 我们需要将它们编入不同的文档中,然后再加入它们。整个行程由嵌套对象和嵌套查询管理。

为什么要有嵌入对象:

这是因为Lucene底层其实没有内部对象的概念,所以ES会利用简单的列表储存字段名和值,将object类型的对象层次摊平,再传给Lucen。

如果需要索引对象数组并维护数组中每个对象的独立性,则应使用nested数据类型而不是object数据类型。 在内部,嵌套对象将数组中的每个对象作为单独的隐藏文档进行索引,这意味着每个嵌套对象都可以使用嵌套查询nested query独立于其他对象进行查询:

参考案例:

#采用object的方式
curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -H 'Content-Type: application/json' -d'
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}
#在内部其转换成一个看起来像下面这样的文档
{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}
#采用nested
curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "my_type": {
      "properties": {
        "user": {
          "type": "nested" 
        }
      }
    }
  }
}

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

嵌套对象定义为具有嵌套类型的标准对象。从映射对象配方的示例中,我们可以更改对象的类型嵌套如下:

PUT test

PUT test/_mapping
{
 "properties" : {
 "id" : {"type" : "keyword"},
 "date" : {"type" : "date"},
 "customer_id" : {"type" : "keyword", "store" : "yes"},
 "sent" : {"type" : "boolean"},
 "item" : {
 "type" : "nested",
 "properties" : {
 "name" : {"type" : "text"},
 "quantity" : {"type" : "integer"},
 "price" : {"type" : "double"},
 "vat" : {"type" : "double"}
    } 
  }
 }
}
2.这个怎么运作…

对文档建立索引后,如果将嵌入式对象标记为嵌套,则该对象为由原始文档提取,然后在新的外部索引中文档,保存在父文档附近的特殊索引位置。
在前面的示例中,我们重用了“映射对象”配方的映射,但是我们将项目的类型从对象更改为嵌套。 无需其他任何操作必须将嵌入式对象转换为嵌套对象。
嵌套的对象是特殊的Lucene文档,它们保存在父文档的数据-这种方法允许与父文档快速连接。

嵌套对象不能用标准查询搜索,只能用嵌套对象搜索。 他们不会显示在标准查询结果中。
嵌套对象的生命与其父对象有关:删除/更新父对象自动删除/更新所有嵌套子代。 改变父母的手段Elasticsearch将执行以下操作:

  • 将旧文档标记为已删除
  • 将所有嵌套文档标记为已删除
  • 索引新文档版本
  • 索引所有嵌套文档
3.还有更多…

有时,需要将嵌套对象的信息传播到其父对象或根对象。 这主要是为了建立有关父母的简单查询(例如术语查询而不使用嵌套查询)。 为了实现这个目标,有两个特殊的必须使用的嵌套对象的属性:

  • include_in_parent:这样可以自动添加嵌套字段到直接父级
  • include_in_root:这会将嵌套对象字段添加到根对象这些设置增加了数据冗余,但它们降低了某些查询的复杂性,从而提高性能。

2.6 通过联接管理子文档领域

在上一个cookbook中,我们了解了如何管理之间的关系具有嵌套对象类型的对象。 嵌套对象的缺点是它们的嵌套来自父母的依赖。 如果您需要更改嵌套对象的值,则可以需要重新索引父级(如果嵌套,这会带来潜在的性能开销对象变化太快)。 为了解决这个问题,Elasticsearch允许您定义子文件。

父子档的原因:
嵌套文档来说,父-子关系的主要优势有:
更新父文档时,不会重新索引子文档。
创建,修改或删除子文档时,不会影响父文档或其他子文档。这一点在这种场景下尤其有用:子文档数量较多,并且子文档创建和修改的频率高时。
子文档可以作为搜索结果独立返回。
1.怎么做…

在下面的示例中,我们有两个相关的对象:一个Order和一个Item。
它们的UML表示如下:

image-20200918113201495

最终的映射应该是Order和Item的字段定义的合并,加上一个特殊字段(在此示例中为join_field),该字段接受父/子关系。
映射将如下所示:

PUT test1/_mapping
{
 "properties": {
 "join_field": {
   "type": "join",
   "relations": {
   "order": "item"
  }
 },
 "id": {
 "type": "keyword"
 },
 "date": {
 "type": "date"
 },
 "customer_id": {
 "type": "keyword"
 },
 "sent": {
 "type": "boolean"
 },
 "name": {
 "type": "text"
 },
 "quantity": {
 "type": "integer"
 },
 "vat": {
 "type": "double"
 }
 }
}

前面的映射与前面的配方非常相似。
如果要存储联接的记录,则需要先保存父记录,然后再保存子文档信息这样:

PUT test1/_doc/1?refresh
 {
 "id": "1",
 "date": "2018-11-16T20:07:45Z",
 "customer_id": "100",
 "sent": true,
 "join_field": "order"
 }

PUT test1/_doc/c1?routing=1&refresh
 {
 "name": "tshirt",
 "quantity": 10,
 "price": 4.3,
 "vat": 8.5,
 "join_field": {
 "name": "item",
 "parent": "1"
 }
}

子项需要特殊管理,因为我们需要添加与父母id的关联。 此外,在对象中,我们需要指定父名称和它的ID。

2.这个怎么运作…

如果在同一索引中存在多个项目关系,则需要进行映射计算为所有其他映射字段的总和。
对象之间的关系必须在join_field中定义。只能有一个join_field进行映射; 如果你需要付出很多关系,可以在关系对象中提供它们。

子文档必须在父文档的同一分片中建立索引; 所以,当建立索引后,必须传递一个额外的参数,该参数用于路由(我们将看看如何做在下一章的索引文档配方中对此进行说明)。
当我们想要子文档不需要重新索引父文档时更改其值。 因此,它在建立索引,重新建立索引(更新)和删除。

3.还有更多…

在Elasticsearch中,我们有不同的方法来管理对象之间的关系,例如
如下:

  • 用type = object嵌入:这是由隐式管理的Elasticsearch并认为嵌入是主文档的一部分。速度很快,但是您需要重新索引主文档以更改值嵌入式对象。
  • 使用type = nested嵌套:这样可以更精确地搜索和使用对子项的嵌套查询对父项进行过滤。 一切正常对于嵌入式对象(查询除外)(必须使用嵌套查询搜索它们)。
  • 外部子文档:这里,子是外部文档,使用join_field属性将它们绑定到父级。 他们一定是索引在与父级相同的分片中。 与父母的加入有点
    比嵌套对象慢,因为嵌套对象在同一数据中Lucene索引中父级的块,并且它们已加载了父级,否则,子文档需要更多的读取操作。

选择如何从对象建模关系取决于您的应用程序场景。
还有另一种方法可以使用,但是在大数据文档中,它带来了表现不佳-连接关系脱钩。 您可以通过两个步骤来执行联接查询:首先,您收集子文档/其他文档的ID,然后搜索它们在他们父母的领域。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值