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/account/_bulk?pretty&refresh' --data-binary "@accounts.json"
curl 'localhost:9200/_cat/indices?v'

返回內容以下:sql

health status index   uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .kibana XYZPR5XGQGWj8YlyZ1et_w   1   1          1            0      3.1kb          3.1kb
yellow open   bank    uoTQIb3GSDOH08CmsIy66A   5   1        999            0    639.5kb        639.5kb

這意味着咱們已經成功批量索引999個文檔到bank索引下(類型爲account )。json

注意,上面的操做不能在kibana中執行,須要使用curlapi

具體操做是,把下載的json文檔放在和curl.exe相同的目錄,而後打開命令提示符定位到curl.exe所在目錄,而後粘貼如下命令(個人curl版本是7.53.1,須要改爲下面的方式才能執行成功),回車便可數組

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

查詢API

運行查詢有兩種方式,一是經過 REST request URI 方式發送查詢參數,二是經過 REST request body 。方式二更爲靈活,可使用可讀性好的JSON 格式定義你的查詢條件,下面咱們針對方式一舉個例子,之後的教程都使用方式二。服務器

REST API查詢條件放在_search以後,如下例子返回 bank 索引中的全部文檔:網絡

GET /bank/_search?q=*&sort=account_number:asc&pretty
bank 表示查詢bank索引中的文檔, _search 後面跟的是查詢條件,q=* 參數指示 Elasticsearch 匹配索引中的全部文檔。 sort=account_number:asc 參數指示使用 account_number 對結果進行升序排序。 pretty 參數告訴 Elasticsearch 返回漂亮的JSON結果。

返回部份內容以下:app

{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : null,
    "hits" : [ {
      "_index" : "bank",
      "_type" : "account",
      "_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" : "account",
      "_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"}
    }, ...
    ]
  }
}

至於返回內容,咱們看到如下部分:

  • took -  Elasticsearch  執行查詢的時間(以毫秒爲單位)
  • timed_out - 告訴咱們查詢是否超時
  • _shards - 告訴咱們查詢了多少個分片,以及查詢成功/失敗的分片數量
  • hits - 查詢結果
  • hits.total - 符合咱們查詢條件的文檔總數
  • hits.hits - 實際查詢結果數組(默認爲前10個文檔)
  • hits.sort - 對結果進行排序的鍵(若是沒提供,則默認使用_score進行排序)
  • hits._scoremax_score-如今先忽略這些字段

使用方式二執行上面查詢以下

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

不一樣點是咱們用json格式的請求體代替了_search api uri中的q=*參數。咱們將會在後面的內容討論json格式的查詢。

注意,當咱們接收到返回結果的時候, elasticsearch 已經徹底處理了這個請求,不會維護任何的服務器端的資源或者在結果中打開遊標。這與許多其餘的平臺造成鮮明的對比(好比sql的遊標)

查詢語言介紹

 Elasticsearch  t提供了一種 JSON-style 的特定領域語言用來執行查詢,稱爲 Query DSL,該查詢語言十分全面,初看可能以爲有點嚇人。事實上,學習它的最好方式就是從幾個基本的例子開始。回到上一個例子,咱們執行了這個查詢:

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

上面的 query   部分告訴咱們查詢定義是什麼,  match_all 部分僅僅是咱們想運行的查詢的類型,也就是查詢指定索引下的全部文檔。

除了查詢參數之外,也能夠經過其餘參數影響查詢結果。好比前面的 sort 指定排序字段,下面經過 size 指定返回結果數

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

注意 size 若是不指定,默認是10。

下面的例子匹配全部,而且返回第11到20之間的文檔

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

 from參數(最小值是0,不是1)指定返回文檔的起始文檔的索引, size 參數指定一共返回多少個文檔。這個特性對實現分頁很是有用。若是 from 沒有指定,默認值是0。

下面的例子匹配全部,而且經過 balance 字段對結果進行降序排序,返回前10條(默認 size )文檔。

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

執行查詢

接下來咱們進一步探討Query DSL。首先看一下返回的文檔字段。默認狀況下,完整的JSON文檔做爲全部搜索的一部分返回。

默認狀況下,完整的JSON文檔做爲全部搜索的一部分返回。文檔原始內容被稱爲源(查詢結果中的_source字段)。若是不但願返回整個源文檔,也能夠請求僅幾個字段被返回。

如下示例顯示如何返回兩個字段(_source內), account_number  balance

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

