1. 程式人生 > >ElasticSearch實踐系列(三):探索資料

ElasticSearch實踐系列(三):探索資料

前言

經過前兩篇文章得實踐,我們已經瞭解了ElasticSearch的基礎知識,本篇文章讓我來操作一些更真實的資料集。我們可以利用www.json-generator.com/生成如下的文件結構:

  {
    "account_number": 1,
    "balance": 39225,
    "firstname": "Amber",
    "lastname": "Duke",
    "age": 32,
    "gender": "M",
    "address": "880 Holmes Lane",
    "employer": "Pyrami",
    "email": "
[email protected]
", "city": "Brogan", "state": "IL" }

載入簡單資料集

我們可以下載es提供的資料集accounts.json,然後推送到ES叢集

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

curl "localhost:9200/_cat/indices?v"

我們可以看到1000個文件已經索引到bank索引下了。

[[email protected] cusD]# curl "localhost:9200/_cat/indices?v"
health status index     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   index     3BGZ895tTNa8qtM_nA3YmA   5   1          1            0      4.4kb          4.4kb
green  open   .kibana   qCbYeswVT2WCogz_E9Y3Ag   1   0          2            0     13.7kb         13.7kb
yellow open   customer  x57uWBR3Rg-w2_Dz7Djduw   5   1          1            0      4.5kb          4.5kb
yellow open   customerb 80DoY8e3RtinVNV4VGU4Cg   5   1          1            0      4.5kb          4.5kb
yellow open   best3     DPh-_bOLQBimS9jqWVyyjw   5   1          3            0     10.9kb         10.9kb
yellow open   best1     oD5uUlCbSnqevbRfLvl2Iw   5   1          1            0      5.5kb          5.5kb
yellow open   customer2 VyIXSBK6R9yHNYNDlsni3A   5   1          0            0      1.2kb          1.2kb
yellow open   customerc Nbglz5hbRO28jyt_XyPNTA   5   1          1            0      4.5kb          4.5kb
yellow open   cust      xuYth97RShixNtgNpbyxBA   5   1          1            0      4.4kb          4.4kb
yellow open   customerf osKgtSLxTPKblJW7mrmO0Q   5   1          1            0      5.1kb          5.1kb
yellow open   bank      Wrk49iM6TjGItiZKWdnzJA   5   1       1000            0    474.7kb        474.7kb
yellow open   customer3 101ZzeNmRuCn9d_NOx5oZg   5   1          0            0      1.2kb          1.2kb
yellow open   customere p2BWLci9Qz-1VnOh0vSSQA   5   1          2            0      7.6kb          7.6kb

搜尋API

讓我們開始執行一些簡單的搜尋api,有兩種方式:

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

讓我們分析下這個搜尋請求。我們正在用_search搜尋 bank索引。q=代表Es會匹配索引內的全部文件。sort=account_number:asc代表每個文件的欄位以account_number升序對結果進行排序。pretty*代表結果以漂亮的json格式輸出。這裡摘選部分結果

{
  "took": 53,
  "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",
        "_score": null,
        "_source": {
          "account_number": 0,
          "balance": 16623,
          "firstname": "Bradshaw",
          "lastname": "Mckenzie",
          "age": 29,
          "gender": "F",
          "address": "244 Columbus Place",
          "employer": "Euron",
          "email": "[email protected]",
          "city": "Hobucken",
          "state": "CO"
        },
        "sort": [
          0
        ]
      },
}
  • took - Elasticsearch執行搜尋的時間(以毫秒為單位)
  • timed_out - 告訴我們搜尋是否超時
  • _shards - 告訴我們搜尋了多少個分片,以及搜尋成功/失敗分片的計數
  • hits - 搜尋結果
  • hits.total - 符合我們搜尋條件的文件總數
  • hits.hits - 實際的搜尋結果陣列(預設為前10個文件)
  • hits.sort - 對結果進行排序(如果按分數排序則丟失)
  • hits._score並max_score- 暫時忽略這些欄位也可以用Request Body方式執行搜尋,格式如下:
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

介紹查詢語言【Query Language】

Elasticsearch提供了一種JSON樣式的特定於域的語言,可用於執行查詢。這被稱為查詢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規定文件開始的索引,size指定了查詢文件的大小。在實現分頁時,這兩個引數非常有用。from如果不傳,預設為0。

下面的示例執行一個 match_all並按帳戶餘額降序對結果進行排序,返回前10個(預設大小)文件。

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

執行搜尋

