上一篇文章介紹了ES中的Rest API,本章将重點介紹ES中的查詢API的使用。由于筆者在實際項目僅僅将ES用作索引資料庫,并沒有深入研究過ES的搜尋功能。而且鑒于筆者的搜尋引擎知識有限,本文将僅僅介紹ES簡單(非全文)的查詢API。
筆者原本打算在本文中介紹聚合API的内容,但是寫着寫着發現文章有點過長,不便于閱讀,故将聚合API的内容移至下一篇部落格中。
引言
單單介紹理論和API是乏味和低效率的,本文将結合一個實際的例子來介紹這些API。下表是本文資料表的表結構,表名(type)為“student”。注意,studentNo是本表的id,也就是_id字段的值與studentNo的值保持一緻。
字段名 | 字段含義 | 類型 | 是否能被索引 | 備注 |
---|---|---|---|---|
studentNo | 學号 | string | 是 | id |
name | 姓名 | string | 是 | |
sex | 性别 | string | 是 | |
age | 年齡 | integer | 是 | |
birthday | 出生年月 | date | 是 | |
address | 家庭住址 | string | 是 | |
classNo | 班級 | string | 是 | |
isLeader | 是否為班幹部 | boolean | 是 |
上面的表結構所對應的mapping如下,将資料儲存在索引名為“student”的索引中。
{
"student": {
"properties": {
"studentNo": {
"type": "string",
"index": "not_analyzed"
},
"name": {
"type": "string",
"index": "not_analyzed"
},
"male": {
"type": "string",
"index": "not_analyzed"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd"
},
"address": {
"type": "string",
"index": "not_analyzed"
},
"classNo": {
"type": "string",
"index": "not_analyzed "
},
"isLeader": {
"type": "boolean"
}
}
}
}
索引中儲存的資料如下,下面介紹的所有API都将基于這個資料表。
studentNo | name | male | age | birthday | classNo | address | isLeader |
---|---|---|---|---|---|---|---|
1 | 劉備 | 男 | 24 | 1985-02-03 | 1 | 湖南省長沙市 | true |
2 | 關羽 | 男 | 22 | 1987-08-23 | 2 | 四川省成都市 | false |
3 | 糜夫人 | 女 | 19 | 1990-06-12 | 1 | 上海市 | false |
4 | 張飛 | 男 | 20 | 1989-07-30 | 3 | 北京市 | false |
5 | 諸葛亮 | 男 | 18 | 1992-04-27 | 2 | 江蘇省南京市 | true |
6 | 孫尚香 | 女 | 16 | 1994-05-21 | 3 | false | |
7 | 馬超 | 男 | 19 | 1991-10-20 | 1 | 黑龍江省哈爾濱市 | false |
8 | 趙雲 | 男 | 23 | 1986-10-26 | 2 | 浙江省杭州市 | false |
查詢API
ES中的查詢非常靈活,為使用者提供了非常友善而強大的API。個人覺得ES的調用接口設計得非常好,所有接口合理且風格一緻,值得好好研究!
Query和Filter
ES為使用者提供兩類查詢API,一類是在查詢階段就進行條件過濾的query查詢,另一類是在query查詢出來的資料基礎上再進行過濾的filter查詢。這兩類查詢的差別是:
- query方法會計算查詢條件與待查詢資料之間的相關性,計算結果寫入一個score字段,類似于搜尋引擎。filter僅僅做字元串比對,不會計算相關性,類似于一般的資料查詢,是以filter得查詢速度比query快。
- filter查詢出來的資料會自動被緩存,而query不能。
query和filter可以單獨使用,也可以互相嵌套使用,非常靈活。
Query查詢
下面的情況下适合使用query查詢:
- 需要進行全文搜尋。
- 查詢結果依賴于相關性,即需要計算查詢串和資料的相關性。
(1)Match All Query
查詢所有的資料,相當于不帶條件查詢。下面的代碼是一個典型的match_all查詢的調用方式。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"match_all": {}
}
}
'
查詢結果如下。其他所有的查詢都是傳回這種格式的資料。
{
"took": , // 查詢耗時(毫秒)
"timed_out": false, // 是否逾時
"_shards": {
"total": , // 總共查詢的分片數
"successful": , // 查詢成功的分片數
"failed": // 查詢失敗的分片數
},
"hits": {
"total": , // 本次查詢的記錄數
"max_score": , // 查詢所有資料中的最大score
"hits": [ // 資料清單
{
"_index": "student", // 資料所屬的索引名
"_type": "student", // 資料所屬的type
"_id": "4", // 資料的id值
"_score": , // 該記錄的score
"_source": { // ES将原始資料儲存到_source字段中
"studentNo": "4",
"name": "張飛",
"male": "男",
"age": "20",
"birthday": "1989-07-30",
"classNo": "3",
"isLeader": "F"
}
},
{
…… // 其他的資料格式相同,就不列出來了
}
]
}
}
查詢時,你會發現無論資料量有多大,每次最多隻能查到10條資料。這是因為ES服務端預設對查詢結果做了分頁處理,每頁預設的大小為10。如果想自己指定查詢的資料,可使用from和size字段,并且按指定的字段排序。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"match_all": {}
},
"from": , // 從2條記錄開始取
"size": , // 取4條資料
"sort": {
"studentNo": { // 按studentNo字段升序
"order": "asc"// 降序為desc
}
}
}
'
注意:不要把from設得過大(超過10000),否則會導緻ES服務端因頻繁GC而無法正常提供服務。其實實際項目中也沒有誰會翻那麼多頁,但是為了ES的可用性,務必要對分頁查詢的頁碼做一定的限制。
(2)term query
詞語查詢,如果是對未分詞的字段進行查詢,則表示精确查詢。查找名為“諸葛亮”的學生,查詢結果為學号為5的記錄。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"term": {
"name": "諸葛亮"
}
}
}
'
(3)Bool Query
Bool(布爾)查詢是一種複合型查詢,它可以結合多個其他的查詢條件。主要有3類邏輯查詢:
- must:查詢結果必須符合該查詢條件(清單)。
- should:類似于in的查詢條件。如果bool查詢中不包含must查詢,那麼should預設表示必須符合查詢清單中的一個或多個查詢條件。
- must_not:查詢結果必須不符合查詢條件(清單)。
查找2班的班幹部,查詢結果為學号為5的記錄。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"bool": {
"must": [
{
"term": {
"classNo": ""
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
'
(4)Ids Query
id字段查詢。查詢資料id值為1和2的同學,由于id的值與studentNo相同,故查詢結果為學号為1和2的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"ids": {
"type": "student",
"values": [
"",
""
]
}
}
}
'
(5)Prefix Query
字首查詢。查找姓【趙】的同學,查詢結果是學号為8的趙雲。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"prefix": {
"name": "趙"
}
}
}
'
(6)Range Query
範圍查詢,針對date和number類型的資料。查找年齡到18~20歲的同學,查詢結果是學号為3、4、5、7的記錄。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"range": {
"age": {
"gte": "18", // 表示>=
"lte": "20" // 表示<=
}
}
}
}
'
實際上,對于date類型的資料,ES中以其時間戳(長整形)的形式存放的。
(7)Terms Query
多詞語查詢,查找符合詞語清單的資料。如果要查詢的字段索引為not_analyzed類型,則terms查詢非常類似于關系型資料庫中的in查詢。下面查找學号為1,3的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"terms": {
"studentNo": [
"",
""
]
}
}
}
'
(8)Wildcard Query
通配符查詢,是簡化的正規表達式查詢,包括下面兩類通配符:
- * 代表任意(包括0個)多個字元
- ? 代表任意一個字元
查找名字的最後一個字是“亮”的同學,查詢結果是學号為5的諸葛亮。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"wildcard": {
"name": "*亮"
}
}
}
'
(9)Regexp Query同學
正規表達式查詢,這是最靈活的字元串類型字段查詢方式。查找家住長沙市的學生,查詢結果為學号為1的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"regexp": {
"address": ".*長沙市.*" // 這裡的.号表示任意一個字元
}
}
}
'
Filter查詢
下面的情況下适合使用filter查詢:
- yes/no的二進制查詢
- 針對精确值進行查詢
filter和query的查詢方式有不少是重疊的,是以本節僅僅介紹API的調用,一些通用的注意的事項就不再重複了。
(1)Term Filter
詞語查詢,如果是對未分詞的字段進行查詢,則表示精确查詢。查找名為“諸葛亮”的學生,查詢結果為學号為5的記錄。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"term": {
"name": "諸葛亮",
"_cache" : true // 與query主要是這裡的差別,可以設定資料緩存
}
}
}
'
filter查詢方式都可以通過設定_cache為true來緩存資料。如果下一次恰好以相同的查詢條件進行查詢并且該緩存沒有過期,就可以直接從緩存中讀取資料,這樣就大大加快的查詢速度。
(2)Bool Filter
查找2班的班幹部,查詢結果為學号為5的記錄。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"bool": {
"must": [
{
"term": {
"classNo": ""
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
'
(3)And Filter
And邏輯連接配接查詢,連接配接1個或1個以上查詢條件。它與bool查詢中的must查詢非常相似。實際上,and查詢可以轉化為對應的bool查詢。查找2班的班幹部,查詢結果為學号為5的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"and": [
{
"term": {
"classNo": ""
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
'
(4)Or Filter
Or連接配接查詢,表示邏輯或。。查找2班或者是班幹部的學生名單,查詢結果為學号為1、2、5、8的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"or": [
{
"term": {
"classNo": ""
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
'
(5)Exists Filter
存在查詢,查詢指定字段至少包含一個非null值的資料。如果字段索引為not_analyzed類型,則查詢sql中的is not null查詢方式。查詢位址存在學生,查詢結果為除了6之外的所有學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"exists": {
"field": "address"
}
}
}
'
(6)Missing Filter
缺失值查詢,與Exists查詢正好相反。查詢位址不存在的學生,查詢結果為學号為6的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"missing": {
"field": "address"
}
}
}
'
(7)Prefix Filter
字首查詢。查找姓【趙】的同學,查詢結果是學号為8的趙雲。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"prefix": {
"name": "趙"
}
}
}
'
(8)Range Filter
範圍查詢,針對date和number類型的資料。查找年齡到18~20歲的同學,查詢結果是學号為3、4、5、7的記錄。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"range": {
"age": {
"gte": "",
"lte": ""
}
}
}
}
'
(9)Terms Filter
多詞語查詢,查找符合詞語清單的資料。如果要查詢的字段索引為not_analyzed類型,則terms查詢非常類似于關系型資料庫中的in查詢。下面查找學号為1,3的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"terms": {
"studentNo": [
"",
""
]
}
}
}
'
(10)Regexp Filter
正規表達式查詢,是最靈活的字元串類型字段查詢方式。查找家住長沙市的學生,查詢結果為學号為1的學生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"regexp": {
"address": ".*長沙市.*"
}
}
}
'
總結
本文介紹了ES中的部分查詢API,這些API是一些常用的簡單API,如果需要使用更加複雜一點的API,請查閱官網文檔。下一篇文章準備介紹ES中的聚合API的使用。