1. 程式人生 > >Elasticsearch常用操作:映射篇

Elasticsearch常用操作:映射篇

數組類 white elastic concept 分詞 路由 ati dsl object

[TOC]


其實就是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常用操作:映射篇