elasticsearch文檔操作

上篇文章向讀者介紹了Elasticsearch中修改數據的操作,使用了Elasticsearch提供的一整套強大的REST API,本文繼續來看通過這一套API如何完成文檔的基本操作。

本文是Elasticsearch系列的第四篇,閱讀前面的文章,有助於更好的理解本文:


1.elasticsearch安裝與配置 2.初識elasticsearch中的REST接口 3.elasticsearch修改數據


加載樣本數據

爲了完成後面的操作,我們首先需要一些樣本數據,這個樣本數據可以在http://www.json-generator.com/網站上自動生成,也可以直接下載已經生成好的,下載地址https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json?raw=true。

將下載下來的JSON數據放到當前用戶目錄下,然後執行如下命令,將數據導入到Elasticsearch中,如下:

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

小貼士:

這裏實際上就是使用了上文提到的批量操作,只不過數據變爲了一個本地的JSON文件而已。

注意,上面這行命令在執行過程中,可能會報如下錯誤:

The bulk request must be terminated by a newline [\n]

這是因爲下載的accounts.json文件少了一個換行符,在下載的accounts.json文件最末尾,按下一個回車即可。

上面的命令執行完後,可以通過如下命令查看數據是否導入成功:

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

查看結果如下:

可以看到,成功的添加了1000個文檔到bank索引中。

搜索API

整體來說,搜索條件既可以放在URL中,也可以放在REST請求體中,一般來說建議採用第二種方案,但是爲了知識的完整性,這裏對兩種方案都予以介紹。

搜索條件在地址欄中

請求如下:

curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:desc&pretty"

請求解釋:

  1. q=* 表示搜索所有文檔。
  2. sort=accountnumber:desc表示請求結果按照accountnumber字段的值倒敘排序。
  3. pretty則表示將響應的JSON格式化,方便閱讀。

執行結果如下(部分):

響應字段解釋:

  1. took表示搜索耗時,單位爲毫秒。
  2. timed_out表示搜索是否超時。
  3. _shards表示有多少個分片被搜索了,成功搜索的分片數量、跳過的分片數量以及失敗的分片數量。
  4. hits表示搜索結果。
  5. hits.total表示搜索到的文檔總數量。
  6. hits.hits表示搜索到的文檔數組,默認顯示搜索到的前十個文檔。

搜索條件在REST請求體中

上面介紹的這種搜索條件在URL中,搜索條件也可以放在REST請求體中,下面這個請求等同於上面的請求:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "desc" }
  ]
}
'

這裏通過一個JSON請求體來描述查詢條件,接下來向讀者詳細介紹這個JSON格式的查詢條件。

查詢語言介紹

Elasticsearch提供了一套JSON風格的查詢條件格式,這稱爲DSL。DSL非常全面,雖然看起來可能有點難以理解,不過我們可以先從幾個簡單的案例來開始這個東西的學習。

回顧我們上文的查詢條件:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "desc" }
  ]
}
'

在上面這個請求中, query表示查詢的定義, match_all表示查詢指定索引下的所有文檔。 sort則是一個排序字段,表示根據 account_number字段的值降序排列。

除了這些參數外,我們還可以指定返回的文檔個數:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "desc" }
  ],
  "size": 1
}
'

這個表示返回的文檔個數爲1,執行結果如下:

此時就只返回了一個文檔,如果不指定的話,size默認爲10。

也可以指定從第幾個文檔開始查詢,類似於SQL中的分頁,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "desc" }
  ],
  "size": 1,
  "from": 10
}
'

這個表示從第10個開始查詢,查詢1一個出來,如下:

如果不指定from,則默認值爲0。

執行搜索

通過上面一小節,讀者對基本的查詢已經有所瞭解,接下來再來看看查詢中其他的一些細節。

自定義返回字段

默認情況下,查詢結果中會返回查詢文檔的所有字段,如果不需要返回所有字段,則可以自定義返回字段,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "desc" }
  ],
  "size": 2,
  "from": 10,
  "_source": ["account_number","balance"]
}
'

此時執行結果如下:

可以看到,查詢結果中的 _source字段中的值已經發生了變化。

match

前面我們已經用過 match_all表示查詢指定索引下的所有文檔,我們也可以使用 match來指定查詢條件,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "account_number": 20 } }
}
'

這個表示查詢 account_number的值爲20的文檔。

his example returns all accounts containing the term "mill" in the address:

下面這個表示查詢address中包含詞語(term)“mill”的所有賬戶:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "address": "mill" } }
}
'

如下則表示查詢address中包含詞語(term)“mill”或者“lane”的所有賬戶:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match": { "address": "mill lane" } }
}
'

match_phrase

match_phrase表示短語查詢,如下查詢表示查詢address中包含“mill lane”的所有文檔,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": { "match_phrase": { "address": "mill lane" } }
}
'

bool查詢

bool查詢允許用戶將幾個較小的查詢條件,通過bool中的邏輯運算,組合成一個較大的查詢條件,如下查詢表示查詢address中包含“mill”和“lane”的所有文檔:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'

上面案例中的must相當於and,而下面的should則相當於or(即查詢的文檔,只要包含的兩個條件中一個符合,則該文檔會被認爲是符合條件的),如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'

如下查詢則表示查詢address中既不包含“mill”,又不包含“lane”的所有文檔,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
'

也可以在一個查詢中一起使用must、must_not、should,如下查詢表示查詢age爲40,並且state不爲ID的所有賬戶:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}
'

執行過濾器

在前面的小節中,我們跳過了搜索結果中的_score字段,這個字段用來描述搜索結果的匹配度,得分越高,文檔匹配度越高,得分越低,文檔的匹配度越低。在Elasticsearch中,所有的查詢操作都會觸發匹配度得分的計算,但是並非所有的查詢都需要獲取匹配度這個參數,對於那些我們不需要匹配度得分的搜索中(例如僅僅只是想過濾文檔集),可以使用Elasticsearch提供的另一種查詢功能---過濾器。過濾器在概念上類似於查詢,但是執行速度高於查詢,之所以查詢速度高,有如下兩個原因:

  • 過濾器不會計算相關度的得分
  • 過濾器可以被緩存到內存中,在重複搜索時,速度會比較快

如下案例表示查詢賬戶餘額介於[20000,30000]之間的所有賬戶(查詢條件和過濾條件也都可以自己定義):

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}
'

在查詢時,究竟是使用過濾器還是使用查詢,要考慮是否關注結果中的 _score字段。

執行聚合

聚合操作有點類似於我們在SQL中的聚合函數,開發者可以通過聚合操作,在一個查詢結果中同時返回查詢到的數據和聚合之後的結果,例如,按照state中的關鍵字對用戶進行分組,然後按照分組後state的關鍵字數量進行排序,並展示出排名最高的前十個,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "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;

請求執行結果如下:

注意,在這個查詢中,因爲我們將size設置爲0,因此只能看到聚合的結果,而沒有查詢結果。

另外,這種聚合操作還可以互相嵌套,如下表示計算每個state賬戶的平均存款並列出前10個:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
'

還可以按照平均餘額進行排序,如下:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
'

如下請求則表示使用首先使用年齡段進行分組 (ages 20-29, 30-39, and 40-49),然後在此基礎上再使用性別進行分組,最後再計算不同年齡段的不同性別用戶的賬戶餘額平均數:

curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "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"
              }
            }
          }
        }
      }
    }
  }
}
'

本文介紹的只是一些非常簡單的常規操作,在後面的文章中,還會就這裏的每一個話題和小夥伴們詳細分享其細節。

好了,本文就先到這裏,有問題歡迎留言討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章