image.png

类型映射关系

高版本去掉_doc

ignore_above 超过ignore_above的字符串将不会被索引或存储

核心数据类型

  • 字符串 - text
    • 用于全文索引,该类型的字段将通过分词器进行分词不可参与聚合
  • 字符串 - keyword

    • 不分词,只能搜索该字段的完整的值, 可以聚合
      1. "city": {
      2. "type": "text",
      3. "fields": {
      4. "keyword": {
      5. "type": "keyword",
      6. "ignore_above": 256
      7. }
      8. }
      9. }
  • 数值型 - Numerical

    • long:有符号64-bit integer:-2^63 ~ 2^63 - 1
    • integer:有符号32-bit integer,-2^31 ~ 2^31 - 1
    • short:有符号16-bit integer,-32768 ~ 32767
    • byte: 有符号8-bit integer,-128 ~ 127
    • double:64-bit IEEE 754 浮点数
    • float:32-bit IEEE 754 浮点数
    • half_float:16-bit IEEE 754 浮点数
    • scaled_float 浮点数的高精度类型
  • 布尔 - boolean
    • 值:false, “false”, true, “true”
  • 日期 - date
    • 由于Json没有date类型,所以es通过识别字符串是否符合format定义的格式来判断是否为date类型
    • format默认为:strict_date_optional_time||epoch_millis format
  • 二进制 - binary
    • 该类型的字段把值当做经过 base64 编码的字符串,默认不存储,且不可搜索
  • 范围类型
    • 范围类型表示值是一个范围,而不是一个具体的值
    • 譬如 age 的类型是 integer_range,那么值可以是 {“gte” : 10, “lte” : 20};搜索 “term” : {“age”: 15} 可以搜索该值;搜索 “range”: {“age”: {“gte”:11, “lte”: 15}} 也可以搜索到
    • range参数 relation 设置匹配模式
      • INTERSECTS :默认的匹配模式,只要搜索值与字段值有交集即可匹配到
      • WITHIN:字段值需要完全包含在搜索值之内,也就是字段值是搜索值的子集才能匹配
      • CONTAINS:与WITHIN相反,只搜索字段值包含搜索值的文档
    • integer_range
    • float_range
    • long_range
    • double_range
    • date_range:64-bit 无符号整数,时间戳(单位:毫秒)
    • ip_range:IPV4 或 IPV6 格式的字符串
  1. # 创建range索引
  2. PUT range_index
  3. {
  4. "mappings": {
  5. "properties": {
  6. "expected_attendees": {
  7. "type": "integer_range"
  8. },
  9. "time_frame": {
  10. "type": "date_range",
  11. "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
  12. }
  13. }
  14. }
  15. }
  16. # 插入一个文档
  17. PUT range_index/_doc/1
  18. {
  19. "expected_attendees" : {
  20. "gte" : 10,
  21. "lte" : 20
  22. },
  23. "time_frame" : {
  24. "gte" : "2015-10-31 12:00:00",
  25. "lte" : "2015-11-05"
  26. }
  27. }
  28. # 12 10~20的范围内,可以搜索到文档1
  29. GET range_index/_search
  30. {
  31. "query" : {
  32. "term" : {
  33. "expected_attendees" : {
  34. "value": 12
  35. }
  36. }
  37. }
  38. }
  39. # within可以搜索到文档
  40. # 可以修改日期,然后分别对比CONTAINSWITHININTERSECTS的区别
  41. GET range_index/_search
  42. {
  43. "query" : {
  44. "range" : {
  45. "time_frame" : {
  46. "gte" : "2015-11-02",
  47. "lte" : "2015-11-03"
  48. }
  49. }
  50. }
  51. }

