elasticsearch複合資料型別——陣列,物件和巢狀
在ElasticSearch中,使用JSON結構來儲存資料,一個Key/Value對是JSON的一個欄位,而Value可以是基礎資料型別,也可以是陣列,文件(也叫物件),或文件陣列,因此,每個JSON文件都內在地具有層次結構。複合資料型別是指陣列型別,物件型別和巢狀型別,各個型別的特點分別是:
- 陣列欄位是指一個欄位有多個值,每個值都是該陣列欄位的一個元素;元素的型別可以是基礎型別,也可以是文件型別;
- 物件型別是指欄位的值是一個JSON文件;
- 巢狀欄位是指物件型別的一個特殊版本,ElasticSearch引擎在內部把巢狀欄位索引成單個文件。如果在巢狀欄位中定義物件陣列,那麼物件陣列中的每個元素(文件)都被索引成單個文件,每個文件都能被獨立地查詢。
一,物件型別
JSON文件是有層次結構的,一個文件可能包含其他文件,如果一個文件包含其他文件,那麼該文件值是物件型別,其資料型別是物件,ElasticSearch預設把文件的屬性type設定為object,即"type":"object"。
例如,在建立索引映時,定義name欄位為物件型別,不需要顯式定義type屬性值,其預設值是object:
"manager":{ "properties":{ "age":{ "type":"integer"}, "name":{ "properties":{ "first":{"type":"string"}, "last":{ "type":"string"} } } } }
預設情況下,上述文件型別被索引為以點號命名的資料結構,把層次結構展開之後,資料結構是由扁平的key/value對構成:
{ "manager.age": 30, "manager.name.first": "John", "manager.name.last": "Smith" }
二,開箱即用的陣列型別
在ElasticSearch中,沒有專門的陣列(Array)資料型別,但是,在預設情況下,任意一個欄位都可以包含0或多個值,這意味著每個欄位預設都是陣列型別,只不過,陣列型別的各個元素值的資料型別必須相同。在ElasticSearch中,陣列是開箱即用的(out of box),不需要進行任何配置,就可以直接使用。
1,陣列型別
在同一個陣列中,陣列元素的資料型別是相同的,ElasticSearch不支援元素為多個數據型別:[ 10, "some string" ],常用的陣列型別是:
- 字元陣列: [ "one", "two" ]
- 整數陣列: productid:[ 1, 2 ]
- 物件(文件)陣列: "user":[ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }],ElasticSearch內部把物件陣列展開為 {"user.name": ["Mary", "John"], "user.age": [12,10]}
對於文件陣列,每個元素都是結構相同的文件,文件之間都不是獨立的,在文件陣列中,不能獨立於其他文件而去查詢單個文件,這是因為,一個文件的內部欄位之間的關聯被移除,各個文件共同構成物件陣列。
對整數陣列進行查詢,例如,使用多詞條(terms)查詢型別,查詢productid為1和2的文件:
{ "query":{ "terms":{ "productid":[ 1, 2 ] } } }
2,物件陣列
通過PUT動詞,自動建立索引和文件型別,在文件中建立物件陣列:
PUT my_index/my_type/1 { "group" : "fans", "user" : [ { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] }
ElasticSearch引擎內部把物件陣列展開成扁平的資料結構,把上例的文件型別的資料結構展開之後,文件資料類似於:
{ "group" : "fans", "user.first" : [ "alice", "john" ], "user.last" : [ "smith", "white" ] }
欄位 user.first 和 user.last 被展開成陣列欄位,但是,這樣展開之後,單個文件內部的欄位之間的關聯就會丟失,在該例中,展開的文件資料丟失first和last欄位之間的關聯,比如,Alice
和 white
的關聯就丟失了。
三,巢狀資料型別
巢狀資料型別是物件資料型別的特殊版本,它允許物件陣列中的各個物件被索引,陣列中的各個物件之間保持獨立,能夠對每一個文件進行單獨查詢,這就意味著,巢狀資料型別保留文件的內部之間的關聯,ElasticSearch引擎內部使用不同的方式處理巢狀資料型別和物件陣列的方式,對於巢狀資料型別,ElasticSearch把陣列中的每一個巢狀文件(Nested Document)索引為單個文件,這些文件是隱藏(Hidden)的,文件之間是相互獨立的,但是,保留文件的內部欄位之間的關聯,使用巢狀查詢(Nested Query)能夠獨立於其他文件而去查詢單個文件。在建立巢狀資料型別的欄位時,需要設定欄位的type屬性為nested。
1,在索引對映中建立巢狀欄位
設定user欄位為巢狀資料型別,由於每個欄位預設都可以是陣列型別,因此,巢狀欄位也可以是物件陣列。
"mappings":{ "my_type":{ "properties":{ "group":{ "type":"string"}, "user":{ "type":"nested", "properties":{ "first":{ "type":"string"}, "second":{ "type":"string"} } } } } }
2,為巢狀欄位賦值
為巢狀欄位賦予多個值,那麼ElasticSearch自動把欄位值轉換為陣列型別。
PUT my_index/my_type/1 { "group" : "fans", "user" : [ { "first" : "John", "last" : "Smith"}, { "first" : "Alice", "last" : "White"} ] }
在ElasticSearch內部,巢狀的文件(Nested Documents)被索引為很多獨立的隱藏文件(separate documents),這些隱藏文件只能通過巢狀查詢(Nested Query)訪問。每一個巢狀的文件都是巢狀欄位(文件陣列)的一個元素。巢狀文件的內部欄位之間的關聯被ElasticSearch引擎保留,而巢狀文件之間是相互獨立的。在該例中,ElasticSearch引起保留Alice和White之間的關聯,而John和White之間是沒有任何關聯的。
預設情況下,每個索引最多建立50個巢狀文件,可以通過索引設定選項:index.mapping.nested_fields.limit 修改預設的限制。
Indexing a document with 100 nested fields actually indexes 101 documents as each nested document is indexed as a separate document.
四,巢狀查詢
巢狀查詢用於查詢巢狀物件,執行巢狀查詢執行的條件是:巢狀物件被索引為單個文件,查詢作用在根文件(Root Parent)上。巢狀查詢由關鍵字“nested”指定:
"nested" : { "path" : "obj1", "query" : {...}
1,必須賦值的引數:
- path引數:指定巢狀欄位的文件路徑,根路徑是頂層的文件,通過點號“.”來指定巢狀文件的路徑;
- query引數:在匹配路徑(引數path)的巢狀文件上執行查詢,query引數指定對巢狀文件執行的查詢條件。
2,使用巢狀查詢訪問巢狀文件
GET my_index/_search { "query": { "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" }}, { "match": { "user.last": "White" }} ] } } } } }
五,使用C#索引陣列型別
1,建立ElasticSearch的索引對映
{ "settings":{ "number_of_shards":5, "number_of_replicas":0 }, "mappings":{ "events":{ "dynamic":"false", "properties":{ "eventid":{ "type":"long", "store":true, "index":"not_analyzed" }, "eventname":{ "type":"string", "store":true, "index":"analyzed", "fields":{ "raw":{ "type":"string", "store":true, "index":"not_analyzed" } } }, "topics":{ "type":"integer", "store":true, "index":"analyzed" } } } } }
對於topics欄位,型別是integer,賦予其一組整數值[1,2,3],那麼該欄位就能儲存陣列。
"topics":{ "type":"integer", "store":true, "index":"analyzed" }
2,建立資料模型(Data Model)
為陣列欄位定義為List型別,每個列表項的資料型別是int。
public class EventBase { public long eventid { get; set; } } public class EbrieEvents:EventBase { public string eventname { get; set; } public List<int> topics { get; set; } }
3,為欄位賦值
為List欄位topics賦值,呼叫NEST對該文件進行索引
EbrieEvents pb = new EbrieEvents(); //Topics List List<string> strTopics = TableRow["Topics"].ToString().TrimEnd(',').Split(',').ToList(); List<int> topics = new List<int>(); foreach(string str in strTopics) { topics.Add(int.Parse(str)); } pb.topics = topics;
4,查詢陣列欄位
{ "query":{ "terms":{ "topics":[1001,487] } } }
參考文件:
作者:悅光陰
本文版權歸作者和部落格園所有,歡迎轉載,但未經作者同意,必須保留此段宣告,且在文章頁面醒目位置顯示原文連線,否則保留追究法律責任的權利。