Elasticsearch 入門介紹

是什麼

Elasticsearch 使用 Java 開發並使用 Lucene 做爲其核心來實現全部索引和搜索的功能,它的目的是經過簡單的 RESTful API 來隱藏 Lucene 的複雜性,從而讓全文搜索變得簡單。node

特色:數據庫

  • 全文搜索
  • 分佈式的實時文件存儲,每一個字段都被索引並可被搜索
  • 分佈式的實時分析搜索引擎
  • 能夠擴展到上百臺服務器,處理PB級結構化或非結構化數據
  • 能夠經過簡單的 RESTful API 與各類開發語言交互
  • 上手簡單,對初學者隱藏了複雜的搜索引擎理論
  • 配置靈活

安裝

Elasticsearch 在安裝以前要安裝 Java ,而後從官網中下載 Elasticsearch 的壓縮包,在本地的自定義的目錄下進行解壓便可。另外有一個 Elasticsearch 的管理和監控工具 Marvel ,它包含了一個叫作 Sense 的交互式控制檯,使用戶方便的經過瀏覽器直接與 Elasticsearch 進行交互。這個不是必需要安裝的。數組

與Elasticsearch交互

  • 若是是用 Java 開發語言,那能夠直接使用 Java API,Elasticsearch 爲 Java 用戶提供了兩種內置客戶端:節點客戶端和傳輸客戶端瀏覽器

  • 以 JSON 爲數據交互格式的 RESTful API ,全部程序語言均可以使用 RESTful API ,經過默認的 9200 端口的與Elasticsearch 進行通訊。向 Elasticsearch 發出的請求的組成部分與其它普通的HTTP請求是同樣的:服務器

    curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
      
      VERB HTTP方法:GET, POST, PUT, HEAD, DELETE
      PROTOCOL http或者https協議(只有在Elasticsearch前面有https代理的時候可用)
      HOST Elasticsearch集羣中的任何一個節點的主機名,若是是在本地的節點,那麼就叫localhost
      PORT Elasticsearch HTTP服務所在的端口,默認爲9200
      PATH API路徑(例如_count將返回集羣中文檔的數量),PATH能夠包含多個組件,例如_cluster/stats或者_nodes/stats/jvm
      QUERY_STRING 一些可選的查詢請求參數,例如?pretty參數將使請求返回更加美觀易讀的JSON數據
      BODY 一個JSON格式的請求主體(若是請求須要的話)
    複製代碼

    舉例:數據結構

    curl -XGET 'http://localhost:9200/_count?pretty' -d '
      {
          "query": {
              "match_all": {}
          }
      }
      	'
    複製代碼

面向文檔

Elasticsearch 是面向文檔 (document oriented) 的,這意味着它能夠存儲整個對象或文檔 (document) 。然而它不只僅是存儲,還會索引 (index) 每一個文檔的內容使之能夠被搜索。在 Elasticsearch 中,你能夠對文檔(而非成行成列的數據)進行索引、搜索、排序、過濾。這種理解數據的方式與以往徹底不一樣,這也是 Elasticsearch 可以執行復雜的全文搜索的緣由之一。負載均衡

ELasticsearch 使用 JSON ,做爲文檔序列化格式。JSON 如今已經被大多語言所支持,並且已經成爲NoSQL領域的標準格式。它簡潔、簡單且容易閱讀。curl

JSON 文檔來表示一個用戶對象:
{
    "email":      "john@smith.com",
    "first_name": "John",
    "last_name":  "Smith",
    "info": {
        "bio":         "Eco-warrior and defender of the weak",
        "age":         25,
        "interests": [ "dolphins", "whales" ]
    },
    "join_date": "2014/05/01"
}
複製代碼

索引

在 Elasticsearch 中,文檔歸屬於一種類型 (type) ,而這些類型存在於索引 (index) 中,咱們能夠畫一些簡單的對比圖來類比傳統關係型數據庫:jvm

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields
Elasticsearch 集羣能夠包含多個索引 (indices) (數據庫),每個索引能夠包含多個類型 (types)(表),每個類型包含多個文檔 (documents)(行),而後每一個文檔包含多個字段 (Fields)(列)。
複製代碼

「索引」的含義:分佈式

