Elasticsearch(四)elasticsearch複雜檢索
Query-string 搜尋通過命令非常方便地進行臨時性的即席搜尋 ,但它有自身的侷限性(參見 輕量 搜尋 )。Elasticsearch 提供一個豐富靈活的查詢語言叫做 查詢表示式 , 它支援構建更加複雜和健壯的查詢。
領域特定語言 (DSL), 指定了使用一個 JSON 請求。我們可以像這樣重寫之前的查詢所有 Smith 的搜尋 :
GET /megacorp/employee/_search
{
“query” : {
“match” : {
“last_name” : “Smith”
}
}
}
View in Sense
返回結果與之前的查詢一樣,但還是可以看到有一些變化。其中之一是,不再使用 query-string 引數,而是一個請求體替代。這個請求使用 JSON 構造,並使用了一個 match 查詢(屬於查詢型別之一,後續將會了解)。
更復雜的搜尋
現在嘗試下更復雜的搜尋。 同樣搜尋姓氏為 Smith 的僱員,但這次我們只需要年齡大於 30 的。查詢需要稍作調整,使用過濾器 filter ,它支援高效地執行一個結構化查詢。
GET /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter" : {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
這部分與我們之前使用的 match 查詢 一樣。
這部分是一個 range 過濾器 , 它能找到年齡大於 30 的文件,其中 gt 表示_大於(_great than)。
目前無需太多擔心語法問題,後續會更詳細地介紹。只需明確我們添加了一個 過濾器 用於執行一個範圍查詢,並複用之前的 match 查詢。現在結果只返回了一個僱員,叫 Jane Smith,32 歲。
{
...
"hits": {
"total": 1,
"max_score": 0.30685282,
"hits": [
{
...
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
bool簡單介紹
發生 描述
must 該條款(查詢)必須出現在匹配的檔案,並將有助於得分。
filter 子句(查詢)必須出現在匹配的文件中。然而不像 must查詢的分數將被忽略。Filter子句在過濾器上下文中執行,這意味著評分被忽略,子句被考慮用於快取記憶體。
should 子句(查詢)應該出現在匹配的文件中。如果 bool查詢位於查詢上下文中並且具有mustorfilter子句,那麼bool即使沒有 should查詢匹配,文件也將匹配查詢。在這種情況下,這些條款僅用於影響分數。如果bool查詢是過濾器上下文 或者兩者都不存在,must或者filter至少有一個should查詢必須與文件相匹配才能與bool查詢匹配。這種行為可以通過設定minimum_should_match引數來顯式控制 。
must_not 子句(查詢)不能出現在匹配的文件中。子句在過濾器上下文中執行,意味著評分被忽略,子句被考慮用於快取記憶體。因為計分被忽略,0所有檔案的分數被返回。
即,must:必須匹配,filter:匹配的結果過濾,should:至少有一個 must_not:不能匹配
Client程式演示bool查詢
term
增加一個方法:
/*
* 簡單運用一個bool查詢,查詢姓Smith且年齡大於的員工
* 查詢姓Smith的員工
* 過濾為大於30歲的
*/
private static void findEmployeeByAgeAndName(Client client) {
SearchRequestBuilder request = client.prepareSearch("megacorp1")
.setTypes("employee1")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setQuery(QueryBuilders.boolQuery().must(termQuery("last_name","Smith")).filter(rangeQuery("age").gt(30)));
// SearchResponse response = request.get();
printResponseHits(request.get());
}
封裝檢視結果方法:
//檢視結果
private static void printResponseHits(SearchResponse response) {
SearchHits searchHits = response.getHits();
Iterator<SearchHit> iterator = searchHits.iterator();
while(iterator.hasNext()) {
SearchHit hit = iterator.next();
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
System.out.println("index="+index+" type="+type+" id="+id+" score="+score+" source-->"+hit.getSourceAsString());
}
}
Main方法中增加呼叫
// 5.查詢姓smith的僱員,過濾過濾器查詢示例 bool查詢
findEmployeeByAgeAndName(client);
結果顯示:
index=megacorp1 type=employee1 id=2 score=1.2809339 source–>{“first_name”:”Jane”,”last_name”:”Smith”,”age”:”32”,”about”:”I like to collect rock albums”,”interests”:[“music”]}
有興趣的可以自己debug到request檢視bool的請求
Head外掛示例
match
我們可以現在用match來寫下:
將剛才的例子改為如下
SearchRequestBuilder request = client.prepareSearch("megacorp1")
.setTypes("employee1")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setQuery(QueryBuilders.boolQuery().must(matchQuery("last_name","Smith")).filter(rangeQuery("age").gt(30)));
// SearchResponse response = request.get();
printResponseHits(request.get());
再次呼叫此方法返回結果為:
index=megacorp1 type=employee1 id=2 score=1.3862944 source–>{“first_name”:”Jane”,”last_name”:”Smith”,”age”:”32”,”about”:”I like to collect rock albums”,”interests”:[“music”]}
head外掛示例
我們的結果沒有區別,因為這裡我們的索引不會進行分詞解析。
我們去可以之前可以分詞解析的索引megacorp中實驗以下:
term:rock climbing
Match:rock climbing
可以看出檔案的結果根據相關性評分排序。整個都匹配的在第一個,匹配其中一個的在後面。
Elasticsearch 預設按照相關性得分排序,即每個文件跟查詢的匹配程度。第一個最高得分的結果很明顯:John Smith 的 about 屬性清楚地寫著 “rock climbing” 。
但為什麼 Jane Smith 也作為結果返回了呢?原因是她的 about 屬性裡提到了 “rock” 。因為只有 “rock” 而沒有 “climbing” ,所以她的相關性得分低於 John 的。
這是一個很好的案例,闡明瞭 Elasticsearch 如何 在 全文屬性上搜索並返回相關性最強的結果。Elasticsearch中的 相關性 概念非常重要,也是完全區別於傳統關係型資料庫的一個概念,資料庫中的一條記錄要麼匹配要麼不匹配。
至於之前的last_name為何查不出,還是一個疑問。
嘗試將lastname增加這個沒有下劃線的欄位,term依舊沒有查出來。
嘗試將內容改為Smith Smith中間空格形式也查不出來term
歡迎解惑。
短語搜尋
找出一個屬性中的獨立單詞是沒有問題的,但有時候想要精確匹配一系列單詞或者短語 。 比如, 我們想執行這樣一個查詢,僅匹配同時包含 “rock” 和 “climbing” ,並且 二者以短語 “rock climbing” 的形式緊挨著的僱員記錄。
為此對 match 查詢稍作調整,使用一個叫做 match_phrase 的查詢:
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
毫無懸念,返回結果僅有 John Smith 的文件。
{
...
"hits": {
"total": 1,
"max_score": 0.23013961,
"hits": [
{
...
"_score": 0.23013961,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
]
}
}
Client程式演示
增加一個方法
/**
* match phrase查詢
* 僅匹配同時包含 “rock” 和 “climbing” ,並且 二者以短語 “rock climbing” 的形式緊挨著的僱員記錄。
* @param client 客戶端
* @param field 欄位
* @param phrase 詞語
*/
private static void findEmployeesWithOneUniqueMatchPhrase(Client client, String field, String phrase) {
SearchRequestBuilder request = client.prepareSearch("megacorp")
.setTypes("employee")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setQuery(QueryBuilders.boolQuery().must(matchPhraseQuery(field, phrase)));
printResponseHits(request.get());
}
Main方法中呼叫
// 6.match_phrase查詢 僅匹配同時包含 “rock” 和 “climbing” ,並且 二者以短語 “rock climbing” 的形式緊挨著的僱員記錄。
findEmployeesWithOneUniqueMatchPhrase(client,"about","rock climbing");
我增加了一些資料:
結果顯示:
index=megacorp type=employee id=5 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”Smith1”,”age”:25,”about”:”I love to go rock climbing”,”interests”:[“sports”,”music”]}
index=megacorp type=employee id=8 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”蜂蜜柚子”,”age”:25,”about”:”I love to go rock climbing”,”interests”:[“sports”,”music”]}
index=megacorp type=employee id=9 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”蜂蜜”,”age”:25,”about”:”I love to go rock climbing”,”interests”:[“sports”,”music”]}
index=megacorp type=employee id=10 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”Smith Smith”,”age”:25,”about”:”I love to go rock climbing”,”interests”:[“sports”,”music”]}
index=megacorp type=employee id=6 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”Smith 1”,”age”:26,”about”:”I love to go rock climbing”,”interests”:[“sports”,”art”]}
index=megacorp type=employee id=1 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”Smith”,”age”:25,”about”:”I love to go rock climbing”,”interests”:[“sports”,”music”]}
index=megacorp type=employee id=7 score=0.6449836 source–>{“first_name”:”John”,”last_name”:”蜂蜜柚子蜂蜜”,”age”:25,”about”:”I love to go rock climbing”,”interests”:[“sports”,”music”]}
可以看出結果完全符合。
Head外掛示例
{“query”:{“match_phrase”:{“about”:”rock climbing”}}}
抱歉圖發不出!!!