1. 程式人生 > >elasticsearch複合資料型別——陣列,物件和巢狀

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]
      }
   }
}

複製程式碼

參考文件:

作者悅光陰

本文版權歸作者和部落格園所有,歡迎轉載,但未經作者同意,必須保留此段宣告,且在文章頁面醒目位置顯示原文連線,否則保留追究法律責任的權利。