索引 (index) 這個詞在 Elasticsearch 中有着不一樣的含義,因此有必要在此作一下區分:
索引(名詞): 如上文所述,一個索引 (index) 就像是傳統關係數據庫中的數據庫,它是相關文檔存儲的地方,index的複數是 indices 或 indexes。
索引(動詞): 「索引一個文檔」表示把一個文檔存儲到索引(名詞)裏,以便它能夠被檢索或者查詢。這很像SQL中的 INSERT 關鍵字,差異是,若是文檔已經存在,新的文檔將覆蓋舊的文檔。
倒排索引:傳統數據庫爲特定列增長一個索引,例如 B-Tree 索引來加速檢索。Elasticsearch 和 Lucene 使用一種叫作倒排索引 (inverted index) 的數據結構來達到相同目的。
複製代碼

增長一個員工信息具體操做案例:

PUT /megacorp/employee/1
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}

其中 megacorp 是索引名,employee 是類型名, 1 是員工 ID。
複製代碼

而後能夠繼續試着添加若干個員工:

PUT /megacorp/employee/2
{
    "first_name" :  "Jane",
    "last_name" :   "Smith",
    "age" :         32,
    "about" :       "I like to collect rock albums",
    "interests":  [ "music" ]
}

PUT /megacorp/employee/3
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}
複製代碼

