乐趣区

关于java:Springboot2x整合ElasticSearch7x实战三

大略浏览 10 分钟

本教程是系列教程,对于初学者能够对 ES 有一个整体意识和实际实战。

还没开始的同学,倡议先读一下系列攻略目录:Springboot2.x 整合 ElasticSearch7.x 实战目录

本篇幅是继上一篇 Springboot2.x 整合 ElasticSearch7.x 实战(二),适宜初学 Elasticsearch 的小白,能够跟着整个教程做一个练习。

[toc]

第五章 Mapping 详解

Mapping 是整个 ES 搜索引擎中最重要的一部分之一,学会构建一个好的索引,能够让咱们的搜索引擎更高效,更节俭资源。

什么是 Mapping?

Mapping 是 Elasticsearch 中一种术语,Mapping 相似于数据库中的表构造定义 schema,它有以下几个作用:

1. 定义索引中的字段的名称
2. 定义字段的数据类型,比方字符串、数字、布尔
3. 字段,倒排索引的相干配置,比方设置某个字段为不被索引、记录 position(地位) 等

在 ES 晚期版本,一个索引下是能够有多个 Type,从 7.0 开始,一个索引只有一个 Type,也能够说一个 Type 有一个 Mapping 定义。

理解了什么是 Mapping 后,接下来对 Mapping 的设置坐下介绍:

Maping 设置

dynamic (动静 Mapping)

官网参考:https://www.elastic.co/guide/en/elasticsearch/reference/7.1/mapping.html

PUT users
{
    "mappings": {
        "_doc": {"dynamic": false}
    }
}

在创立一个索引的时候,能够对 dynamic 进行设置,能够设成 false、true 或者 strict。

比方一个新的文档,这个文档蕴含一个字段,当 Dynamic 设置为 true 时,这个文档能够被索引进 ES,这个字段也能够被索引,也就是这个字段能够被搜寻,Mapping 也同时被更新;当 dynamic 被设置为 false 时候,存在新增字段的数据写入,该数据能够被索引,然而新增字段被抛弃;当设置成 strict 模式时候,数据写入间接出错。

index

另外还有 index 参数,用来管制以后字段是否被索引,默认为 true,如果设为 false(有些业务场景,某些字段不心愿被搜寻到),则该字段不可被搜寻。

# index 属性管制 字段是否能够被索引
PUT user_test
{
  "mappings": {
    "properties": {
      "firstName":{"type": "text"},
      "lastName":{"type": "text"},
      "mobile" :{
        "type": "text",
        "index": false
      }
    }
  }
}

index_options

参数 index_options 用于管制倒排索引记录的内容,有如下 4 种配置:

  • doc:只记录 doc id
  • freqs:记录 doc id 和 term frequencies
  • positions:记录 doc id、term frequencies 和 term position
  • offsets:记录 doc id、term frequencies、term position 和 character offects

另外,text 类型默认配置为 positions,其余类型默认为 doc,记录内容越多,占用存储空间越大。

null_value

null_value 次要是当字段遇到 null 值时的解决策略,默认为 NULL,即空值,此时 ES 会默认疏忽该值,能够通过设定该值设定字段的默认值,另外只有 KeyWord 类型反对设定 null_value。

  • 示例
# 设定 Null_value
DELETE users
PUT users
{
    "mappings" : {
      "properties" : {
        "firstName" : {"type" : "text"},
        "lastName" : {"type" : "text"},
        "mobile" : {
          "type" : "keyword",
          "null_value": "NULL"
        }
      }
    }
}
 
PUT users/_doc/1
{
  "firstName":"Zhang",
  "lastName": "Fubing",
  "mobile": null
}
 
PUT users/_doc/2
{
  "firstName":"Zhang",
  "lastName": "Fubing2"
}
 
# 查看后果,有且仅有_id 为 2 的记录
GET users/_search
{
  "query": {
    "match": {"mobile":"NULL"}
  }
}

_all

这个属性当初应用很少,不做深刻解说

参考官网:https://www.elastic.co/guide/…

copy_to

这个属性用于将以后字段拷贝到指定字段。

  1. _all 在 7.x 版本曾经被 copy_to 所代替
  2. 可用于满足特定场景
  3. copy_to 将字段数值拷贝到指标字段,实现相似_all 的作用
  4. copy_to 的指标字段不呈现在_source 中
