elasticsearch(二)---基本數據操做

修改數據

Elasticsearch 提供了近實時的數據操做和搜索功能。默認狀況下, 您能夠指望從索引/更新/刪除數據的時間到一個秒延遲 (刷新間隔), 直到顯示在搜索結果中的時間。這是與 SQL 相似的其餘平臺的重要區別, 其中數據在事務完成後當即可用。sql

索引、替換文檔

索引單個文檔。讓咱們再次記住該命令:api

PUT /customer/_doc/1?pretty
{
  "name": "John Doe"
}
複製代碼

一樣, 上述將將指定的文檔索引爲客戶索引, ID 爲1。若是咱們再用不一樣 (或相同) 的文檔再次執行上述命令, Elasticsearch 將替換 (即 reindex) 一個新文檔, 上面有一個 ID 爲1的文件:數組

PUT /customer/_doc/1?pretty
{
  "name": "Jane Doe"
}
複製代碼

上述更改的文件名稱, 其 ID 爲1從 "無名氏" 到 "無名氏"。另外一方面, 若是咱們使用不一樣的 ID, 則會對新文檔進行索引, 而且索引中已經存在的現有文檔將保持不變。服務器

PUT /customer/_doc/2?pretty
{
  "name": "Jane Doe"
}
複製代碼

上面的索引是一個 ID 爲2的新文檔。網絡

索引時, ID 部分是可選的。若是未指定, Elasticsearch 將生成隨機 ID, 而後使用它對文檔進行索引。架構

實際 ID Elasticsearch 生成 (或在前面的示例中顯式指定的任何內容) 做爲索引 API 調用的一部分返回。函數

此示例演示如何索引沒有顯式 ID 的文檔:優化

POST /customer/_doc?pretty
{
  "name": "Jane Doe"
}
複製代碼

請注意, 在上述狀況下, 咱們使用的是POST而不是PUT, 由於咱們沒有指定 ID。spa

更新文檔

除了可以索引和替換文檔以外, 咱們還能夠更新文檔。注意,每次進行更新時, Elasticsearch 都會刪除舊文檔, 而後用一次快照將更新應用於該文檔, 對其進行索引rest

本示例演示如何經過將名稱字段更改成 "無名氏" 來更新之前的文檔 (ID 爲 1):

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "Jane Doe" }
}
複製代碼

本示例演示如何經過將 "名稱" 字段更改成 "無名氏", 並同時向其添加 "年齡" 字段來更新咱們之前的文檔 (ID 1):

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "Jane Doe", "age": 20 }
}
複製代碼

刪除文檔

刪除文檔至關簡單。此示例演示如何刪除 ID 爲2的客戶:

DELETE /customer/_doc/2?pretty
複製代碼

批量處理

除了可以索引、更新和刪除單個文檔以外, Elasticsearch 還提供了使用bulk API在批處理中執行上述任何操做的能力。此功能很重要, 由於它提供了一個很是有效的機制, 儘量快地完成多個操做, 儘量少使用網絡往返。

例如, 如下調用在一個批量操做中索引兩個文檔 (id 1-無名氏和 id 2-無名氏):

POST /customer/_doc/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
複製代碼

本示例更新第一個文檔 (id 爲 1), 而後在一個批量操做中刪除第二個文檔 (id 爲 2):

POST /customer/_doc/_bulk?pretty
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
複製代碼

注意, 對於刪除操做, 在它以後沒有相應的源文檔, 由於刪除只要求刪除文檔的 ID。

因爲其中一個操做失敗, 批量 API 不會失敗。若是單個操做因任何緣由而失敗, 它將繼續處理其後面的其他操做。當批量 API 返回時, 它將爲每一個操做提供一個狀態 (與發送的順序相同), 以便您能夠檢查特定操做是否失敗。

瀏覽數據

如今, 咱們已經看到了基本知識, 讓咱們嘗試一個更現實的數據集。我已經準備了一個關於客戶銀行賬戶信息的虛擬 JSON 文檔示例。每一個文檔都具備如下架構:

{
    "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"
}
複製代碼

搜索API

如今讓咱們從一些簡單的搜索開始。運行搜索有兩種基本方法: 一種是經過rest 請求 URI發送搜索參數, 另外一種是經過rest 請求正文發送。請求正文方法容許您更具表現力, 也能夠用更可讀的 JSON 格式定義搜索。

咱們將嘗試一個請求 URI 方法的示例, 但對於本教程的其他部分, 咱們將專門使用請求正文方法。

用於搜索的 REST API 可從端點訪問。本示例返回銀行索引中的全部文檔:_search

GET /bank/_search?q=*&sort=account_number:asc&pretty
複製代碼

讓咱們先解剖一下上面的查詢語句。咱們在bank索引中搜索 (端點), q=*參數指示 Elasticsearch 匹配索引中的全部文檔。sort=account_number:asc該參數指示使用每一個文檔的字段升序對結果進行排序。pretty該參數, 只是告訴 Elasticsearch 返回漂亮的打印 JSON 結果。

響應 (部分顯示):

{
  "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"}
    }, ...
    ]
  }
}
複製代碼

對於響應, 咱們看到如下部分:

took-Elasticsearch 執行搜索的時間 (以毫秒爲單位)

timed_out–告訴咱們搜索超時與否

_shards–告訴咱們搜索了多少碎片, 以及成功/失敗的搜索碎片的計數

