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的索引映射
View Code
對於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 ListList<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] } }}
參考文檔:
Elasticsearch Reference [2.4] » Mapping » Field datatypes
Elasticsearch Reference [2.4] » Query DSL » Joining queries » Nested Query