Elasticsearch 參考指南(探索你的數據)

探索你的數據

樣本數據集

如今咱們已經瞭解了基礎知識,讓咱們嘗試更真實的數據集,我準備了一份關於客戶銀行帳戶信息的虛構JSON文檔樣本,每一個文檔都有如下模式:html

{
    "account_number": 0,
    "balance": 16623,
    "firstname": "Bradshaw",
    "lastname": "Mckenzie",
    "age": 29,
    "gender": "F",
    "address": "244 Columbus Place",
    "employer": "Euron",
    "email": "bradshawmckenzie@euron.com",
    "city": "Hobucken",
    "state": "CO"
}

奇怪的是,這些數據是使用www.json-generator.com/生成的,所以請忽略數據的實際值和語義,由於這些都是隨機生成的。git

加載示例數據集

你能夠從此處下載示例數據集(accounts.json),將它解壓縮到咱們當前的目錄,而後將它們加載到咱們的集羣中,以下所示:github

curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
curl "localhost:9200/_cat/indices?v"

響應以下:json

health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   bank  l7sSYV2cQXmu6_4rJWVIww   5   1       1000            0    128.6kb        128.6kb

這意味着咱們只是成功地將1000個文檔批量索引到bank索引(在_doc類型下)。segmentfault

搜索API

如今讓咱們從一些簡單的搜索開始吧,運行搜索有兩種基本方法:一種是經過REST請求URI發送搜索參數,另外一種是經過REST請求體發送它們。請求體方法容許你更具表現力,並以更易讀的JSON格式定義搜索,咱們將嘗試一個請求URI方法的示例,可是對於本教程的其他部分,咱們將專門使用請求體方法。數組

能夠從_search端點訪問用於搜索的REST API,此示例返回bank索引中的全部文檔:服務器

GET /bank/_search?q=*&sort=account_number:asc&pretty

讓咱們首先剖析搜索調用,咱們在bank索引中搜索(_search端點),q=*參數指示Elasticsearch匹配索引中的全部文檔。sort=account_number:asc參數指示使用每一個文檔的account_number字段升序對結果進行排序,pretty參數再次告訴Elasticsearch返回漂亮的JSON結果。網絡

響應(部分顯示):app

{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : null,
    "hits" : [ {
      "_index" : "bank",
      "_type" : "_doc",
      "_id" : "0",
      "sort": [0],
      "_score" : null,
      "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
    }, {
      "_index" : "bank",
      "_type" : "_doc",
      "_id" : "1",
      "sort": [1],
      "_score" : null,
      "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
    }, ...
    ]
  }
}

至於響應,咱們看如下部分:curl

  • took— Elasticsearch執行搜索的時間(以毫秒爲單位)
  • timed_out — 告訴咱們搜索是否超時
  • _shards — 告訴咱們搜索了多少個碎片,以及搜索成功/失敗碎片的計數
  • hits — 搜索結果
  • hits.total — 符合咱們搜索條件的文檔總數
  • hits.hits — 實際的搜索結果數組(默認爲前10個文檔)
  • hits.sort — 對結果進行排序(若是按分數排序則丟失)
  • hits._scoremax_score — 暫時忽略這些字段

如下是使用替代請求體方法的上述徹底相同的搜索:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

這裏的區別在於,咱們不是在URI中傳遞q=*,而是向_search API提供JSON樣式的查詢請求體,咱們將在下一節討論這個JSON查詢。

重要的是要理解,一旦你得到了搜索結果,Elasticsearch就徹底完成了請求,而且不會在結果中維護任何類型的服務器端資源或打開遊標,這與SQL等許多其餘平臺造成鮮明對比,其中你可能最初預先得到查詢結果的部分子集,而後若是要獲取(或翻頁)其他的則必須連續返回服務器使用某種有狀態服務器端遊標的結果。

查詢語言介紹