DELETE users
PUT users
{
"mappings": {
  "properties": {
    "firstName":{
      "type": "text",
      "copy_to": "fullName"
    },
    "lastName":{
      "type": "text",
      "copy_to": "fullName"
    }
  }
}
}
PUT users/_doc/1
{
"firstName":"Li",
"lastName": "Sunke"
}
// 没有新建字段
GET users/_doc/1
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
  "firstName" : "Li",
  "lastName" : "Sunke"
}
}
GET users/_search?q=fullName:(Li sunke)

以前的用法是:

curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'{"mappings": {"my_type": {"properties": {"first_name": {"type":"text","copy_to":"full_name"  # 1},
        "last_name": {
          "type": "text",
          "copy_to": "full_name"  # 2
        },
        "full_name": {"type": "text"}
      }
    }
  }
}
'curl -XPUT'localhost:9200/my_index/my_type/1?pretty'-H'Content-Type: application/json'-d'
{
  "first_name": "John",
  "last_name": "Smith"
}
'curl -XGET'localhost:9200/my_index/_search?pretty'-H'Content-Type: application/json'-d'
{
  "query": {
    "match": {
      "full_name": {  # 3
        "query": "John Smith",
        "operator": "and"
      }
    }
  }
}
'
  1. first_name(名字)和 last_name(姓氏)字段复制到 full_name 字段;
  2. first_name(名字)和 last_name(姓氏)字段依然能够别离查问;
  3. full_name 能够通过 first_name(名字)和 last_name(姓氏)来查问;

一些要点:

  • 复制的是字段值, 而不是 term(词条)(由剖析过程产生).
  • _source 字段不会被批改来显示复制的值.
  • 雷同的值能够复制到多个字段, 通过 “copy_to”: [“field_1”, “field_2”] 来操作.

分词器 analyzer 和 arch_analyzer

PUT /my_index
{
  "mappings": {
    "properties": {
      "text": { 
        "type": "text",
        "fields": {
          "english": { 
            "type":     "text",
            "analyzer": "english",
            "search_analyzer": "english" 
          }
        }
      }
    }
  }
}
#应用_analyze  测试分词器
GET my_index/_analyze 
{
  "field": "text",
  "text": "The quick Brown Foxes."
}
 
GET my_index/_analyze 
{
  "field": "text.english",
  "text": "The quick Brown Foxes."
}

构建 Mapping 形式

咱们晓得 Mapping 是能够通过咱们插入的文档主动生成索引,然而可能还是有一些问题。例如:生成的字段类型不正确,字段的附加属性不满足咱们的需要。这是咱们能够通过显式 Mapping 的形式来解决。俩种办法:

  1. 参考官网 api,纯手写
  2. 构建长期索引;写入一些样本数据;通过 Maping API 查问临时文件的动静 Mapping 定义;批改后、再应用此配置创立索引;删除长期索引;

举荐第二种,不容易出错,效率高。

类型自动识别

ES 类型的自动识别是基于 JSON 的格局,如果输出的是 JSON 是字符串且格局为日期格局,ES 会主动设置成 Date 类型;当输出的字符串是数字的时候,ES 默认会当成字符串来解决,能够通过设置来转换成适合的类型;如果输出的是 Text 字段的时候,ES 会主动减少 keyword 子字段,还有一些自动识别如下图所示:

  • Demo:
# 写入文档,查看 Mapping
PUT mapping_test/_doc/1
{
  "firstName": "Chan", -- Text
  "lastName":  "Jackie", -- Text
  "loginDate": "2018-07-24T10:29:48.103Z" -- Date
}
 
# Dynamic Mapping,推断字段的类型
PUT mapping_test/_doc/1
{
    "uid": "123", -- Text
    "isVip": false, -- Boolean
    "isAdmin": "true", -- Text
    "age": 19, -- Long
    "heigh": 180 -- Long
}
 
# 查看 Dynamic Mapping
GET mapping_test/_mapping

映射参数

mappings 中 field 定义抉择:

"field": {  
         "type":  "text", // 文本类型  
       
         "index": "false"//,设置成 false,字段将不会被索引  
         
         "analyzer":"ik"// 指定分词器  
         
         "boost":1.23// 字段级别的分数加权  
         
         "doc_values":false// 对 not_analyzed 字段,默认都是开启,analyzed 字段不能应用,对排序和聚合能晋升较大性能,节约内存, 如果您确定不须要对字段进行排序或聚合,或者从 script 拜访字段值,则能够禁用 doc 值以节俭磁盘空间:"fielddata":{"loading" : "eager"}//Elasticsearch 加载内存 fielddata 的默认行为是 提早 加载。当 Elasticsearch 第一次查问某个字段时,它将会残缺加载这个字段所有 Segment 中的倒排索引到内存中,以便于当前的查问可能获取更好的性能。"fields":{"keyword": {"type": "keyword","ignore_above": 256}} // 能够对一个字段提供多种索引模式,同一个字段的值,一个分词,一个不分词  
         
         "ignore_above":100 // 超过 100 个字符的文本,将会被疏忽,不被索引
           
         "include_in_all":ture// 设置是否此字段蕴含在_all 字段中,默认是 true,除非 index 设置成 no 选项  
         
         "index_options":"docs"// 4 个可选参数 docs(索引文档号),freqs(文档号 + 词频),positions(文档号 + 词频 + 地位,通常用来间隔查问),offsets(文档号 + 词频 + 地位 + 偏移量,通常被应用在高亮字段)分词字段默认是 position,其余的默认是 docs  
         
         "norms":{"enable":true,"loading":"lazy"}// 分词字段默认配置,不分词字段:默认{"enable":false},存储长度因子和索引时 boost,倡议对须要参加评分字段应用,会额定减少内存消耗量  
         
         "null_value":"NULL"// 设置一些缺失字段的初始化值,只有 string 能够应用,分词字段的 null 值也会被分词  
         
         "position_increament_gap":0// 影响间隔查问或近似查问,能够设置在多值字段的数据上火分词字段上,查问时可指定 slop 距离,默认值是 100  
         
         "store":false// 是否独自设置此字段的是否存储而从_source 字段中拆散,默认是 false,只能搜寻,不能获取值  
         
         "search_analyzer":"ik"// 设置搜寻时的分词器,默认跟 ananlyzer 是统一的,比方 index 时用 standard+ngram,搜寻时用 standard 用来实现主动提醒性能  
         
         "similarity":"BM25"// 默认是 TF/IDF 算法,指定一个字段评分策略,仅仅对字符串型和分词类型无效  
         
     "term_vector":"no"// 默认不存储向量信息,反对参数 yes(term 存储),with_positions(term+ 地位),with_offsets(term+ 偏移量),with_positions_offsets(term+ 地位 + 偏移量) 对疾速高亮 fast vector highlighter 能晋升性能,但开启又会加大索引体积,不适宜大数据量用  
       }  

总结一下:

  • 与域数据格式及束缚相干的参数:normalizer,format,ignore_above,ignore_malformed,coerce
  • 与索引相干的参数:index,dynamic,enabled
  • 与存储策略相干的参数:store, fielddata,doc_values
  • 分析器相干参数:analyzer,search_analyzer
  • 其它参数:boost,copy_to,null_value

对于这些参数的形容次要基于笔者的了解,可能有不精确之处。实际上这些参数与 ES 的实现机制(如存储构造,索引构造亲密无关),只能在理论利用中去缓缓领会。

字段数据类型

ES 字段类型相似于 MySQL 中的字段类型,ES 字段类型次要有:外围类型、简单类型、天文类型以及非凡类型,具体的数据类型如下图所示:

外围类型

从图中能够看出外围类型能够划分为字符串类型、数字类型、日期类型、布尔类型、基于 BASE64 的二进制类型、范畴类型。

字符串类型

其中,在 ES 7.x 有两种字符串类型:text 和 keyword,在 ES 5.x 之后 string 类型曾经不再反对了。

text 类型实用于须要被全文检索的字段,例如新闻注释、邮件内容等比拟长的文字,text 类型会被 Lucene 分词器(Analyzer)解决为一个个词项,并应用 Lucene 倒排索引存储,text 字段不能被用于排序,如果须要应用该类型的字段只须要在定义映射时指定 JSON 中对应字段的 type 为 text。

keyword 适宜简短、结构化字符串,例如主机名、姓名、商品名称等,能够用于过滤、排序、聚合检索,也能够用于准确查问。

数字类型

数字类型分为 long、integer、short、byte、double、float、half_float、scaled_float。

数字类型的字段在满足需要的前提下该当尽量抉择范畴较小的数据类型,字段长度越短,搜寻效率越高,对于浮点数,能够优先思考应用 scaled_float 类型,该类型能够通过缩放因子来准确浮点数,例如 12.34 能够转换为 1234 来存储。

