ES學習第二篇

ES學習第二篇

一、與elastic search的交互

我是Java工程師,相應的我就說我們是怎麼交互的。

1.1 Java API

在代碼中你可以使用 Elasticsearch 內置的兩個客戶端:

1.1.1 節點客戶端

節點客戶端作爲一個非數據節點加入到本地集羣中。換句話說,它本身不保存任何數據,但是它知道數據在集羣中的哪個節點中,並且可以把請求轉發到正確的節點。

1.1.2 傳輸客戶端

輕量級的傳輸客戶端可以將請求發送到遠程集羣。它本身不加入集羣,但是它可以將請求轉發到集羣中的一個節點上。

1.1.3 小總結

兩個 Java 客戶端都是通過 9300 端口並使用 Elasticsearch 的原生 傳輸 協議和集羣交互。集羣中的節點通過端口 9300 彼此通信。如果這個端口沒有打開,節點將無法形成一個集羣。

注意:Java 客戶端作爲節點必須和 Elasticsearch 有相同的 主要 版本;否則,它們之間將無法互相連接。

1.2 RESTful API with JSON over HTTP

所有其他語言可以使用 RESTful API 通過端口 9200 和 Elasticsearch 進行通信,你可以用你最喜愛的 web 客戶端訪問 Elasticsearch 。事實上,正如你所看到的,你甚至可以使用 curl 命令來和 Elasticsearch 交互。

一個 Elasticsearch 請求和任何 HTTP 請求一樣由若干相同的部件組成:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'

< > 標記的部件:

VERB 適當的 HTTP 方法謂詞 : GETPOSTPUTHEAD 或者 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 返回一個 HTTP 狀態碼(例如:200 OK)和(除HEAD請求)一個 JSON 格式的返回值。前面的 curl 請求將返回一個像下面一樣的 JSON 體:

{
    "count" : 0,
    "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
    }
}

縮寫格式就是省略請求中所有相同的部分,例如主機名、端口號以及 curl 命令本身。

curl -XGET 'localhost:9200/_count?pretty' -d '

等價於:

GET /_count

二、文獻檢索

2.1 檢索文檔

目前我們已經在 Elasticsearch 中存儲了一些數據, 接下來就能專注於實現應用的業務需求了。第一個需求是可以檢索到單個僱員的數據。

這在 Elasticsearch 中很簡單。簡單地執行 一個 HTTP GET 請求並指定文檔的地址——索引庫、類型和ID。 使用這三個信息可以返回原始的 JSON 文檔:

GET /megacorp/employee/1

返回結果包含了文檔的一些元數據,以及 _source 屬性,內容是 John Smith 僱員的原始 JSON 文檔:

{
  "_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" ]
  }
}

2.2 輕量搜索

一個 GET 是相當簡單的,可以直接得到指定的文檔。 現在嘗試點兒稍微高級的功能,比如一個簡單的搜索!

第一個嘗試的幾乎是最簡單的搜索了。我們使用下列請求來搜索所有僱員:

GET /megacorp/employee/_search

可以看到,我們仍然使用索引庫 megacorp 以及類型 employee,但與指定一個文檔 ID 不同,這次使用 _search 。返回結果包括了所有三個文檔,放在數組 hits 中。一個搜索默認返回十條結果。

{
   "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:

{
   ...
   "hits": {
      "total":      2,
      "max_score":  0.30685282,
      "hits": [
         {
            ...
            "_source": {
               "first_name":  "John",
               "last_name":   "Smith",
               "age":         25,
               "about":       "I love to go rock climbing",
               "interests": [ "sports", "music" ]
            }
         },
         {
            ...
            "_source": {
               "first_name":  "Jane",
               "last_name":   "Smith",
               "age":         32,
               "about":       "I like to collect rock albums",
               "interests": [ "music" ]
            }
         }
      ]
   }
}

2.3 使用表達式搜索

Elasticsearch 提供一個豐富靈活的查詢語言叫做 查詢表達式 , 它支持構建更加複雜和健壯的查詢。這比輕量搜索更加方便。

領域特定語言 (DSL), 使用 JSON 構造了一個請求。我們可以像這樣重寫之前的查詢所有名爲 Smith 的搜索 :

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}

返回結果與之前的查詢一樣,但還是可以看到有一些變化。其中之一是,不再使用 query-string 參數,而是一個請求體替代。這個請求使用 JSON 構造,並使用了一個 match 查詢(屬於查詢類型之一,後面將繼續介紹)。

2.4 更復雜的搜索

現在嘗試下更復雜的搜索。 同樣搜索姓氏爲 Smith 的員工,但這次我們只需要年齡大於 30 的。查詢需要稍作調整,使用過濾器 filter ,它支持高效地執行一個結構化查詢。

GET /megacorp/employee/_search
{
    "query" : {
        "bool": {
            "must": {
                "match" : {
                   	//這部分與我們之前使用的 match 查詢 一樣。
                    "last_name" : "smith" 
                }
            },
            "filter": {
                "range" : {
                    //這部分是一個 range 過濾器 , 它能找到年齡大於 30 的文檔,其中 gt 表示_大於(_great than)。
                    "age" : { "gt" : 30 } 
                }
            }
        }
    }
}

2.5 全文搜索

截止目前的搜索相對都很簡單:單個姓名,通過年齡過濾。現在嘗試下稍微高級點兒的全文搜索——一項 傳統數據庫確實很難搞定的任務。

搜索下所有喜歡攀巖(rock climbing)的員工:

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}

顯然我們依舊使用之前的 match 查詢在about 屬性上搜索 “rock climbing” 。得到兩個匹配的文檔:

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

其中_source爲相關性得分

2.6 短語檢索

找出一個屬性中的獨立單詞是沒有問題的,但有時候想要精確匹配一系列單詞或者短語 。 比如, 我們想執行這樣一個查詢,僅匹配同時包含 “rock” “climbing” ,並且 二者以短語 “rock climbing” 的形式緊挨着的僱員記錄。

爲此對 match 查詢稍作調整,使用一個叫做 match_phrase 的查詢:

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}

2.7 高亮搜索

許多應用都傾向於在每個搜索結果中 高亮 部分文本片段,以便讓用戶知道爲何該文檔符合查詢條件。在 Elasticsearch 中檢索出高亮片段也很容易。

再次執行前面的查詢,並增加一個新的 highlight 參數:

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

當執行該查詢時,返回結果與之前一樣,與此同時結果中還多了一個叫做 highlight 的部分。這個部分包含了 about 屬性匹配的文本片段,並以 HTML 標籤 <em></em> 封裝:

{
   ...
   "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>" 
               ]
            }
         }
      ]
   }
}

三、分析

終於到了最後一個業務需求:支持管理者對員工目錄做分析。 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
           }
        }
     ]
  }

輸出基本是第一次聚合的加強版。依然有一個興趣及數量的列表,只不過每個興趣都有了一個附加的 avg_age 屬性,代表有這個興趣愛好的所有員工的平均年齡。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章