Elasticsearch提供了一種JSON樣式的特定於域的語言,可用於執行查詢,這被稱爲Query DSL,查詢語言很是全面,乍一看可能使人生畏,但實際學習它的最佳方法是從一些基本示例開始。

回到上一個例子,咱們執行了這個查詢:

GET /bank/_search
{
  "query": { "match_all": {} }
}

解析上面的內容,query部分告訴咱們查詢定義是什麼,match_all部分只是咱們想要運行的查詢類型,match_all查詢只是搜索指定索引中的全部文檔。

除了query參數,咱們還能夠傳遞其餘參數來影響搜索結果,在上面的部分示例中,咱們傳遞了sort,這裏咱們傳入size

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
}

請注意,若是未指定size,則默認爲10。

此示例執行match_all並返回文檔10到19:

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}

from參數(從0開始)指定從哪一個文檔索引開始,size參數指定從from參數開始返回的文檔數,在實現搜索結果的分頁時,此功能很是有用,請注意,若是未指定from,則默認爲0。

此示例執行match_all並按賬戶balance降序對結果進行排序,並返回前10個(默認大小)文檔。

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}

執行搜索

如今咱們已經看到了一些基本的搜索參數,讓咱們再深刻研究一下Query DSL,咱們先來看一下返回的文檔字段。默認狀況下,完整的JSON文檔做爲全部搜索的一部分返回,這被稱爲源(搜索命中中的_source字段),若是咱們不但願返回整個源文檔,咱們能夠只請求返回源中的幾個字段。

此示例顯示如何從搜索中返回兩個字段,account_numberbalance(在_source內部):

GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}

請注意,上面的示例只是簡化了_source字段,它仍然只返回一個名爲_source的字段,但在其中只包含字段account_numberbalance

若是你有來自SQL背景,則上述內容在概念上與SQL SELECT FROM字段列表有些類似。

如今讓咱們轉到查詢部分,之前,咱們已經看到match_all查詢如何用於匹配全部文檔。如今讓咱們介紹一種稱爲match query的新查詢,它能夠被認爲是基本的字段搜索查詢(即針對特定字段或字段集進行的搜索)。

此示例返回帳戶數爲20的帳戶:

GET /bank/_search
{
  "query": { "match": { "account_number": 20 } }
}

此示例返回地址中包含「mill」項的全部賬戶:

GET /bank/_search
{
  "query": { "match": { "address": "mill" } }
}

此示例返回地址中包含「mill」或「lane」項的全部賬戶:

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}

此示例是matchmatch_phrase)的變體,它返回地址中包含短語「mill lane」的全部賬戶:

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

咱們如今介紹bool querybool查詢容許咱們使用布爾邏輯將較小的查詢組成更大的查詢。

此示例組成兩個match查詢,並返回地址中包含「mill」和「lane」的全部賬戶:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的示例中,bool must子句指定全部必須爲true的查詢才能將文檔視爲匹配項。

相反,此示例組成兩個match查詢並返回地址中包含「mill」或「lane」的全部賬戶:

GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的示例中,bool should子句指定了一個查詢列表,其中的任何一個爲true則將文檔視爲匹配項。

此示例組成兩個匹配查詢,並返回地址中既不包含「mill」也不包含「lane」的全部賬戶:

GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的示例中,bool must_not子句指定了一個查詢列表,對於文檔而言,這些查詢都不能爲true。

咱們能夠在bool查詢中同時組合mustshouldmust_not子句,此外,咱們能夠在任何這些bool子句中組合bool查詢來模仿任何複雜的多級布爾邏輯。

此示例返回任何40歲但不住在ID(aho)的人的全部賬戶:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}

執行過濾器

在上一節中,咱們跳過了一個稱爲文檔分數的小細節(搜索結果中的_score字段),分數是一個數值,它是文檔與咱們指定的搜索查詢匹配程度的相對度量,分數越高,文檔越相關,分數越低,文檔的相關性越低。

