Elasticsearch常用操作:映射篇
其實就是es的字段類型是由es來做自動檢測還是由我們自己來指定,因此會分為動態映射和靜態映射。
1 動態映射
1.1 映射規則
JSON格式的數據 | 自動推測的字段類型 |
---|---|
null | 沒有字段被添加 |
true or false | boolean類型 |
浮點類型數字 | float類型 |
數字 | long類型 |
JSON對象 | object類型 |
數組 | 由數組中第一個非空值決定 |
string | 有可能是date類型(開啟日期檢測)、double或long類型、text類型、keyword類型 |
1.2 日期檢測
默認是開啟的(es5.4),測試案例如下:
PUT myblog GET myblog/_mapping PUT myblog/article/1 { "id":1, "postdate":"2018-10-27" } GET myblog/_mapping { "myblog": { "mappings": { "article": { "properties": { "id": { "type": "long" }, "postdate": { "type": "date" } } } } } }
關閉日期檢測後,則不會檢測為日期,如下:
PUT myblog { "mappings": { "article": { "date_detection": false } } } GET myblog/_mapping PUT myblog/article/1 { "id":1, "postdate":"2018-10-27" } GET myblog/_mapping { "myblog": { "mappings": { "article": { "date_detection": false, "properties": { "id": { "type": "long" }, "postdate": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } } }
2 靜態映射
2.1 基本案例
PUT myblog { "mappings": { "article": { "properties": { "id":{"type": "long"}, "title":{"type": "text"}, "postdate":{"type": "date"} } } } } GET myblog/_mapping PUT myblog/article/1 { "id":1, "title":"elasticsearch is wonderful!", "postdate":"2018-10-27" } GET myblog/_mapping { "myblog": { "mappings": { "article": { "properties": { "id": { "type": "long" }, "postdate": { "type": "date" }, "title": { "type": "text" } } } } } }
2.2 dynamic屬性
默認情況下,當添加一份文檔時,如果出現新的字段,es也會添加進去,不過這個是可以進行控制的,通過dynamic來進行設置:
dynamic值 | 說明 |
---|---|
true | 默認值為true,自動添加字段 |
false | 忽略新的字段 |
strict | 嚴格模式,發現新的字段拋出異常 |
PUT myblog
{
"mappings": {
"article": {
"dynamic":"strict",
"properties": {
"id":{"type": "long"},
"title":{"type": "text"},
"postdate":{"type": "date"}
}
}
}
}
GET myblog/_mapping
PUT myblog/article/1
{
"id":1,
"title":"elasticsearch is wonderful!",
"content":"a long text",
"postdate":"2018-10-27"
}
{
"error": {
"root_cause": [
{
"type": "strict_dynamic_mapping_exception",
"reason": "mapping set to strict, dynamic introduction of [content] within [article] is not allowed"
}
],
"type": "strict_dynamic_mapping_exception",
"reason": "mapping set to strict, dynamic introduction of [content] within [article] is not allowed"
},
"status": 400
}
3 字段類型
3.1 普通字段類型
一級分類 | 二級分類 | 具體類型 |
---|---|---|
核心類型 | 字符串類型 | string、text、keyword |
數字類型 | long、intger、short、byte、double、float、half_float、scaled_float | |
日期類型 | date | |
布爾類型 | boolean | |
二進制類型 | binary | |
範圍類型 | range | |
復合類型 | 數組類型 | array |
對象類型 | object | |
嵌套類型 | nested | |
地理類型 | 地理坐標 | geo_point |
地理圖形 | geo_shape | |
特殊類型 | IP類型 | ip |
範圍類型 | completion | |
令牌計數類型 | token_count | |
附件類型 | attachment | |
抽取類型 | percolator |
下面只會列出一些在個人工作中常用的,詳細的可以參考官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/mapping.html。
3.1.1 string
ex 5.x之後不支持,但仍可以添加,由text或keyword替代。
3.1.2 text
用於做全文搜索的字段,其字段內容會被分詞器分析,在生成倒排索引前,字符串會被分詞器分成一個個的詞項。
實際應用中,text多用在長文本的字段中,如article的content,顯然,這樣的字段用於排序和聚合都是沒有太大意義的。
3.1.3 keyword
只能通過精確值搜索到,區別於text類型。
其索引的詞項都是字段內容本身,因此在實際應用中,會用來比較、排序、聚合等操作。
3.1.4 數字類型
具體註意的細節問題可以考慮官方文檔,一般的使用都能滿足需求。
3.1.5 date
json中沒有日期類型,所以默認情況es的時間的形式可以為:
- 1."yyyy-MM-dd"或"yyyy-MM-ddTHH:mm:ssZ"
- 也就是說"yyyy-MM-dd HH:mm:ss"需要寫成:"2018-10-22T23:12:22Z"的形式,其實就是加了時區;
- 2.表示毫秒的timestamp的長整型數
- 3.表示秒的timestamp的整型數
es內部存儲的是毫秒計時的長整型數。
當然上面只是默認情況下的,在設置字段的類型時,我們也可以設置自己定義的時間格式:
PUT myblog
{
"mappings": {
"article": {
"properties": {
"postdate":{
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
format也可以指定多個日期格式,使用"||"分隔開:
"format": "yyyy-MM-dd HH:mm:ss||yyyy/MM/dd HH:mm:ss"
之後就可以寫入定義的時間格式的數據了:
PUT myblog/article/1
{
"postdate":"2017-09-23 23:12:22"
}
在我的工作場景中,如果需要存入的為時間,很多時候會先把其處理為毫秒值的timestamp,然後再存入es中,取出顯示時再處理為時間字符串。
3.1.6 boolean
設置字段類型為boolean後,可以填入的值為:true、false、"true"、"false"。
3.1.7 binary
binary類型接受base64編碼的字符串。
3.1.8 array
es沒有專用的數組類型,默認情況下任何字段都可以包含一個或者多個值,但是一個數組中的值必須是同一種類型。動態添加數據時,數組的第一個值的類型決定整個數組的類型(其實也就是這個字段的類型),混合數組是不支持的。數組可以包含null值,空數組[]會被當作missing field對待。另外在文檔中使用array類型不需要提前做任何配置,默認支持。
比如添加下面一個數組的字段數據:
DELETE my_index
PUT my_index/my_type/1
{
"lists":[
{
"name":"xpleaf",
"job":"es"
}
]
}
其實際上該字段的類型就會被動態映射為text:
GET my_index/my_type/_mapping
{
"my_index": {
"mappings": {
"my_type": {
"properties": {
"lists": {
"properties": {
"job": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
}
}
直接搜索也是支持的:
GET my_index/my_type/_search
{
"query": {
"term": {
"lists.name": {
"value": "xpleaf"
}
}
}
}
返回結果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.2876821,
"_source": {
"lists": [
{
"name": "xpleaf",
"job": "es"
}
]
}
}
]
}
}
3.1.9 object
可以直接將一個json對象寫入es中,如下:
DELETE my_index
PUT my_index/my_type/1
{
"object":{
"name":"xpleaf",
"job":"es"
}
}
其實際上該字段的類型就會被動態映射為text:
{
"my_index": {
"mappings": {
"my_type": {
"properties": {
"object": {
"properties": {
"job": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
}
}
直接搜索也是可以的:
GET my_index/my_type/_search
{
"query": {
"term": {
"object.name": {
"value": "xpleaf"
}
}
}
}
返回結果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.2876821,
"_source": {
"object": {
"name": "xpleaf",
"job": "es"
}
}
}
]
}
}
object對象,實際上在es內部會被扁平化處理,如上面的,在es中實際為:
{"object.name":"xpleaf", "object.job":"es"}
3.1.10 nested
nested類型是object類型中的一個特例,可以讓對象數組獨立索引和查詢。Lucene沒有內部對象的概念,所以es將對象層次扁平化,轉化成字段名字和值構成的簡單列表。
雖然是object類型中的一個特例,但是其字段的type是固定的,也就是nested,這是與object的最大不同。
那麽為什麽要使用nested類型呢,使用object不就可以了嗎?這裏貼一下官方提供的一個例子來進行說明(https://www.elastic.co/guide/en/elasticsearch/reference/5.6/nested.html):
Arrays of inner object
fields do not work the way you may expect. Lucene has no concept of inner objects, so Elasticsearch flattens object hierarchies into a simple list of field names and values. For instance, the following document:
PUT my_index/my_type/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
would be transformed internally into a document that looks more like this:
{
"group" : "fans",
"user.first" : [ "alice", "john" ],
"user.last" : [ "smith", "white" ]
}
The user.first
and user.last
fields are flattened into multi-value fields, and the association between alice
and white
is lost. This document would incorrectly match a query for alice AND smith
:
GET my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
上面是直接使用object而導致的問題,也就是說實際上進行上面的搜索時,該文檔是不應該被匹配出來的,但是確匹配出來了。使用nested對象類型就可以保持數組中每個對象的獨立性,nested類型將數組中每個對象作為獨立隱藏文檔來索引,這意味著每個嵌套對象都可以獨立被搜索。
If you need to index arrays of objects and to maintain the independence of each object in the array, you should use the nested
datatype instead of the object
datatype. Internally, nested objects index each object in the array as a separate hidden document, meaning that each nested object can be queried independently of the others, with the nested
query:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
}
PUT my_index/my_type/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
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": {}
}
}
}
}
}
}
索引一個包含100個nested字段的文檔實際上就是索引101個文檔,每個嵌套文檔都作為一個獨立文檔來索引。為了防止過度定義嵌套字段的數量,每個索引可以定義的嵌套字段被限制在50個。
3.1.11 range
range類型及其取值範圍如下:
類型 | 範圍 |
---|---|
integer_range | -2^31~2^31-1 |
float_range | 32-bit IEEE 754 |
long_range | -2^63~2^63-1 |
double_range | 64-bit IEEE 754 |
date_range | 64位整數,毫秒計時 |
3.2 元字段
元字段就是描述文檔本身的字段,其分類及說明如下:
元字段分類 | 具體屬性 | 作用 |
---|---|---|
文檔屬性的元字段 | _index | 文檔所屬索引 |
_uid | 包含_type 和_id 的復合字段(取值為{type}#{id} ) |
|
_type | 文檔的類型 | |
_id | 文檔的id | |
源文檔的元字段 | _source | 文檔的原始JSON字符串 |
_size | _source字段的大小 | |
_all | 包含索引全部字段的超級字段 | |
_field_names | 文檔中包含非空值的所有字段 | |
路由的元字段 | _parent | 指定文檔間的父子關系 |
_routing | 將文檔路由到特定分片的自定義路由值 | |
自定義元字段 | _meta | 用於自定義元數據 |
各個字段的詳細說明,可以參考:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/mapping-fields.html。
4 映射參數
參考:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/mapping-params.html。
Elasticsearch常用操作:映射篇