elasticsearch(二)---基本資料操作

P城到底誰說的算發表於2018-08-19

修改資料

Elasticsearch 提供了近實時的資料操作和搜尋功能。預設情況下, 您可以期望從索引/更新/刪除資料的時間到一個秒延遲 (重新整理間隔), 直到顯示在搜尋結果中的時間。這是與 SQL 類似的其他平臺的重要區別, 其中資料在事務完成後立即可用。

索引、替換文件

索引單個文件。讓我們再次記住該命令:

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。

更新文件

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

本示例演示如何通過將名稱欄位更改為 "無名氏" 來更新以前的文件 (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-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"
              }
            }
          }
        }
      }
    }
  }
}
複製程式碼

結論

Elasticsearch 是一個簡單而複雜的產品。迄今為止, 我們已經瞭解了它的基本知識、如何檢視它的內部以及如何使用一些 REST api 來處理它。

相關文章