复杂数据类型

  • 数组类型 Array, 数组中所有的值必须是同一种数据类型, 不支持混合数据类型的数组
    • 字符串数组 [ “one”, “two” ]
    • 整数数组 [ 1, 2 ]
    • 数组的数组 [ 1, [ 2, 3 ]],相当于 [ 1, 2, 3 ]
    • Object对象数组 [ { “name”: “Mary”, “age”: 12 }, { “name”: “John”, “age”: 10 }]
    • 同一个数组只能存同类型的数据,不能混存,譬如 [ 10, “some string” ] 是错误的
    • 数组中的 null 值将被 null_value 属性设置的值代替或者被忽略
    • 空数组 [] 被当做 missing field 处理 — 没有值的字段.
  • 对象类型 Object
    • 对象类型可能有内部对象
    • 被索引的形式为:manager.name.first
  1. # tags字符串数组,lists 对象数组
  2. POST my_index/1
  3. {
  4. "message": "some arrays in this document...",
  5. "tags": [ "elasticsearch", "wow" ],
  6. "lists": [
  7. {
  8. "name": "prog_list",
  9. "description": "programming list"
  10. },
  11. {
  12. "name": "cool_list",
  13. "description": "cool stuff list"
  14. }
  15. ]
  16. }
  17. # 查看映射
  18. {
  19. "my_index" : {
  20. "mappings" : {
  21. "properties" : {
  22. "lists" : {
  23. "properties" : {
  24. "description" : {
  25. "type" : "text",
  26. "fields" : {
  27. "keyword" : {
  28. "type" : "keyword",
  29. "ignore_above" : 256
  30. }
  31. }
  32. },
  33. "name" : {
  34. "type" : "text",
  35. "fields" : {
  36. "keyword" : {
  37. "type" : "keyword",
  38. "ignore_above" : 256
  39. }
  40. }
  41. }
  42. }
  43. },
  44. "message" : {
  45. "type" : "text",
  46. "fields" : {
  47. "keyword" : {
  48. "type" : "keyword",
  49. "ignore_above" : 256
  50. }
  51. }
  52. },
  53. "tags" : {
  54. "type" : "text",
  55. "fields" : {
  56. "keyword" : {
  57. "type" : "keyword",
  58. "ignore_above" : 256
  59. }
  60. }
  61. }
  62. }
  63. }
  64. }
  65. }

嵌套类型Nested

nested 类型是一种对象类型的特殊版本,它允许索引对象数组,独立地索引每个对象
嵌套类型与Object类型的区别
通过例子来说明:

  1. 插入一个文档,不设置mapping,此时 user 字段被自动识别为对象数组 ```json DELETE /my_index PUT /my_index?pretty

POST /my_index/1 { “group” : “fans”, “user” : [ { “first” : “John”, “last” : “Smith” }, { “first” : “Alice”, “last” : “White” } ] }

POST /my_index/2 { “group” : “fans”, “user” : [ { “first” : “a1”, “last” : “b1” }, { “first” : “Alice”, “last” : “Smith” } ] }

GET /my_index/_mapping


2. 查询 user.first为 Alice,user.last 为 Smith的文档,理想中应该找不到匹配的文档
3. 结果是查到了文档1,为什么呢?
```json
GET /my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}
  1. 是由于Object对象类型在内部被转化成如下格式的文档:

    {
    "group" :        "fans",
    "user.first" : [ "alice", "john" ],
    "user.last" :  [ "smith", "white" ]
    }
    
  2. user.first 和 user.last 扁平化为多值字段,alice 和 white 的关联关系丢失了。导致这个文档错误地匹配对 alice 和 smith 的查询

  3. 如果最开始就把user设置为 nested 嵌套对象呢?
DELETE /my_index
PUT /my_index
{
  "mappings": {
      "properties": {
        "user": {
          "type": "nested" 
        }
      }
  }
}

