Elasticsearch學習筆記(3)

目錄

探索你的數(shù)據(jù)

現(xiàn)在我們已經(jīng)了解了基礎(chǔ)知識,讓我們嘗試處理一個更真實的數(shù)據(jù)集。

加載示例數(shù)據(jù)集

我們可以從這里下載數(shù)據(jù)集。將它下載保存到我們的當前目錄并且加載到我們的集群里,如下:

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

響應(yīng)格式:

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類型下)。

搜索API

現(xiàn)在讓我們從簡單的搜索開始。這里有兩個基本的辦法去運行搜索:其一是通過REST request URI發(fā)送搜索參數(shù),另一個是通過REST request body發(fā)送搜索參數(shù)。請求體方法允許您更有表現(xiàn)力,也可以用更可讀的JSON格式定義搜索。我們將嘗試請求URI方法的一個示例,但是對于本教程的其余部分,我們將只使用請求主體方法。

搜索的REST API可以從_search端點訪問。這個示例返回銀行索引中的所有文檔:

curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty"

讓我們首先分析搜索調(diào)用。我們在bank索引中搜索(_search端點),q=參數(shù)指示Elasticsearch匹配索引中的所有文檔。sort=account_number:asc參數(shù)指示使用每個文檔的account_number字段按升序?qū)Y(jié)果進行排序。同樣,pretty參數(shù)只是告訴Elasticsearch返回漂亮的JSON結(jié)果。
響應(yīng)(部分顯示):

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

關(guān)于響應(yīng),我們看到以下部分:

  • took - Elasticsearch執(zhí)行搜索的時間以毫秒為單位
  • time_out - 告訴我們這個搜索是否超時
  • _shards - 告訴我們搜索了多少碎片,以及成功/失敗搜索碎片的計數(shù)
  • hist - 搜索結(jié)果
  • hits.total - 符合我們的搜索條件的文檔總數(shù)
  • hits.hits - 實際的搜索結(jié)果數(shù)組(默認為前10個文檔)
  • hits.sort - 結(jié)果的排序鍵(如果按分數(shù)排序,則會丟失)
  • hits._score and max_score - 暫時忽略這些字段

下面是使用替代請求主體方法進行的相同搜索:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}
'

這里的區(qū)別在于,我們沒有在URI中傳遞q=*,而是向_search API提供json風格的查詢請求體。我們將在下一節(jié)討論這個JSON查詢。
很重要的一點:一旦返回搜索結(jié)果,Elasticsearch就完全完成了對請求的處理,不會在結(jié)果中維護任何類型的服務(wù)器端資源或打開游標。這與許多其他平臺如SQL形成鮮明對比,你最初可能會得到查詢結(jié)果的部分子集,然后如果你想獲取其余的結(jié)果,必須使用某種狀態(tài)的服務(wù)器端游標不斷到服務(wù)器去獲取。

介紹查詢語言

Elasticsearch提供了一種json風格的域特定語言,您可以使用這種語言執(zhí)行查詢。這稱為DSL查詢語言。查詢語言非常全面,乍一看可能有些嚇人, 但實際上最好的學習辦法是從幾個基本示例開始。
回到上一個例子,我們執(zhí)行了這個查詢:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} }
}
'

分析上面,query部分告訴我們查詢定義是什么,match_all部分只是我們想要運行的查詢類型。match_all查詢只是在指定索引中搜索所有文檔。

除了查詢參數(shù)外,我們還可以傳遞其他參數(shù)來影響搜索結(jié)果。在上面部分的例子中,我們傳遞了sort,這里我們傳遞了size

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "size": 1
}
'

注意,如果沒有指定大小,默認值為10。
這個示例執(zhí)行match_all并返回文檔10到19:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}
'

from參數(shù)(基于0)指定從哪個文檔索引開始,size參數(shù)指定從from參數(shù)開始返回多少文檔。這個特性在實現(xiàn)搜索結(jié)果分頁時非常有用。注意,如果未指定from,則默認為0。

這個示例執(zhí)行match_all,并按照帳戶余額降序?qū)Y(jié)果進行排序,并返回前10個(默認大小)文檔。

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}
'
執(zhí)行搜索

現(xiàn)在我們已經(jīng)看到了一些基本的搜索參數(shù),讓我們更深入一些到DSL查詢語句中。首先讓我們看一下返回的文檔字段。默認情形下,完整的JSON文檔是返回的所有查詢結(jié)果的一部分。這稱為源(搜索命中的_source字段)。如果我們不想整個的源文檔返回,我們能夠從源文檔中請求只返回幾個字段。
這個示例展示了如何從查詢中返回兩個字段,account_numberbalance(在_source字段中):

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}
'

注意,上面的示例只是減少了_source字段。它仍然只返回一個名為_source的字段,但其中只包含account_number和balance字段。
如果你有SQL的背景,上面的查詢在概念上是和SQL語言,SELECT FROM 字段列表有點相似的。
現(xiàn)在讓我們繼續(xù)查詢部分,之前,我們已經(jīng)看到match_all是如何被用來匹配所有文檔的?,F(xiàn)在讓我們介紹一個新的查詢叫做match query,可以將其視為基本的字段搜索查詢(即針對特定字段或字段集進行的搜索)。
這個示例返回編號為20的帳戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "account_number": 20 } }
}
'

這個示例返回地址中包含術(shù)語“mill”的所有帳戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "address": "mill" } }
}
'

這個示例返回地址中包含術(shù)語“mill”或“l(fā)ane”的所有帳戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "address": "mill lane" } }
}
'

這個示例是match (match_phrase)的變體,它返回地址中包含短語“mill lane”的所有帳戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": { "match_phrase": { "address": "mill lane" } }
}
'