日期类型

在 ES 中日期能够为以下模式:

格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17
工夫戳(和 1970-01-01 00:00:00 UTC 的差值),单位毫秒或者秒
即便是格式化的日期字符串,ES 底层仍然采纳的是工夫戳的模式存储。

布尔类型

JSON 文档中同样存在布尔类型,不过 JSON 字符串类型也能够被 ES 转换为布尔类型存储,前提是字符串的取值为 true 或者 false,布尔类型罕用于检索中的过滤条件。

二进制类型

二进制类型 binary 承受 BASE64 编码的字符串,默认 store 属性为 false,并且不能够被搜寻。

范畴类型

范畴类型能够用来表白一个数据的区间,能够分为 5 种:integer_range、float_range、long_range、double_range 以及 date_range。

简单类型

复合类型次要有对象类型(object)和嵌套类型(nested):

对象类型

JSON 字符串容许嵌套对象,一个文档能够嵌套多个、多层对象。能够通过对象类型来存储二级文档,不过因为 Lucene 并没有外部对象的概念,ES 会将原 JSON 文档扁平化,例如文档:

{
    "name": {
        "first": "wu",
        "last": "px"
    }
}

实际上 ES 会将其转换为以下格局,并通过 Lucene 存储,即便 name 是 object 类型:

{
    "name.first": "wu",
    "name.last": "px"
}

嵌套类型

嵌套类型能够看成是一个非凡的对象类型,能够让对象数组独立检索,例如文档:

{
  "group": "users",
  "username": [{ "first": "wu", "last": "px"},
    {"first": "hu", "last": "xy"},
    {"first": "wu", "last": "mx"}
  ]
}

username 字段是一个 JSON 数组,并且每个数组对象都是一个 JSON 对象。如果将 username 设置为对象类型,那么 ES 会将其转换为:

{
  "group": "users",
  "username.first": ["wu", "hu", "wu"],
  "username.last": ["px", "xy", "mx"]
}

能够看出转换后的 JSON 文档中 first 和 last 的关联失落了,如果尝试搜寻 first 为 wu,last 为 xy 的文档,那么胜利会检索出上述文档,然而 wu 和 xy 在原 JSON 文档中并不属于同一个 JSON 对象,该当是不匹配的,即检索不出任何后果。

嵌套类型就是为了解决这种问题的,嵌套类型将数组中的每个 JSON 对象作为独立的暗藏文档来存储,每个嵌套的对象都可能独立地被搜寻,所以上述案例中尽管外表上只有 1 个文档,但实际上是存储了 4 个文档。

天文类型

天文类型字段分为两种:经纬度类型和天文区域类型:

经纬度类型

经纬度类型字段(geo_point)能够存储经纬度相干信息,通过天文类型的字段,能够用来实现诸如查找在指定天文区域内相干的文档、依据间隔排序、依据地理位置批改评分规定等需要。

天文区域类型

经纬度类型能够表白一个点,而 geo_shape 类型能够表白一块天文区域,区域的形态能够是任意多边形,也能够是点、线、面、多点、多线、多面等几何类型。

非凡类型

非凡类型包含 IP 类型、过滤器类型、Join 类型、别名类型等,在这里简略介绍下 IP 类型和 Join 类型,其余非凡类型能够查看官网文档。

IP 类型

IP 类型的字段能够用来存储 IPv4 或者 IPv6 地址,如果须要存储 IP 类型的字段,须要手动定义映射:

{
  "mappings": {
    "properties": {
      "my_ip": {"type": "ip"}
    }
  }
}

Join 类型

Join 类型是 ES 6.x 引入的类型,以取代淘汰的 _parent 元字段,用来实现文档的一对一、一对多的关系,次要用来做父子查问。

Join 类型的 Mapping 如下:

PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": { 
        "type": "join",
        "relations": {"question": "answer"}
      }
    }
  }
}

其中,my_join_field 为 Join 类型字段的名称;relations 指定关系:question 是 answer 的父类。

例如定义一个 ID 为 1 的父文档:

PUT my_join_index/1?refresh
{
  "text": "This is a question",
  "my_join_field": "question" 
}

接下来定义一个子文档,该文档指定了父文档 ID 为 1:

PUT my_join_index/_doc/2?routing=1&refresh 
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

join 参考:https://www.elastic.co/guide/…

退出移动版