在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 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] } } }
實際代碼:
PUT /product POST /product/doc/_mapping { "_source": {"enabled": true}, "dynamic": true, "properties" : { "id" : {"type" : "text"}, "hash" : {"type" : "text"}, "name" : {"type" : "text","analyzer" : "ik_max_word","norms":false}, "type" : {"type" : "keyword","doc_values":true}, "info" : { "properties":{ "label":{"type":"keyword","doc_values":true}, "format":{ "type":"keyword","doc_values":true}, "id":{ "type":"text"}, "value" : {"type":"text"} } } } } PUT product/doc/1 { "id": "5f701fddf9a5151bc718d3fb", "hash":"65a0b117dcd0bc8cee3564b51f418bab", "name" : "Antitheft", "type" : "基礎數據", "info" : [ { "label" : "姓名", "format" : "names", "id" : "ef12a8a50fe0c94b346747599bf8296a", "value" : [ "Antitheft", "edg" ] }, { "label" : "電話號碼", "format" : "phone", "id" : "458fbb7ca7fb59ccf98c2d9088a241cb", "value" : [ "1-8427279327" ] }, { "label" : "唯一鍵", "format" : "uuid", "id" : "f08fb53efe9d6d4c0e4e2320c586c425", "value" : [ "gcHzB" ] }, { "label" : "ID", "format" : "id", "id" : "aeacd0a458a35938b095d47ca9c398a4", "value" : [ 2 ] }, { "label" : "位置", "format" : "location", "id" : "530d8bf12fe04a5b4fab309ceb72b9e5", "value" : [ "美國" ] }, { "label" : "郵箱", "format" : "email", "id" : "f58ad384a37dce190fccb6e7d4c9e051", "value" : [ null ] }, { "label" : "平台", "format" : "platform", "id" : "5566e4c3d3a3f8bc95bc2c2622c99030", "value" : [ "社交平台;viber;通信" ] }, { "label" : "呼叫總數", "format" : "contact_count", "id" : "866e88d0c3e02101940e518e4cb03ab3", "value" : [ 0 ] }, { "label" : "被呼叫總數", "format" : "be_contact_count", "id" : "777eb09f71b66bcafc050204328b780a", "value" : [ 1 ] }, { "label" : "總呼叫數", "format" : "all_contact_count", "id" : "977875a99eaa4b89f3fae271fae9ff71", "value" : [ 1 ] }, { "label" : "消息總數", "format" : "msg_count", "id" : "872c23c6dbb1b121e6c381250de8e544", "value" : [ 0 ] }, { "label" : "其他數據", "format" : "else_data", "id" : "dd2628ef46d7c66728ca58f22edf2f5d", "value" : [ "{}" ] } ] }
GET product/doc/_search { "_source": ["info.format", "info.value"], "size":1, "query": { "bool": { "must": [ { "match": { "info.format": "names" }}, { "match": { "info.value": "edg" }} ] } } }