上面我們已經看到了一些基本的查詢示例,讓我們再深入瞭解下QueryDSL。讓我們來看下返回的json文件的欄位。預設情況下會返回命中文件的所有欄位。這被稱為源(_source代表命中的欄位)。有些情況下,我們只需要部分欄位,如下:

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

下面我們來說說查詢部分。之前我們講過match_all是匹配所有文件,現在讓我們瞭解一個match query,它能針對特定欄位或欄位集進行搜尋。下面這個示例能搜尋account_number為20的資料:

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

此示例返回地址中包含術語“mill”或“lane”的所有帳戶,這裡格外注意【空格隔開的兩個單詞是or查詢】:

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

此示例演示地址種包含“mill lane”的所有賬戶,【用match_phrase查詢時,空格隔開的依然是一個單詞】

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

然後我們繼續介紹下 bool query,它允許我們使用布林查詢將更小的查詢組合成更大的查詢。must 同時滿足條件此示例組成兩個match查詢並返回地址中包含“mill”和“lane”的所有帳戶:

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

在上面的示例中,該bool must子句指定必須為true才能將文件視為匹配的所有查詢。

should或滿足一個即可 此示例組成兩個match查詢並返回地址中包含“mill”或“lane”的所有帳戶:

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

在上面的示例中,該bool should子句指定了一個查詢列表,其中任何一個查詢都必須為true,才能將文件視為匹配項。

must_not都不包含 此示例組成兩個match查詢並返回地址中既不包含“mill”也不包含“lane”的所有帳戶:

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

我們可以在查詢中同時組合must,should和must_not子句bool。此外,我們可以bool在任何這些bool子句中組合查詢來模仿任何複雜的多級布林邏輯。此示例返回任何40歲但不住在ID(aho)的人的所有帳戶

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

執行過濾器

上面的示例中,我們跳過了一個稱為文件分數的小細節(_score搜尋結果中的欄位)。分數是一個數值,它是文件與我們指定的搜尋查詢匹配程度的相對度量。分數越高,文件越相關,分數越低,文件的相關性越低。但是查詢並不總是需要產生分數,特別是當它們僅用於“過濾”文件集時。Elasticsearch會檢測這些情況並自動優化查詢執行,以便不計算無用的分數。

我們在上面示例介紹的bool查詢還支援filter允許使用查詢來限制將與其他子句匹配的文件的子句,而不會更改計算得分的方式。作為示例,讓我們介紹一下range查詢,它允許我們按一系列值過濾文件。一般數字或日期會用到range。此示例使用bool查詢返回餘額大於或等於20000且小於或等於30000的帳戶。

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

解析上面的內容,bool查詢包含match_all查詢(查詢部分)和range查詢(過濾部分)。我們可以將任何其他查詢替換為查詢和過濾器部分。除了match_all,match,bool,和range查詢,有很多可用的其他查詢型別的,這裡暫時不講了,我們瞭解了大致的工作原理後,將這些知識應用於學習和試驗其他查詢型別應該不會太困難。

執行聚合

聚合提供了從資料中分組和提取統計資訊的功能。考慮聚合的最簡單方法是將其大致等同於SQL GROUP BY和SQL聚合函式。在Elasticsearch中,您可以執行返回匹配的搜尋,同時在一個響應中返回與命中相關的聚合結果。這是非常強大和高效的,因為您可以執行查詢和多個聚合,並一次性獲取兩個(或任一)操作的結果,避免使用簡潔和簡化的API進行網路往返。首先,此示例按state對所有帳戶進行分組,然後返回按計數降序排序的前10個(預設)states(也是預設值),(這裡的group_by_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": 0,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1000,
    "max_score": 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
        }
      ]
    }
  }
}

我們可以看到key為ID的有27個賬戶,TX也是27個賬戶,AL的是25個賬戶,以此類推。請注意,我們設定size=0為不顯示搜尋匹配,因為我們只希望在響應中看到聚合結果。在前一個聚合的基礎上,此示例按州計算平均帳戶餘額(同樣僅針對按降序排序的前10個州):

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

請注意我們如何巢狀average_balance聚合內的group_by_state聚合。這是所有聚合的常見模式。您可以在聚合中任意巢狀聚合,以從資料中提取所需的輪轉摘要。

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

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

還有許多其他聚合功能,我們在此不再詳述。如果您想進行進一步的實驗,聚合參考指南是一個很好的起點。

總結

本篇文章依據官方文件,實踐了查詢和聚合命令,前面查詢的部分還是很簡單的,聚合這塊有些複雜。本篇到此結束,感謝觀看。有興趣的可以通過 http://www.weixinhe.cn:5601 演示上述命令。