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": "amberduke@pyrami.com",
    "city": "Brogan",
    "state": "IL"
}

加載簡單數據集

我們可以下載es提供的數據集accounts.json,然后推送到ES集群

wget https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json

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索引下了。

[root@XXXXX 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": "bradshawmckenzie@euron.com",
          "city": "Hobucken",
          "state": "CO"
        },
        "sort": [
          0
        ]
      },
}
  • took - Elasticsearch執(zhí)行搜索的時間(以毫秒為單位)
  • timed_out - 告訴我們搜索是否超時
  • _shards - 告訴我們搜索了多少個分片,以及搜索成功/失敗分片的計數
  • hits - 搜索結果
  • hits.total - 符合我們搜索條件的文檔總數
  • hits.hits - 實際的搜索結果數組(默認為前10個文檔)
  • hits.sort - 對結果進行排序(如果按分數排序則丟失)
  • hits._score并max_score- 暫時忽略這些字段
    也可以用Request Body方式執(zhí)行搜索,格式如下:
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

介紹查詢語言【Query Language】

Elasticsearch提供了一種JSON樣式的特定于域的語言,可用于執(zhí)行查詢。這被稱為查詢DSL。查詢語言非常全面,乍一看可能令人生畏,但實際學習它的最佳方法是從一些基本示例開始。
回到上面的例子,我們執(zhí)行查詢:

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

解析上面的內容,該query部分告訴我們查詢定義是什么,match_all部分只是我們想要運行的查詢類型。該match_all查詢僅僅是在指定索引的所有文件進行搜索。
除了query參數,我們還可以傳遞其他參數來影響搜索結果。在上面我們傳入的部分的示例中 sort,我們傳入size:

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

請注意,如果size未指定,則默認為10。
此示例執(zhí)行一個 match_all并返回文檔10到19:

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

from規(guī)定文檔開始的索引,size指定了查詢文檔的大小。在實現分頁時,這兩個參數非常有用。from如果不傳,默認為0。

下面的示例執(zhí)行一個 match_all并按帳戶余額降序對結果進行排序,返回前10個(默認大?。┪臋n。

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

執(zhí)行搜索

上面我們已經看到了一些基本的查詢示例,讓我們再深入了解下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”或“l(fā)ane”的所有帳戶,這里格外注意【空格隔開的兩個單詞是or查詢】:

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

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

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

然后我們繼續(xù)介紹下 bool query,它允許我們使用布爾查詢將更小的查詢組合成更大的查詢。
must 同時滿足條件此示例組成兩個match查詢并返回地址中包含“mill”和“l(fā)ane”的所有帳戶:

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

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

should或滿足一個即可 此示例組成兩個match查詢并返回地址中包含“mill”或“l(fā)ane”的所有帳戶:

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

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

must_not都不包含 此示例組成兩個match查詢并返回地址中既不包含“mill”也不包含“l(fā)ane”的所有帳戶:

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

執(zhí)行過濾器

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

我們在上面示例介紹的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查詢,有很多可用的其他查詢類型的,這里暫時不講了,我們了解了大致的工作原理后,將這些知識應用于學習和試驗其他查詢類型應該不會太困難。

執(zhí)行聚合

聚合提供了從數據中分組和提取統(tǒng)計信息的功能??紤]聚合的最簡單方法是將其大致等同于SQL GROUP BY和SQL聚合函數。在Elasticsearch中,您可以執(zhí)行返回匹配的搜索,同時在一個響應中返回與命中相關的聚合結果。這是非常強大和高效的,因為您可以運行查詢和多個聚合,并一次性獲取兩個(或任一)操作的結果,避免使用簡潔和簡化的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 演示上述命令。

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容