POST /my_index/1
{
  "group": "fans",
  "user": [
    {
      "first": "John",
      "last": "Smith"
    },
    {
      "first": "Alice",
      "last": "White"
    }
  ]
}
  1. 再来进行查询,可以发现以下第一个查不到文档,第二个查询到文档1,符合我们预期 ```json GET my_index/_search { “query”: { “nested”: { “path”: “user”, “query”: {
     "bool": {
       "must": [
         { "match": { "user.first": "Alice" }},
         { "match": { "user.last":  "Smith" }} 
       ]
     }
    
    } } } }

GET my_index/_search { “query”: { “nested”: { “path”: “user”, “query”: { “bool”: { “must”: [ { “match”: { “user.first”: “Alice” }}, { “match”: { “user.last”: “White” }} ] } }, “inner_hits”: { “highlight”: { “fields”: { “user.first”: {} } } } } } }


8. nested对象将数组中每个对象作为独立隐藏文档来索引,这意味着每个嵌套对象都可以独立被搜索
9. 需要注意的是:
- 使用 [nested 查询](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html)来搜索
- 使用 nested 和 [reverse_nested](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-reverse-nested-aggregation.html) 聚合来分析
- 使用 [nested sorting](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html#nested-sorting) 来排序
- 使用 [nested inner hits](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-inner-hits.html#nested-inner-hits) 来检索和高亮
<a name="Qyw5k"></a>
## Date类型
elasticsearch 数据是以 JSON 格式存储的,而 [JSON](https://www.cnblogs.com/reycg-blog/p/9967865.html#json-%E7%9A%84%E8%AF%AD%E6%B3%95)中是并没有 date 数据类型,因此 Elasticsearch 中虽然有 date 类型,但在展示时却要转化成另外的格式。<br />date 类型在 Elasticsearch 展示的格式有下面几种:

- 将日期时间格式化后的字符串,如 "2015-01-01" 或者 "2015/01/01 12:10:30"
- long 型的整数,意义是 milliseconds-since-the-epoch,翻译一下就是自 1970-01-01 00:00:00 UTC 以来经过的毫秒数。
- int 型的整数,意义是 seconds-since-the-epoch, 是指自 1970-01-01 00:00:00 UTC 以来经过的秒数。

后两种的描述里都包含 UTC ,什么是 UTC 呢?<br />UTC(Universal Time Coordinated) 叫做世界统一时间,中国大陆和 UTC 的时差是 + 8 ,也就是 UTC+8。<br />不论 date 是什么展示格式,在 Elasticsearch 内部存储时都是转换成 UTC,并且把时区也会计算进去,从而得到 milliseconds-since-the-epoch 并作为存储的格式。<br />在查询日期时,会执行下面的过程:

1. 转换成 long 整形格式的范围(range) 查询
1. 得到聚合的结果
1. 将结果中的 date 类型(long 整型数据)根据 date format 字段转换回对应的展示格式

**默认格式**<br />Date 的格式化类型是可以通过 format 来指定的,如果没有指定,就会使用默认的格式:
```java
"strict_date_optional_time||epoch_millis"

表示只要是 ISO datetime parser 可以正常解析的都是 strict_date_optional_time。都有哪些语法呢?

 date-opt-time     = date-element ['T' [time-element] [offset]]
 date-element      = std-date-element | ord-date-element | week-date-element
 std-date-element  = yyyy ['-' MM ['-' dd]]
 ord-date-element  = yyyy ['-' DDD]
 week-date-element = xxxx '-W' ww ['-' e]
 time-element      = HH [minute-element] | [fraction]
 minute-element    = ':' mm [second-element] | [fraction]
 second-element    = ':' ss [fraction]
 fraction          = ('.' | ',') digit+

其中中括号内的都是可选的,可填可不填。以 std-date_element 举个例子

2018-11-19
2018
2018-11

上面 3 种格式都满足要求。
除了 strict_date_optional_time ,还可以是 epoch_millis 格式,即 epoch 以来的毫秒数。
举个例子

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type": "date" 
        }
      }
    }
  }
}

PUT my_index/_doc/1
{ "date": "2015-01-01" } 

PUT my_index/_doc/2
{ "date": "2015-01-01T12:10:30Z" } 

PUT my_index/_doc/3
{ "date": 1420070400001 } 

GET my_index/_search
{
  "sort": { "date": "asc"}

上面的 PUT 请求中的 date 数据均满足默认的要求。
如何指定多个date格式?
同一个 date 字段可以指定多个 date 格式,只要使用 || 分隔就可以了。在索引,都会对 date 格式挨个进行匹配,直到找到匹配的格式为止。
如果存储时 date 格式为 milliseconds-since-the-epoch ,在查询时会将其转换为指定的第一个 date 格式。

PUT my_index
{
  "mappings": {
    "doc": {
      "properties": {
        "date": {
          "type":   "date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        }
      }
    }
  }
}


PUT /my_index/doc/1
{ "date": "2018-09-24 19:23:45" }


PUT /my_index/doc/2
{ "date": "2018-09-25" }

GET my_index/_search
{
  "query": {
    "match_all": {}
  }
}

地理位置数据类型

  • geo_point
    • 地理位置,其值可以有如下四中表现形式:
      • object对象:”location”: {“lat”: 41.12, “lon”: -71.34}
      • 字符串:”location”: “41.12,-71.34”
      • geohash:”location”: “drm3btev3e86”
      • 数组:”location”: [ -71.34, 41.12 ]
    • 查询的时候通过 Geo Bounding Box Query 进行查询
  • geo_shape
# 创建映射
PUT /company-locations
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "location": {
        "type": "geo_point"
      }
    }
  }
}