返回內容以下:

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 999,
    "max_score": 1,
    "hits": [
      {
        "_index": "bank",
        "_type": "account",
        "_id": "25",
        "_score": 1,
        "_source": {
          "account_number": 25,
          "balance": 40540
        }
      }
    ]
  }
}

以上的例子僅僅減小了 _source 裏的字段,返回的字段 account_number 和 balance 仍然包含在 _source

若是你以前有SQL背景,上述在概念上與SQL SELECT FROM字段列表有些類似。

如今來看看查詢部分。經過前面的示例,咱們已經學會了如何使用 match_all 查詢來匹配全部文檔。如今介紹一個名爲match查詢的新查詢,能夠將其視爲基本的字段搜索查詢(即針對特定字段或一組字段進行搜索)。

如下示例返回的 account_number 爲20:

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

返回結果:

{
  "took": 15,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "bank",
        "_type": "account",
        "_id": "20",
        "_score": 1,
        "_source": {
          "account_number": 20,
          "balance": 16418,
          "firstname": "Elinor",
          "lastname": "Ratliff",
          "age": 36,
          "gender": "M",
          "address": "282 Kings Place",
          "employer": "Scentric",
          "email": "elinorratliff@scentric.com",
          "city": "Ribera",
          "state": "WA"
        }
      }
    ]
  }
}

如下實例返回 address 中包含"mill"的全部帳戶:

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

如下示例返回address中包含"mill"或者"lane"的全部帳戶:

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

如下示例是matchmatch_phrase)的一個變體,返回在地址中包含短語"mill lane"的全部賬戶:

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

下面介紹bool(ean) query 布爾查詢容許咱們把多個 match 查詢合併到一個查詢中。

如下示例由兩個 match  查詢組成,返回 address 中既包含"mill" 又包含"lane" 的全部帳戶:

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

在上面的示例中, bool must 裏面的全部查詢條件必須都爲真時纔會被匹配。

相比之下,下面的示例由兩個match查詢組成,並返回在地址中包含"mill"或"lane"的全部賬戶:

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

在上面的例子中, bool should 子句指定了一個查詢列表,只要其中一個查詢爲真,文檔就會被匹配。

如下示例由兩個match查詢組成,並返回在地址中既不包含"mill"也不包含"lane"的全部賬戶:

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

在上面的例子中,bool must_not 子句指定一個查詢列表,只有查詢列表中的條件都爲假的時候纔會被匹配。

也能夠把 must,should,must_not 同時組合到bool子句。此外,咱們也能夠組合bool 到任何一個bool子句中,實現複雜的多層bool子句嵌套邏輯。

下面的例子返回全部年齡是40歲但不居住在ID(Idaho)的帳戶:

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

執行過濾

前面咱們跳過了一點細節,文檔得分(也就是在搜索結果中的 _score 字段)。分數是一個數值,它是文檔與咱們指定的搜索查詢匹配的相對度量。分數越高,文檔越相關,分數越低,文檔的相關性越低。但查詢並不老是須要產生分數,特別是當它們僅用於"過濾"文檔集時。 Elasticsearch 會檢測這些狀況,並自動優化查詢執行,以避免計算無用的分數。

bool查詢支持filter子句,它容許你使用一個查詢語句去限制其它子句的匹配結果,同時不會計算文檔的得分。例如,咱們來介紹一下 range query, 它容許咱們經過一個範圍值去過濾文檔。一般用於數字或日期過濾。

如下示例使用布爾查詢返回餘額在20000到30000之間(包括端值)的全部賬戶。換句話說,咱們想找到餘額大於或等於20000且小於等於30000的帳戶。

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

仔細分析上面的例子,bool查詢包含了一個match_all查詢(查詢部分)和一個range查詢(過濾部分)。咱們也能夠用任何其它的查詢語句代替查詢和過濾部分的語句。對於上面的例子,由於全部文檔都是指定範圍以內的,他們從某種意義上來講是等價的(equally),即他們的相關度都是同樣的(filter子句查詢,不會改變得分)。

除了 match_all,match,bool,range查詢,還有不少種類的查詢,但咱們不在這裏一一介紹。從如今開始,咱們對查詢已經有一個基礎的瞭解,把學到的知識應用到其餘查詢類型應該也沒什麼難度。

執行聚合