hits–搜索結果

hits.total–與搜索條件匹配的文檔總數

hits.hits–實際的搜索結果數組 (默認爲前10個文檔)

hits.sort-爲結果排序鍵 (若是按分數排序則丟失)

下面是使用備選請求正文方法的相同的精確搜索:

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

這裏的區別在於, 咱們不是在 URI 中傳遞, 而是向 API 提供 JSON 樣式的查詢請求正文。

注意, 當你獲得搜索結果, Elasticsearch 已經請求結束。 與許多其餘平臺 (如 SQL) 不一樣, SQL最初可能會得到查詢結果的部分子集, 而後要使用某種方法(如翻頁) 繼續返回到服務器再次查詢獲取其他的結果。
複製代碼

引入查詢語言

Elasticsearch 提供了一個 JSON 樣式的域特定語言, 您可使用它來執行查詢。這稱爲查詢 DSL。查詢語言很是全面, 乍一看可能很嚇人, 但真正瞭解它的最好方法是從幾個基本示例開始。

回到最後一個示例, 咱們執行了如下查詢:

GET /bank/_search
{
  "query": { "match_all": {} }
}
複製代碼

咱們還能夠經過其餘參數來影響搜索結果。在上面的部分中, 咱們經過size設置查詢結果數量:

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
}
複製代碼

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

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

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}
複製代碼

本示例按賬戶餘額降序排列結果:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}
複製代碼

執行搜索

既然咱們已經看到了一些基本的搜索參數, 讓咱們在查詢 DSL 中多挖掘一些。

讓咱們先來看看返回的文檔字段。默認狀況下, 完整的 JSON 文檔做爲全部搜索的一部分返回。這稱爲_source源 (搜索命中中的字段)。若是咱們不但願返回整個源文檔, 咱們就有能力請求返回源中的幾個字段。

此示例演示如何從搜索中返回兩個字段:

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

相似 select account_number,balance from table;

之前, 咱們已經看到了如何使用查詢來匹配全部文檔。如今讓咱們介紹一個名爲 " 匹配查詢" 的新查詢, 它能夠被看做是一個基本的搜索查詢 (即針對特定字段或字段集進行的搜索)。

本示例返回編號爲20的賬戶:

GET /bank/_search
{
  "query": { "match": { "account_number": 20 } }
}
複製代碼

本示例返回address中包含 mill 一詞的全部賬戶:

GET /bank/_search
{
  "query": { "match": { "address": "mill" } }
}
複製代碼

本示例返回address中包含 milllane 術語的全部賬戶:

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}
複製代碼

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

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}
複製代碼

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

本示例構成兩個查詢, 並返回address中包含 milllane 的全部賬戶:

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

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

本示例構成兩個查詢, 並返回address中包含 milllane 的全部賬戶:

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

在上面的示例中, 子句bool should指定一個查詢列表, 其中有一個都是 true, 才能將文檔視爲匹配項。

本示例構成兩個查詢, 並返回address中不包含 milllane 的全部賬戶:

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

本示例返回40歲的人的全部賬戶, 但不居住在 ID (阿霍) 中:

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

執行篩選

在上一節中, 咱們跳過一個名爲_score "文檔分數" (搜索結果中的字段) 的小細節。

分數是一個數值, 它是文檔與咱們指定的搜索查詢匹配程度的相對度量值。

分數越高, 文檔越相關, 分數越低, 文檔越不相關。

可是查詢並不老是須要產生分數, 特別是當它們僅用於 "篩選" 文檔集時。

Elasticsearch 檢測這些狀況, 並自動優化查詢執行, 以不計算無用的分數。

例如, 讓咱們介紹範圍查詢, 它容許咱們按一系列值篩選文檔。這一般用於數字或日期篩選。

本示例使用 bool 查詢返回包含20000和30000之間的餘額的全部賬戶。換言之, 咱們但願找到一個餘額大於或等於20000且小於或等於30000的賬戶。

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

解剖上面, 布爾查詢包含查詢 (查詢部分) 和查詢 (篩選器部分)。在上述狀況下, 範圍查詢是徹底無心義的, 由於只要在這個範圍內的文件,他們的分數應該相同,沒有誰比誰更相關。因此,咱們能夠不用關心他們的文檔分數,從而使用filter篩選文檔。

執行聚合

聚合提供了從數據中分組和提取統計信息的能力。考慮聚合的最簡單方法是大體將它等同於 sql group和 sql 聚合函數。在 Elasticsearch 中, 您有能力執行返回命中的搜索, 同時返回聚合結果, 並在一個響應中與命中所有分開。這是很是強大和高效的, 在這個意義上, 您能夠運行查詢和多個聚合, 並得到兩個 (或兩個以上) 操做的結果, 以免網絡往返。

首先, 本示例按狀態對全部賬戶進行分組, 而後返回按降序 (也爲默認值) 排序的前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 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愛達荷州) 有27個賬戶, 其次是27賬戶 (TX得克薩斯州), 其次是25賬戶 (AL阿拉巴馬州), 等等。

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

本示例計算平均賬戶餘額 (僅針對按降序排序的前10個狀態):

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "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-2九、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 是一個簡單而複雜的產品。迄今爲止, 咱們已經瞭解了它的基本知識、如何查看它的內部以及如何使用一些 REST api 來處理它。

相關文章
相關標籤/搜索