# 字符串形式
PUT /company-locations/_doc/1
{
  "name": "NetEase",
  "location": "40.715,74.011"
}

# 对象形式
PUT /company-locations/_doc/2
{
  "name": "sina",
  "location": {
    "lat": 40.722,
    "lon": 73.989
  }
}

# 数组形式
PUT /company-locations/_doc/3
{
  "name": "baidu",
  "location": [
    73.983,
    40.719
  ]
}

注意

  • 字符串形式以半角逗号分割,如 “lat,lon”
  • 对象形式显式命名为 lat 和 lon
  • 数组形式表示为 [lon,lat]

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

过滤器 作用
geo_bounding_box 找出落在指定矩形框中的点
geo_distance 找出与指定位置在给定距离内的点, 定义圆心和半径的
geo_distance_range 找出与指定点距离在给定最小距离和最大距离之间的点
geo_polygon 找出落在多边形中的点。这个过滤器使用代价很大。当你觉得自己需要使用它,最好先看看geo-shapes。

geo_bounding_box

这是目前为止最有效的地理坐标过滤器了,因为它计算起来非常简单。 你指定一个矩形的顶部 , 底部 , 左边界和右边界,然后过滤器只需判断坐标的经度是否在左右边界之间,纬度是否在上下边界之间

location这些坐标也可以用 bottom_left 和 top_right 来表示

GET /company-locations/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      }, 
      "filter": {
        "geo_bounding_box": {
          "location": {
            "top_left": {
              "lat": 40.73,
              "lon": 71.12
            },
            "bottom_right": {
              "lat": 40.01,
              "lon": 74.1
            }
          }
        }
      }
    }
  }
}

geo_distance

定义圆心和半径的
过滤仅包含与地理位置相距特定距离内的匹配的点。
假设以下映射和索引文档然后可以使用geo_distance过滤器执行以下查询

GET /company-locations/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_distance": {
          "distance": "200km",
          "location": {
            "lat": 40,
            "lon": 70
          }
        }
      }
    }
  }
}

专用数据类型

  • 记录IP地址 ip
  • 实现自动补全 completion
  • 记录分词数 token_count
  • 记录字符串hash值 murmur3
  • Percolator
# ip类型,存储IP
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "ip_addr": {
          "type": "ip"
        }
      }
    }
  }
}

PUT my_index/_doc/1
{
  "ip_addr": "192.168.1.1"
}

GET my_index/_search
{
  "query": {
    "term": {
      "ip_addr": "192.168.0.0/16"
    }
  }
}

多字段特性 multi-fields

  • 允许对同一个字段采用不同的配置,比如分词,常见例子如对人名实现拼音搜索,只需要在人名中新增一个子字段为 pinyin 即可
  • 通过参数 fields 设置

在创建索引,指定analyzer,ES在创建时会先检查是否设置了analyzer字段,如果没定义就用ES预设的
在查询时,指定search_analyzer,ES查询时会先检查是否设置了search_analyzer字段,
如果没有设置,还会去检查创建索引时是否指定了analyzer,还是没有还设置才会去使用ES预设的

  • 插入文档时,将text类型的字段做分词然后插入倒排索引,此时就可能用到analyzer指定的分词器
  • 在查询时,先对要查询的text类型的输入做分词,再去倒排索引搜索,此时就可能用到search_analyzer指定的分词器
    "name": {
    "type": "text",
    "fields": [{
      "name_english": {
        "type": "text",
        "analyzer": "english",
        "search_anlyzer": "english"
        "ignore_above": 256
      }
    }]
    }
    
    "city": {
    "type": "text",
    "analyzer": "ik_smart",
    "search_anlyzer": "ik_smart",
    "fields": [
    {
      "keyword": {
        "type": "keyword",
        "ignore_above": 256
      }
    },
     {
      "k1": {
        "type": "text",
        "analyzer": "ik_max_word",
        "ignore_above": 256
      }
    },
    ]
    }
    
    搜索可以这样使用
    GET /booktest/_search 
    {
      "query": {
      "term": {
        "description.keyword": "联想手机"
      }
    }
    }