聚合提供從數據中分組和提取統計信息的功能。理解聚合的最簡單的方法是將其大體等同於SQL GROUP BY和SQL聚合函數。在 Elasticsearch 中,能夠返回匹配搜索的同時返回聚合結果,在一個響應中將全部匹配的結果和聚合結果同時返回。這是很是強大和高效的,能夠下降網絡請求的次數。

如下示例經過state字段進行分組,並按照count 降序排序,返回前10(默認值)條數據:

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

返回內容(僅部分)以下:

{
  "took": 50,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 999,
    "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": "MO",
          "doc_count": 20
        },
        {
          "key": "AK",
          "doc_count": 19
        }
      ]
    }
  }
}

能夠看到,有27個帳戶居住在ID(Idaho),27個帳戶居住在TX(Texas),25個帳戶居住在AL(Alabama)等等。

注意,設置size=0 是爲了避免顯示搜索結果,由於咱們僅僅想看返回的聚合結果。

基於上述例子,下面的例子除了分組還會計算每一個州的帳戶的平均餘額:

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

返回內容(僅部分)以下:

{
  "took": 32,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 999,
    "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,
          "average_balance": {
            "value": 24368.777777777777
          }
        },
        {
          "key": "TX",
          "doc_count": 27,
          "average_balance": {
            "value": 27462.925925925927
          }
        },
        {
          "key": "AL",
          "doc_count": 25,
          "average_balance": {
            "value": 25739.56
          }
        },
        {
          "key": "MD",
          "doc_count": 25,
          "average_balance": {
            "value": 24963.52
          }
        },
        {
          "key": "TN",
          "doc_count": 23,
          "average_balance": {
            "value": 29796.782608695652
          }
        },
        {
          "key": "MA",
          "doc_count": 21,
          "average_balance": {
            "value": 29726.47619047619
          }
        },
        {
          "key": "NC",
          "doc_count": 21,
          "average_balance": {
            "value": 26785.428571428572
          }
        },
        {
          "key": "ND",
          "doc_count": 21,
          "average_balance": {
            "value": 26303.333333333332
          }
        },
        {
          "key": "MO",
          "doc_count": 20,
          "average_balance": {
            "value": 24151.8
          }
        },
        {
          "key": "AK",
          "doc_count": 19,
          "average_balance": {
            "value": 24088.63157894737
          }
        }
      ]
    }
  }
}

注意咱們是如何把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"
              }
            }
          }
        }
      }
    }
  }
}

返回結果以下:

{
  "took": 21,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 999,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "group_by_age": {
      "buckets": [
        {
          "key": "20.0-30.0",
          "from": 20,
          "to": 30,
          "doc_count": 450,
          "group_by_gender": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
              {
                "key": "M",
                "doc_count": 231,
                "average_balance": {
                  "value": 27400.982683982686
                }
              },
              {
                "key": "F",
                "doc_count": 219,
                "average_balance": {
                  "value": 25341.260273972603
                }
              }
            ]
          }
        },
        {
          "key": "30.0-40.0",
          "from": 30,
          "to": 40,
          "doc_count": 504,
          "group_by_gender": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
              {
                "key": "F",
                "doc_count": 253,
                "average_balance": {
                  "value": 25670.869565217392
                }
              },
              {
                "key": "M",
                "doc_count": 251,
                "average_balance": {
                  "value": 24288.239043824702
                }
              }
            ]
          }
        },
        {
          "key": "40.0-50.0",
          "from": 40,
          "to": 50,
          "doc_count": 45,
          "group_by_gender": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
              {
                "key": "M",
                "doc_count": 24,
                "average_balance": {
                  "value": 26474.958333333332
                }
              },
              {
                "key": "F",
                "doc_count": 21,
                "average_balance": {
                  "value": 27992.571428571428
                }
              }
            ]
          }
        }
      ]
    }
  }
}

還有許多其餘聚合功能,將再也不詳細介紹。若是您想進一步試驗聚合參考指南是一個很好的起點。

結論

 elasticsearch 是一個既簡單又複雜的產品。目前爲止,咱們已經學瞭如何使用REST API以及 elasticsearch 的基本概念和特性。但願這個教程可讓你很好的理解 elasticsearch ,更重要的是,激勵你繼續學習後續教程要介紹的強大特性。

官方文檔

https://www.elastic.co/guide/en/elasticsearch/reference/current/_exploring_your_data.html

參考文檔

https://github.com/13428282016/elasticsearch-CN/wiki/es-gettting-started

相關文章
相關標籤/搜索