前言
? ? ? ?上一節(jié)我們已經(jīng)介紹過了使用RESTful API來操作Elasticsearch了,但是上一節(jié)我們只是學到了如何新增文檔、刪除文檔和通過文檔id獲取文檔,那接下來我們將來學習一下使用RESTful API來操作Elasticsearch的文檔搜索。
簡單搜索
首先我們來看看不帶任何搜索條件的最簡單的搜索:
GET /store/employee/_search
{
"took": 203,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "store",
"_type": "employee",
"_id": "2",
"_score": 1,
"_source": {
"name": "李四",
"age": 24,
"about": "我是一個作家",
"interests": [
"看書",
"寫作"
]
}
},
{
"_index": "store",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"name": "張三",
"age": 25,
"about": "我是一個中國人,張姓在中國是大姓,你有神馬意見嗎?",
"interests": [
"sports",
"music"
]
}
},
省略下面的數(shù)據(jù).....
]
}
}
通過上面的這個RESTful API我們可以發(fā)現(xiàn),我們查詢的依然是store索引庫和employee文檔類型,但是該API跟我們上一節(jié)查詢的又有不一樣的地方,上一節(jié)內(nèi)容我們給了一個文檔id,獲取指定的文檔內(nèi)容,而現(xiàn)在我們沒有給指定的文檔id,取而代之的是用了一個_search這個URL后綴。我們來看看這個返回結(jié)果中各個字段分別代表什么意思:
took:是查詢花費的時間,毫秒單位
time_out:標識查詢是否超時
_shards:描述了查詢分片的信息,查詢了多少個分片、成功的分片數(shù)量、失敗的分片數(shù)量等
hits:搜索的結(jié)果,total是全部的滿足的文檔數(shù)目,hits是返回的實際數(shù)目(默認是10)
_score是文檔的分數(shù)信息,與排名相關度有關,參考各大搜索引擎的搜索結(jié)果,就容易理解。
帶搜索條件的搜索
GET /store/employee/_search?q=name:張三
{
"took": 292,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.5753642,
"hits": [
{
"_index": "store",
"_type": "employee",
"_id": "1",
"_score": 0.5753642,
"_source": {
"name": "張三",
"age": 25,
"about": "我是一個中國人,張姓在中國是大姓,你有神馬意見嗎?",
"interests": [
"sports",
"music"
]
}
}
]
}
}
通過以上的RESTful API我們可以發(fā)現(xiàn):我們這次使用的RESTful API還是上面那個URL,但是我們給這個URL路徑傳入了參數(shù)了,這個參數(shù)名叫q,參數(shù)的值為name:張三,這就表示我們這次的搜索是需要指定特定內(nèi)容的文檔的,而這個特定內(nèi)容就是name這個字段,值為張三的文檔。
使用DSL語句搜索
我們除了可以使用簡單的傳參方式搜索外,還可以使用Elasticsearch提供豐富且靈活的查詢語言:DSL(Domain Specific Language特定領域語言)查詢,它允許你構(gòu)建更加復雜、強大的查詢。DSL以JSON數(shù)據(jù)格式為載體,用HTTP請求體傳輸數(shù)據(jù)內(nèi)容。
match查詢:
POST /store/employee/_search
{ "query" : { "match" : { "name" : "張" } } }
{
"took": 8,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "store",
"_type": "employee",
"_id": "1",
"_score": 0.2876821,
"_source": {
"name": "張三",
"age": 25,
"about": "我是一個中國人,張姓在中國是大姓,你有神馬意見嗎?",
"interests": [
"sports",
"music"
]
}
}
]
}
}
以上的這種搜索方式跟我們之前使用簡單搜索效果是一樣的,{ "query" : { "match" : { "name" : "張" } } }這個JSON數(shù)據(jù)就是Elasticsearch的DSL查詢語言,它需要放到HTTP請求體中,query代表執(zhí)行搜索操作,在該屬性下,可以靈活的使用各種查詢類型進行組合,match代表DSL語句中的其中一種查詢類型,在該屬性中就可以匹配我們需要查詢文檔的什么字段和字段值了,并且可以匹配多個字段。
同時我們根據(jù)返回內(nèi)容上看發(fā)現(xiàn),搜索出的文檔score(得分)為0.28..,這是因為我們這次的搜索沒有精準的匹配name字段為“張三”的文檔,而是只匹配“張”,那么搜索出的文檔得分就比較低了。
除此之外,我們還可以結(jié)合DSL語句的其他語法來控制搜索結(jié)果。如:
{ "query" : { "match_all":{} }, "size":2 }
搜索全部員工文檔,并返回前面兩條數(shù)據(jù),如果不給size字段,那么就返回前10條。
{ "query" : { "match" : { "name" : "張" } }, "from":2, "size":2 }
搜索name字段帶有“張”的員工文檔,并且返回搜索結(jié)果從第二條開始,共兩條數(shù)據(jù)。
{ "query" : { "match_all":{} }, "sort":{ "age":{"order":"desc"} } }
搜索全部文檔,并且返回結(jié)果安照“age”字段做倒序排序。
{ "query":{ "match_phrase":{ "about":"rock climbing" } } }
使用match的話我們只能匹配到一個單詞,但是如果我們想要同時匹配多個單詞的話就不能使用match了,而是使用match_phrase,該屬性意思是短語搜索,例如上面這條DSL語句就是搜索包含rock climbing這個短語的文檔,這個短語包含兩個單詞,這個兩個單詞是需要相鄰的。
bool查詢:
elasticsearch還提供了bool語法查詢,該語法可以把多個查詢條件組合在一起,看以下的例子:
POST /store/employee/_search
{ "query": { "bool": { "must": [ { "match": { "name": "張" } }, { "range": { "age": { "gte":25 } } } ] } } }
查詢結(jié)果如下:
{
"took": 18,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1.5753641,
"hits": [
{
"_index": "store",
"_type": "employee",
"_id": "1",
"_score": 1.5753641,
"_source": {
"name": "張三",
"age": 25,
"about": "我是一個中國人,張姓在中國是大姓,你有神馬意見嗎?",
"interests": [
"sports",
"music"
]
}
}
]
}
}
以上的查詢涉及到DSL新的語法:bool查詢,這是一個可以讓我們把多個搜索條件組合在一起的語法,結(jié)合屬性must意思為所有組合條件都必須滿足。同時這里還涉及到兩個新的屬性:range意思為范圍查詢和gte屬性,意思為大于等于(gt為大于)。
那么整個查詢語句意思為查詢名字帶有“張”,并且年齡大于等于25歲的員工文檔。
此外,bool查詢語法除了must之外,還有must_not和should。
如果我們把上面那條查詢語句中的must改成should,那么意思就變成了:查詢名字帶有“張”,或者年齡大于等于25歲的員工文檔。
在一條bool查詢語句中還可以把must,must_not和should組合成更復雜的查詢語句,比如以下的這條語句:
{ "query": { "bool": { "must": [ { "match": { "name": "張" } }, { "range": { "age": { "gte":25 } } } ], "must_not":{ "match":{ "age":29 } } } } }
該語句意思是查詢名字包含“張”并且年齡大于等于25歲的,但是不包含年齡為29歲的員工文檔。
由此可以看出,bool查詢可以很靈活的組合各種查詢條件,類似于我們SQL語句中的where多個條件組合。
過濾查詢
之前我們說過score字段指定了文檔的分數(shù),使用query搜索會計算文檔的分數(shù),最后通過分數(shù)確定哪些文檔更相關,返回哪些文檔,并且文檔的排序默認是按分數(shù)倒序的。但是有的時候我們可能對分數(shù)不感興趣,就可以使用filter進行過濾,它不會去計算分值,因此效率也就更高一些。我們來看看以下這條查詢語句:
POST /store/employee/_search
{ "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "age": { "gt": 20, "lt": 25 } } } } } }
{
"took": 44,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "store",
"_type": "employee",
"_id": "2",
"_score": 1,
"_source": {
"name": "李四",
"age": 24,
"about": "I love to go rock climbing",
"interests": [
"sports",
"music",
"coding"
]
}
},
{
"_index": "store",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"name": "張三",
"age": 25,
"about": "我是一個中國人,張姓在中國是大姓,你有神馬意見嗎?",
"interests": [
"sports",
"music"
]
}
}
]
}
}
filter過濾可以嵌套在bool查詢內(nèi)部使用,比如這條語句意思就是過濾查詢年齡大于20,小于25之間的員工文檔。
高亮結(jié)果
所有帶有全文檢索的應用在搜索出來的文檔結(jié)果上都會把搜索詞高亮標記,這樣用戶就可以直觀的知道為什么這些文檔和查詢條件相匹配了。在Elasticsearch中高亮片段是非常容易的,我們可以來看看以下這個例子:
GET /store/employee/_search
{ "query" : { "match" : { "name" : "張" } }, "highlight": { "fields" : { "name" : {} } } }
{
"took": 8,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "store",
"_type": "employee",
"_id": "1",
"_score": 0.2876821,
"_source": {
"name": "張三",
"age": 25,
"about": "我是一個中國人,張姓在中國是大姓,你有神馬意見嗎?",
"interests": [
"sports",
"music"
]
},
"highlight": {
"name": [
"<em>張</em>三"
]
}
}
]
}
}
這樣,我們就可以把搜索名字帶有“張”的員工文檔搜索出來,并且搜索的結(jié)果多出了一個highlight的屬性,在該屬性中有name這個文檔字段,其中字段值中的“張”是被<em>標簽包裹的,代表高亮的意思,因為我們的搜索詞就是“張”,所以elasticsearch把結(jié)果中的“張”高亮了。
以上就是常用的搜索語句用法,當然,DSL功能非常強大,語法也不僅僅只有這些,但是目前為止,我們先學會了這些已經(jīng)足夠我們使用了,后續(xù)章節(jié)如何會涉及到其他的DSL語句我們再逐一的詳細介紹其用法。