現(xiàn)在讓我們介紹bool query。bool查詢允許我們使用布爾邏輯將較小的查詢組合成較大的查詢。
這個示例組合兩個match查詢并且返回地址中包含“mill”和“l(fā)ane”的所有賬戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'

在上面的示例中,bool must子句指定所有查詢,這些查詢必須為true,才能將文檔視為匹配。
與此相反,這個示例包含兩個匹配查詢,并返回地址中包含“mill”或“l(fā)ane”的所有帳戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'

在上面的示例中,bool should子句指定了一個查詢列表,其中一個查詢列表必須為true,才能將文檔視為匹配。
這個示例包含兩個匹配查詢,并返回地址中既不包含“mill”也不包含“l(fā)ane”的所有帳戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'

在上面的例子中,bool must_not子句指定了一個查詢列表,其中沒有一個查詢必須為真,才能將文檔視為匹配。

我們可以在bool查詢中同時組合must、should和must_not子句。
此外,我們可以在任何bool子句中編寫bool查詢,以模擬任何復(fù)雜的多級布爾邏輯。
這個例子返回了所有40歲但不生活在ID(aho)中的人的賬戶:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}
'
執(zhí)行過濾器

在上一節(jié)中,我們跳過了一個名為文檔分數(shù)(搜索結(jié)果中的_score字段)的小細節(jié)。分數(shù)是一個數(shù)值,它是文檔與我們指定的搜索查詢匹配程度的相對度量。分數(shù)越高,文檔越相關(guān),分數(shù)越低,文檔越不相關(guān)。但是查詢并不總是需要產(chǎn)生分數(shù),特別是當它們僅用于“過濾”文檔集時。Elasticsearch可以檢測到這些情況,并自動優(yōu)化查詢執(zhí)行,以避免計算無用的分數(shù)。我們上一節(jié)介紹的bool query也支持filter子句,它允許我們使用一個查詢來限制由其他子句匹配的文檔,不改變?nèi)绾斡嬎惴謹?shù)。作為一個例子,讓我們引入range query,它允許我們通過一系列值篩選文檔。這通常用于數(shù)字或日期過濾。

這個示例使用bool查詢返回所有余額在20000到30000之間的帳戶(包括在內(nèi))。換句話說,我們希望找到的賬戶余額大于等于20000,小于等于30000。

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}
'

分析上面的語句,bool查詢包含一個match_all子查詢(查詢部分)和一個range子查詢(過濾部分)。我們可以代替任意其他的查詢到這個查詢部分和過濾部分。在上面的例子中,范圍查詢非常有意義,因為屬于范圍的所有文檔都“相等”匹配,也就是說, 沒有哪個文檔是比其他文檔更相關(guān)的。

除了match_all、matchboolrange查詢之外,還有許多其他的查詢類型可供使用,我們在這里不深入討論。由于我們已經(jīng)對它們的工作原理有了基本的了解,因此在學習和試驗其他查詢類型時應(yīng)用這些知識應(yīng)該不會太難。

執(zhí)行聚合

聚合提供了從你的數(shù)據(jù)中分組和提取統(tǒng)計信息的能力。關(guān)于聚合最簡單的理解方式就是將其大致等同于SQL GROUP BY和SQL聚合函數(shù)。在Elasticsearch中,您可以執(zhí)行返回命中的搜索,同時在一個響應(yīng)中返回與命中分離的聚合結(jié)果。這在某種意義上非常強大和高效,因為您可以運行查詢和多個聚合,并一次性獲得兩個(或兩個)操作的結(jié)果,從而避免使用簡潔和簡化的API進行網(wǎng)絡(luò)往返。

首先,這個示例按狀態(tài)對所有帳戶進行分組,然后返回按計數(shù)遞減排序的前10個(默認)狀態(tài):

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "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;

部分響應(yīng)內(nèi)容:

{
  "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(愛達荷州)有27個賬戶,緊跟著TX(德克薩斯州)有27個賬戶,緊跟著AL(阿拉巴馬州)有25個賬戶,等等。

注意:我們設(shè)置size=0來不展示搜索結(jié)果因為我們只想在返回內(nèi)容里看到聚合結(jié)果。

在前面的聚合基礎(chǔ)上,本示例計算了按狀態(tài)排序的平均帳戶余額(同樣,僅計算按計數(shù)降序排序的前10個狀態(tài)):

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
'

注意我們?nèi)绾螌?strong>average_balance聚合嵌套到group_by_state聚合中。這是所有聚合的常見模式。您可以將聚合嵌套在聚合中,任意地從數(shù)據(jù)中提取需要的旋轉(zhuǎn)匯總。

在前面的聚合的基礎(chǔ)上,現(xiàn)在讓我們按降序?qū)ζ骄囝~進行排序:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
'

這個例子展示了我們?nèi)绾胃鶕?jù)年齡等級(20-29歲,30-39歲,40-49歲)來分組,然后根據(jù)性別分組,最后得到平均賬戶余額,每個年齡等級,每個性別:

curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
  "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"
              }
            }
          }
        }
      }
    }
  }
}
'

還有很多其他的聚合功能我們在這里不會詳細介紹。如果您想做進一步的實驗,聚合參考指南是一個很好的起點。

結(jié)論

Elasticsearch是一個簡單的和復(fù)雜的乘積。到目前為止,我們已經(jīng)了解了它是什么,如何查看它的內(nèi)部,以及如何使用一些REST api使用它。希望本教程能讓您更好地理解Elasticsearch是什么,更重要的是,它激發(fā)了您對其其他強大功能的進一步試驗!

下一章 —— Elasticsearch學習筆記(4)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容