搜索

  1. 執行 HTTP 的 GET 請求並指出文檔的「地址」——索引、類型和 ID 既可:

    GET /megacorp/employee/1
    複製代碼

    根據這三部分信息,咱們就能夠返回原始 JSON 文檔,響應的內容中包含一些文檔的元信息。John Smith 的原始 JSON 文檔包含在 _source 字段中。

    {
       "_index" :   "megacorp",
       "_type" :    "employee",
       "_id" :      "1",
       "_version" : 1,
       "found" :    true,
       "_source" :  {
           "first_name" :  "John",
           "last_name" :   "Smith",
           "age" :         25,
           "about" :       "I love to go rock climbing",
           "interests":  [ "sports", "music" ]
       }
     }
    複製代碼

    經過 HTTP 方法 GET 來檢索文檔,一樣的,咱們可使用 DELETE 方法刪除文檔,使用 HEAD 方法檢查某文檔是否存在。若是想更新已存在的文檔,咱們只需再 PUT 一次。

  2. 嘗試一個最簡單的搜索所有員工的請求,與上一個命令不一樣指出是在結尾使用關鍵字 _search 來取代原來的文檔 ID 。響應內容的 hits 數組中包含了咱們全部的三個文檔。默認狀況下搜索會返回前 10 個結果:

    GET /megacorp/employee/_search
     
     {
        "took":      6,
        "timed_out": false,
        "_shards": { ... },
        "hits": {
           "total":      3,
           "max_score":  1,
           "hits": [
              {
                 "_index":         "megacorp",
                 "_type":          "employee",
                 "_id":            "3",
                 "_score":         1,
                 "_source": {
                    "first_name":  "Douglas",
                    "last_name":   "Fir",
                    "age":         35,
                    "about":       "I like to build cabinets",
                    "interests": [ "forestry" ]
                 }
              },
              {
                 "_index":         "megacorp",
                 "_type":          "employee",
                 "_id":            "1",
                 "_score":         1,
                 "_source": {
                    "first_name":  "John",
                    "last_name":   "Smith",
                    "age":         25,
                    "about":       "I love to go rock climbing",
                    "interests": [ "sports", "music" ]
                 }
              },
              {
                 "_index":         "megacorp",
                 "_type":          "employee",
                 "_id":            "2",
                 "_score":         1,
                 "_source": {
                    "first_name":  "Jane",
                    "last_name":   "Smith",
                    "age":         32,
                    "about":       "I like to collect rock albums",
                    "interests": [ "music" ]
                 }
              }
           ]
        }
     }
    複製代碼

    若是要搜索姓氏中包含 「Smith」 的員工。能夠在命令行中使用輕量級的搜索方法。這種方法常被稱做查詢字符串 (query string) 搜索,由於咱們像傳遞 URL 參數同樣去傳遞查詢語句:

    GET /megacorp/employee/_search?q=last_name:Smith
    複製代碼

    請求中依舊使用 _search 關鍵字,而後將查詢語句傳遞給參數 q= 。這樣就能夠獲得全部姓氏爲 Smith 的結果。

  3. 查詢字符串搜索便於經過命令行完成特定的搜索,可是它也有侷限性(參閱簡單搜索章節)。Elasticsearch 提供豐富且靈活的查詢語言叫作 DS L查詢 (Query DSL) ,它容許你構建更加複雜、強大的查詢。

    DSL (Domain Specific Language 特定領域語言) 以 JSON 請求體的形式出現。咱們能夠這樣表示以前關於 Smith 的查詢:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match" : {
                 "last_name" : "Smith"
             }
         }
     }
    複製代碼

    返回的結果和以前查詢的結果同樣,可是這裏不使用查詢字符串 (query string) 作爲參數,而是使用請求體代替。這個請求體使用 JSON 表示,其中使用了 match 語句。

  4. 若是須要結合更復雜條件進行搜索,則須要添加過濾器,以下:

    GET /megacorp/employee/_search
     {
         "query" : {
             "filtered" : {
                 "filter" : {
                     "range" : {
                         "age" : { "gt" : 30 } <1>
                     }
                 },
                 "query" : {
                     "match" : {
                         "last_name" : "smith" <2>
                     }
                 }
             }
         }
     }
    複製代碼

    <1> 這部分查詢屬於區間過濾器 (range filter) ,它用於查找全部年齡大於 30 歲的數據—— gt 爲 "greater than" 的縮寫。

    <2> 這部分查詢與以前的 match 語句(query)一致。

    結果中只顯示了一個員工:

    {
        ...
        "hits": {
           "total":      1,
           "max_score":  0.30685282,
           "hits": [
              {
                 ...
                 "_source": {
                    "first_name":  "Jane",
                    "last_name":   "Smith",
                    "age":         32,
                    "about":       "I like to collect rock albums",
                    "interests": [ "music" ]
                 }
              }
           ]
        }
     }
    複製代碼
  5. 到目前爲止搜索都很簡單:搜索特定的名字,經過年齡篩選。接下來可使用一種更高級的搜索,全文搜索——一種傳統數據庫很難實現的功能。搜索全部喜歡 「rock climbing」 的員工:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match" : {
                 "about" : "rock climbing"
             }
         }
     }
    複製代碼

    獲得兩個匹配的結果文檔:

    {
        ...
        "hits": {
           "total":      2,
           "max_score":  0.16273327,
           "hits": [
              {
                 ...
                 "_score":         0.16273327, <1>
                 "_source": {
                    "first_name":  "John",
                    "last_name":   "Smith",
                    "age":         25,
                    "about":       "I love to go rock climbing",
                    "interests": [ "sports", "music" ]
                 }
              },
              {
                 ...
                 "_score":         0.016878016, <2>
                 "_source": {
                    "first_name":  "Jane",
                    "last_name":   "Smith",
                    "age":         32,
                    "about":       "I like to collect rock albums",
                    "interests": [ "music" ]
                 }
              }
           ]
        }
     }
    複製代碼

    默認狀況下,Elasticsearch 根據結果相關性評分來對結果集進行排序,所謂的「結果相關性評分」就是文檔與查詢條件的匹配程度。排名第一的 John Smith 的 about 字段明確的寫到 「rock climbing」 。可是爲何 Jane Smith 也會出如今結果裏呢?緣由是 「rock」 在她的 abuot 字段中被說起了。由於只有 「rock」 被說起而 「climbing」 沒有,因此她的 _score 要低於 John 。 這個例子很好的解釋了 Elasticsearch 如何在各類文本字段中進行全文搜索,而且返回相關性最大的結果集。相關性 (relevance) 的概念在 Elasticsearch 中很是重要,而這個概念在傳統關係型數據庫中是不可想象的,由於傳統數據庫對記錄的查詢只有匹配或者不匹配。

  6. 目前咱們能夠在字段中搜索單獨的一個詞,可是有時候你想要確切的匹配若干個單詞或者短語 (phrases) 。例如咱們想要查詢同時包含 "rock" 和 "climbing" (而且是相鄰的)的員工記錄。 要作到這個,咱們只要將 match 查詢變動爲 match_phrase 查詢便可:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match_phrase" : {
                 "about" : "rock climbing"
             }
         }
     }
    複製代碼

    毫無疑問,該查詢返回John Smith的文檔。

  7. 不少應用喜歡從每一個搜索結果中高亮 (highlight) 匹配到的關鍵字。在 Elasticsearch 中高亮片斷是很是容易的。在語句上增長highlight參數:

    GET /megacorp/employee/_search
     {
         "query" : {
             "match_phrase" : {
                 "about" : "rock climbing"
             }
         },
         "highlight": {
             "fields" : {
                 "about" : {}
             }
         }
     }
    複製代碼

    最後運行會獲得相同的結果,可是在返回結果中會有一個新的部分叫作 highlight ,這裏包含了來自 about 字段中的文本。

    {
        ...
        "hits": {
           "total":      1,
           "max_score":  0.23013961,
           "hits": [
              {
                 ...
                 "_score":         0.23013961,
                 "_source": {
                    "first_name":  "John",
                    "last_name":   "Smith",
                    "age":         25,
                    "about":       "I love to go rock climbing",
                    "interests": [ "sports", "music" ]
                 },
                 "highlight": {
                    "about": [
                       "I love to go <em>rock</em> <em>climbing</em>" <1>
                    ]
                 }
              }
    複製代碼

聚合

Elasticsearch 有一個功能叫作聚合 (aggregations) ,它容許你在數據上生成複雜的分析統計。它很像 SQL 中的 GROUP BY 可是功能更強大。舉例找全部職員中最大的共同點(興趣愛好)是什麼:

GET /megacorp/employee/_search
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" }
    }
  }
}
複製代碼

