iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
1
Elastic Stack on Cloud

Elastic Stack武學練功坊系列 第 18

Elastic Stack第十八重

  • 分享至 

  • xImage
  •  

Mapping Part VI(結構)

這篇是介紹field data types的第四篇,也會是field type最後一篇
主要介紹object, array以及nested


object field type
JSON本身就是個可以包含階層性的

直接看範例

Reqeust

PUT my-index-000008/_doc/1
{
  "region": "US",
  "manager": { (1)
    "age": 30,
    "name": { (2)
      "first": "John",
      "last":  "Smith"
    }
  }
}

(1): 其中 key manager 的 value 為 json object
(2): name 也是包含了 object

另外說明, object field type 的資料,是會被 flat(攤平) 成 key-value pairs,
以上述新增的範例而言會是以如下方式建立索引

{
  "region": "US",
  "manager.age": 30,
  "manager.name.first": "John",
  "manager.name.last": "Smith"
}

上述範例新增後,es自動建立的index的mapping會是如何?

查看mapping

GET my-index-000008/_mapping

Response
Response

  • 先介紹一下 properties,我們在最上層定義 fields 時,其實就會用到了,如果現在是object,也就是會含有 sub-fields 時,也會需要使用 properties ,如上圖所示, manager 下在定義 agename field 之前會需要先用 properties ,以此類推, manager.name 也含有 properties
  • 發現 manager field為什麼沒有定義他的 field type,實際上 managerobject field type,畢竟有 sub-fields (inner properties), object 是 dafault,所以就不需要額外設定 (一般在建立object時,也都直接用 properties 針對 sub-fields 定義,不會額外定義 "type": "object")

Field capabilities API
可以讓查看indices下的指定fields的能力(capabilities)

Request
GET /_field_caps?fields=<fields>
POST /_field_caps?fields=<fields>
GET /<target>/_field_caps?fields=<fields>
POST /<target>/_field_caps?fields=<fields>

為什麼要突然介紹這個API呢? 因為我想用這個來驗證和查看上述index

查看 index my-index-000008 的 manager為前綴的fields (使用上述第三種)

GET my-index-000008/_field_caps?fields=manager*

Response
Response

在上圖可看到 manager 這邊有顯示 "type" : "object"

Arrays
在es,沒有所謂的 array data type,每一個 field 是可以包含0或多個值得,
但是所有的值必須是同一個 data type

例如:
定義 string field data type,可以輸入 ["one", "two"]

[Note]

  • array內包含了不同data types,則是不允許的,例如: [10, "some string"]
  • 在新增document時,如果field的values為 empty array,則此field是不會被建立至mapping的

直接使用 example 來介紹

PUT my-index-000009/_doc/1
{
  "message": "some arrays in this document...",
  "tags": [ (1)
    "elasticsearch",
    "wow"
  ],
  "lists": [ (2)
    {
      "name": "prog_list",
      "description": "programming list"
    },
    {
      "name": "cool_list",
      "description": "cool stuff list"
    }
  ]
}

PUT my-index-000009/_doc/2 
{
  "message": "no arrays in this document...",
  "tags": "elasticsearch", (3)
  "lists": { (4)
    "name": "prog_list",
    "description": "programming list"
  }
}

(1): 會根據此 tags 產生出 string field data type
(1): 此 lists 產生出 object field data type
(3)(4): 沒有使用 array ,但是 field data type 符合,所以成功新增 document

用search是否可以搜尋array和非array單純string的document?

GET my-index-000009/_search
{
  "query": {
    "match": {
      "tags": "elasticsearch"
    }
  }
}

Response
Response

正確,兩筆document都可以被搜出來

nested field type
object data type 中的一個版本,可以允許 array of objects中,objects之間彼此是獨立的

實際上就是,一般的 object 是會把 ojbect的階層解析為 key-value pair,

可能還是很難懂,直接使用範例

Request

PUT my-index-000010/_doc/1
{
  "group": "fans",
  "user": [ (1)
    {
      "first": "John",
      "last": "Smith"
    },
    {
      "first": "Alice",
      "last": "White"
    }
  ]
}

(1): 此 user 會是 object field data type

而上述範例es會轉化為

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

其實上面介紹 object field type 有提到,
只是上面範例是用 object,這邊用 array of objects

回歸正題,
es把object的階層性轉化為 "user.first""user.last" multi-value fields,
分別用來儲存 user[i].firstuser[i].last 的值,
會發現,這樣會讓原本存在在同一object的關係消失了,
以上述為例,即為 AliceWhite 的關係不存在了

驗證: 搜尋 user.firstAliceuser.lastSmith

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

Response
Response

如上圖所示,是會搜尋得到的,即使當時新增的 AliceSmith 是不在同一個object的,
但因為es的攤平(flat),所以object內的資料的關係消失了

那要如何保留這種關係,也就是維持每一個object在array內的獨立性呢?
沒錯,就是使用 nested data type

題外話,依照目前es預設設定 dynamic field mapping: true
也就是自動判斷field的data type,是不會有 nested data type這個選項的,
如果有興趣關於JSON的data type會對應到es判定的data type可參考reference

所以如下範例是會先

建立index且明確指定mapping

PUT my-index-000011
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested"
      }
    }
  }
}

輸入跟剛剛一樣的範例document

PUT my-index-000011/_doc/1
{
  "group": "fans",
  "user": [
    {
      "first": "John",
      "last": "Smith"
    },
    {
      "first": "Alice",
      "last": "White"
    }
  ]
}

驗證: 搜尋 user.firstAliceuser.lastSmith
補充:搜尋 nested field type 需使用 nested query

GET my-index-000011/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "user.first": "Alice"
              }
            },
            {
              "match": {
                "user.last": "Smith"
              }
            }
          ]
        }
      }
    }
  }
}

Response
Response

是正確的,因為 AliceSmith 卻是不在同一object

驗證: 搜尋 user.firstAliceuser.lastWhite

GET my-index-000011/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "user.first": "Alice"
              }
            },
            {
              "match": {
                "user.last": "White"
              }
            }
          ]
        }
      }
    }
  }
}

Response
Response

正確,AliceWhite 在同一組有被搜出來了


小小新手,如有理解錯誤或寫錯再請不吝提醒或糾正


Reference

Field data types
Field capabilities API
Dynamic field mapping


上一篇
Elastic Stack第十七重
下一篇
Elastic Stack第十九重
系列文
Elastic Stack武學練功坊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言