1. 程式人生 > >ElasticSearch的Nested(巢狀)資料型別

ElasticSearch的Nested(巢狀)資料型別

[[nested]]
=== Nested(巢狀)資料型別

nested 型別是一種特殊的 [object,object] 型別.
這種型別允許對 object 陣列內的元素進行單獨查詢.

object 陣列是怎麼展開(flatten)的

[object,object] 陣列的功能可能跟你想象中的不太一樣.
Lucene 沒有內部 object 的概念, 所以 Elasticsearch 內部會把 object 解析成簡單的欄位名與值的資訊,
以下面這個文件為例:

[source,js]

PUT my_index/_doc/1
{
  "group" : "fans",
  "user"
: [ <1> { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] }

<1> user 被欄位配置為 object 欄位.

內部會把文件轉化為類似與下面這種形式:

[source,js]

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith"
, "white" ]
}

user.firstuser.last 被展開成多值欄位,
而且 alicewhite 之間的關係被丟棄了.
這個文件可能會錯誤地匹配 alice AND smith.

[source,js]

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}

將 object 陣列欄位配置為 nested

如果你需要保留 object 陣列中每個物件的內部關係, 則需要使用 nested 型別, 而不是 [object,object] 型別.
Elasticsearch 內部會把陣列中的每個元素當成一個隱藏文件, 所以可以用 [query-dsl-nested-query,nested query]
單獨對每個元素進行查詢:

[source,js]

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "user": {
          "type": "nested" <1>
        }
      }
    }
  }
}

PUT my_index/_doc/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" }} <2>
          ]
        }
      }
    }
  }
}

GET my_index/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "White" }} <3>
          ]
        }
      },
      "inner_hits": { <4>
        "highlight": {
          "fields": {
            "user.first": {}
          }
        }
      }
    }
  }
}

<1> user 被配置為 nested 而不是 object.
<2> 無法查詢到資料, 因為 AliceSmith 不在同一個 nested object.
<3> 可以查詢到資料, 因為 AliceWhite 在相同一個 nested object.
<4> inner_hits 允許高亮匹配到的 nested 文件.

Nested 文件可以:

  • 用 [query-dsl-nested-query,nested] 進行查詢.
  • 用 [search-aggregations-bucket-nested-aggregation,nested]
    和 [search-aggregations-bucket-reverse-nested-aggregation, reverse_nested]
    進行聚合分析.
  • 用 [nested-sorting,nested sorting] 進行排序.
  • 用 [nested-inner-hits,nested inner hits] 獲取和高亮.

[[nested-params]]

nested 的引數

nested 欄位支援以下引數:

[horizontal]
[dynamic,dynamic]:

是否把新的 `屬性` 自動新增到已有的巢狀物件.  接收 `true` (預設), `false` 和 `strict`.

[properties,properties]:

巢狀物件包含的欄位, 可以是任意的
[mapping-types,資料型別], 包括 `nested`, 新的 `屬性` 可能會被新增到已有的巢狀物件.

[IMPORTANT]

因為 nested 文件被索引為單獨的文件, 所以只能使用 nested 查詢, nested/reverse_nested 聚合, 或者用 [nested-inner-hits,nested inner hits].

例如, 對一個 nested 文件中的 string 欄位用 [index-options,index_options] 對 offsets 進行設定, 是沒有用的.
需要用 [nested-inner-hits,nested inner hits].

=============================================

限制 nested 欄位的數量

索引一個有 100個 nested 欄位的文件, 實際上需要索引 101 個文件, 因為每個巢狀物件會被當成一個隱藏文件.
為了防止某些錯誤配置的 mapping, nested 欄位的數量可以限制為每個 index 最多 50 個. 詳情參考 [mapping-limit-settings].