elasticsearch的object型別和動態對映

丹江怒潮發表於2017-08-08

我們需要討論的最後一個自然JSON資料型別是物件(object)——在其它語言中叫做hash、hashmap、dictionary 或者 associative array.

內部物件(inner objects)經常用於在另一個物件中嵌入一個實體或物件。例如,做為在tweet文件中user_nameuser_id的替代,我們可以這樣寫:

{
    "tweet":            "Elasticsearch is very flexible",
    "user": {
        "id":           "@johnsmith",
        "gender":       "male",
        "age":          26,
        "name": {
            "full":     "John Smith",
            "first":    "John",
            "last":     "Smith"
        }
    }
}

內部物件的對映

Elasticsearch 會動態的檢測新物件的欄位,並且對映它們為 object 型別,將每個欄位加到 properties欄位下

{
  "gb": {
    "tweet": { <1>
      "properties": {
        "tweet":            { "type": "string" },
        "user": { <2>
          "type":             "object",
          "properties": {
            "id":           { "type": "string" },
            "gender":       { "type": "string" },
            "age":          { "type": "long"   },
            "name":   { <3>
              "type":         "object",
              "properties": {
                "full":     { "type": "string" },
                "first":    { "type": "string" },
                "last":     { "type": "string" }
              }
            }
          }
        }
      }
    }
  }
}

<1> 根物件.

<2><3> 內部物件.

username欄位的對映與tweet型別自己很相似。事實上,type對映只是object對映的一種特殊型別,我們將 object 稱為根物件。它與其他物件一模一樣,除非它有一些特殊的頂層欄位,比如 _source,_all 等等。

內部物件是怎樣被索引的

Lucene 並不瞭解內部物件。 一個 Lucene 檔案包含一個鍵-值對應的扁平表單。 為了讓 Elasticsearch 可以有效的索引內部物件,將檔案轉換為以下格式:

{
    "tweet":            [elasticsearch, flexible, very],
    "user.id":          [@johnsmith],
    "user.gender":      [male],
    "user.age":         [26],
    "user.name.full":   [john, smith],
    "user.name.first":  [john],
    "user.name.last":   [smith]
}

內部欄位可被歸類至name,例如"first"。 為了區別兩個擁有相同名字的欄位,我們可以使用完整路徑,例如"user.name.first" 或甚至型別名稱加上路徑:"tweet.user.name.first"

注意: 在以上扁平化檔案中,並沒有欄位叫作user也沒有欄位叫作user.name。 Lucene 只索引階層或簡單的值,而不會索引複雜的資料結構。

動態對映

當 Elasticsearch 處理一個位置的欄位時,它通過【動態對映】來確定欄位的資料型別且自動將該欄位加到型別對映中。

有時這是理想的行為,有時卻不是。或許你不知道今後會有哪些欄位加到文件中,但是你希望它們能自動被索引。或許你僅僅想忽略它們。特別是當你使用 Elasticsearch 作為主資料來源時,你希望未知欄位能丟擲一個異常來警示你。

幸運的是,你可以通過 dynamic 設定來控制這些行為,它接受下面幾個選項:

true:自動新增欄位(預設)

false:忽略欄位

strict:當遇到未知欄位時丟擲異常

dynamic 設定可以用在根物件或任何 object 物件上。你可以將 dynamic 預設設定為 strict,而在特定內部物件上啟用它:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "dynamic":      "strict", <1>
            "properties": {
                "title":  { "type": "string"},
                "stash":  {
                    "type":     "object",
                    "dynamic":  true <2>
                }
            }
        }
    }
}

<1> 當遇到未知欄位時,my_type 物件將會丟擲異常

<2> stash 物件會自動建立欄位

通過這個對映,你可以新增一個新的可搜尋欄位到 stash 物件中:

PUT /my_index/my_type/1
{
    "title":   "This doc adds a new field",
    "stash": { "new_field": "Success!" }
}

但是在頂層做同樣的操作則會失敗:

PUT /my_index/my_type/1
{
    "title":     "This throws a StrictDynamicMappingException",
    "new_field": "Fail!"
}

備註:將 dynamic 設定成 false 完全不會修改 _source 欄位的內容。_source 將仍舊保持你索引時的完整 JSON 文件。然而,沒有被新增到對映的未知欄位將不可被搜尋。


+

相關文章