運行結果:

{
   ...
   "hits": { ... },
   "aggregations": {
      "all_interests": {
         "buckets": [
            {
               "key":       "music",
               "doc_count": 2
            },
            {
               "key":       "forestry",
               "doc_count": 1
            },
            {
               "key":       "sports",
               "doc_count": 1
            }
         ]
      }
   }
}
複製代碼

咱們能夠看到兩個職員對音樂有興趣,一個喜歡林學,一個喜歡運動。這些數據並無被預先計算好,它們是實時的從匹配查詢語句的文檔中動態計算生成的。若是咱們想知道全部姓 "Smith" 的人最大的共同點(興趣愛好),咱們只須要增長合適的語句既可:

GET /megacorp/employee/_search
{
  "query": {
    "match": {
      "last_name": "smith"
    }
  },
  "aggs": {
    "all_interests": {
      "terms": {
        "field": "interests"
      }
    }
  }
}
複製代碼

all_interests 聚合已經變成只包含和查詢語句相匹配的文檔了:

...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2
        },
        {
           "key": "sports",
           "doc_count": 1
        }
     ]
  }
複製代碼

聚合也容許分級彙總。例如讓咱們統計每種興趣下職員的平均年齡:

GET /megacorp/employee/_search
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}
複製代碼

雖然此次返回的聚合結果有些複雜,但任然很容易理解:

...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2,
           "avg_age": {
              "value": 28.5
           }
        },
        {
           "key": "forestry",
           "doc_count": 1,
           "avg_age": {
              "value": 35
           }
        },
        {
           "key": "sports",
           "doc_count": 1,
           "avg_age": {
              "value": 25
           }
        }
     ]
  }
複製代碼

能夠看出經過這個特性能夠完成至關複雜的聚合工做,你能夠處理任何類型的數據。

分佈式的特性

Elasticsearch 在分佈式概念上作了很大程度上的透明化,用戶不須要知道任何關於分佈式系統、分片、集羣發現或者其餘大量的分佈式概念。既能夠運行在你的筆記本上,也能夠運行在擁有100個節點的集羣上,其工做方式是同樣的。

Elasticsearch 致力於隱藏分佈式系統的複雜性。如下這些操做都是在底層自動完成的:

將你的文檔分區到不一樣的容器或者分片 (shards) 中,它們能夠存在於一個或多個節點中。
將分片均勻的分配到各個節點,對索引和搜索作負載均衡。
冗餘每個分片,防止硬件故障形成的數據丟失。
將集羣中任意一個節點上的請求路由到相應數據所在的節點。
不管是增長節點,仍是移除節點,分片均可以作到無縫的擴展和遷移。複製代碼
相關文章
相關標籤/搜索