可是查詢並不老是須要產生分數,特別是當它們僅用於「過濾」文檔集時,Elasticsearch會檢測這些狀況並自動優化查詢執行,以便不計算無用的分數。

咱們在上一節中介紹的bool query還支持filter子句,這些子句容許使用查詢來限制將與其餘子句匹配的文檔,而不會更改計算得分的方式。做爲一個例子,讓咱們介紹range query,它容許咱們按一系列值過濾文檔,這一般用於數字或日期過濾。

此示例使用bool查詢返回全部餘額介於20000和30000之間的賬戶,換句話說,咱們但願找到餘額大於或等於20000且小於或等於30000的賬戶。

GET /bank/_search
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}

解析上面的內容,bool查詢包含match_all查詢(查詢部分)和range查詢(過濾部分),咱們能夠將任何其餘查詢替換爲查詢和過濾部分,在上述狀況下,範圍查詢很是有意義,由於落入範圍的文檔都「相同」匹配,即,沒有文檔比另外一文檔更相關。

除了match_allmatchboolrange查詢以外,還有不少其餘可用的查詢類型,咱們不會在這裏討論它們,因爲咱們已經基本瞭解它們的工做原理,所以將這些知識應用於學習和試驗其餘查詢類型應該不會太困難。

執行聚合

聚合提供了從數據中分組和提取統計信息的能力,考慮聚合的最簡單方法是將其大體等同於SQL GROUP BY和SQL聚合函數。在Elasticsearch中,你能夠執行返回匹配的搜索,同時在一個響應中返回與命中相關的聚合結果。這是很是強大和高效的,由於你能夠運行查詢和多個聚合,並一次性獲取兩個(或任一)操做的結果,避免使用簡潔和簡化的API進行網絡往返。

首先,此示例按state對全部賬戶進行分組,而後返回前10個(默認)state,按計數降序排序(也是默認值):

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      }
    }
  }
}

在SQL中,上述聚合在概念上相似於:

SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;

響應(部分顯示):

{
  "took": 29,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "group_by_state" : {
      "doc_count_error_upper_bound": 20,
      "sum_other_doc_count": 770,
      "buckets" : [ {
        "key" : "ID",
        "doc_count" : 27
      }, {
        "key" : "TX",
        "doc_count" : 27
      }, {
        "key" : "AL",
        "doc_count" : 25
      }, {
        "key" : "MD",
        "doc_count" : 25
      }, {
        "key" : "TN",
        "doc_count" : 23
      }, {
        "key" : "MA",
        "doc_count" : 21
      }, {
        "key" : "NC",
        "doc_count" : 21
      }, {
        "key" : "ND",
        "doc_count" : 21
      }, {
        "key" : "ME",
        "doc_count" : 20
      }, {
        "key" : "MO",
        "doc_count" : 20
      } ]
    }
  }
}

咱們能夠看到ID(Idaho)有27個帳戶,其次是TX(Texas)的27個帳戶,其次是AL(Alabama)的25個帳戶,依此類推。

請注意,咱們將size=0設置爲不顯示搜索命中,由於咱們只想在響應中看到聚合結果。

在前一個聚合的基礎上,此示例按state計算平均賬戶餘額(一樣僅針對按降序排序的前10個state):

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

請注意咱們如何嵌套group_by_state聚合中的average_balance聚合,這是全部聚合的常見模式,你能夠在聚合中任意嵌套聚合,以從數據中提取所需的輪轉摘要。

在前一個聚合的基礎上,咱們如今按降序排列平均餘額:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

此示例演示了咱們如何按年齡段(20-29歲,30-39歲和40-49歲)進行分組,而後按性別分組,最後獲得每一個年齡段的平均賬戶餘額:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  }
}

還有許多其餘聚合功能,咱們在此再也不詳述,若是你想進行進一步的實驗,聚合參考指南是一個很好的起點。


上一篇:修改你的數據

下一篇:安裝Elasticsearch

相關文章
相關標籤/搜索