Writing queries Version:5.x 英文原文地址: "Writing queries" 將數據索引到了 Elasticsearch 之後,就可以準備搜索它們了。Elasticsearch 提供了一個強大的查詢 DSL ,使得用戶可以定義個性化的搜索邏輯。這個 DSL 是基於 JS ...
Writing queries
Version:5.x
英文原文地址:Writing queries
將數據索引到了 Elasticsearch 之後,就可以準備搜索它們了。Elasticsearch 提供了一個強大的查詢 DSL ,使得用戶可以定義個性化的搜索邏輯。這個 DSL 是基於 JSON 的,NEST 提供了 Fluent API 和 Object Initializer 語法來實現 DSL 。
Match All query
最簡單的查詢應該就是 match_all
了,這種查詢會返回所有的文檔,並給每份文檔的 _score
統一賦值為 1.0
匹配的文檔並不是都會在一次響應中全部返回,預設情況下只返回前十份。你可以使用
from
和size
來將結果分頁
var searchResponse = client.Search<Project>(s => s
.Query(q => q
.MatchAll()
)
);
上面的請求會被序列化成下麵這個 JSON 對象
{
"query": {
"match_all": {}
}
}
由於 match_all
查詢很常見,因此前面的慄子有一個簡單的寫法,兩種方式序列化的結果是一樣的
searchResponse = client.Search<Project>(s => s
.MatchAll()
);
前面的兩個慄子都是使用 Fluent API 來描述查詢。NEST 還公開了一種 Object Initializer 語法去構造查詢
var searchRequest = new SearchRequest<Project>
{
Query = new MatchAllQuery()
};
searchResponse = client.Search<Project>(searchRequest);
Search request parameters
search request 有一些可用的參數,你可以參閱 search 以獲得詳細的信息。
Common queries
預設情況下,文檔會根據 _score
降序返回。每個命中的 _score
是根據文檔和查詢條件的匹配程度計算的關聯分數。數字越大,表示越符合查詢條件。
NEST 提供了許多搜索查詢,它們都記錄在 Query DSL 參考部分。在這裡,我們要強調用戶經常執行的三類查詢操作
- Structured search
- Unstructured search
- Combining queries
Structured search
結構化搜索是指,查詢具有固定結構的數據。日期、時間和數字都是結構化的,查詢這些類型的欄位通常是為了查找準確的匹配項、某個範圍內的值等等。文本也可以結構化,比如博客里使用的關鍵字標簽。
通過結構化搜索,查詢的答案總是 “是” 或者 “否”。也就是說,文檔要麼匹配查詢,要麼就不匹配。
術語級別的查詢通常用於結構化搜索。下麵的慄子查找開始日期在指定範圍內的文檔
var searchResponse = client.Search<Project>(s => s
.Query(q => q
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
);
(1) 查找開始於 2017 年的所有的 Project
會生成以下查詢 JSON
{
"query": {
"range": {
"startedOn": {
"lt": "2018-01-01T00:00:00",
"gte": "2017-01-01T00:00:00"
}
}
}
}
因為這個查詢的答案只有 yes
和 no
兩種情況,我自然就不需要給查詢計分了。為此,我們可以把這個查詢包裝在一個 bool
查詢 filter
子句中,這樣就可以讓查詢在篩選上下文中執行
searchResponse = client.Search<Project>(s => s
.Query(q => q
.Bool(b => b
.Filter(bf => bf
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
)
)
);
{
"query": {
"bool": {
"filter": [
{
"range": {
"startedOn": {
"lt": "2018-01-01T00:00:00",
"gte": "2017-01-01T00:00:00"
}
}
}
]
}
}
}
在篩選上下文中執行查詢的好處是,Elasticsearch 可以放棄計算相關性分數,還可以緩存篩選器從而獲得更快的後續性能
重要:術語級別的查詢沒有分析階段,也就是說不會分析查詢的輸入,進而在反向索引中尋找輸入的精確匹配。如果一個欄位在索引時進行了分析,那麼再通過術語級別查詢多半會失敗。
當欄位僅用於精確匹配時,應當考慮將其映射為
keyword
類型。如果欄位既用於精確匹配,又用於全文搜索,則應考慮將其映射為multi fields
。
Unstructured search
另一個常見的用例是,在全文欄位中搜索以查找最相關的文檔。
全文查詢用於非結構化的搜索。在這裡,我們使用 match
查詢來查找開發人員的名字中包含 “Russ” 的所有文檔
var searchResponse = client.Search<Project>(s => s
.Query(q => q
.Match(m => m
.Field(f => f.LeadDeveloper.FirstName)
.Query("Russ")
)
)
);
會生成以下查詢 JSON
{
"query": {
"match": {
"leadDeveloper.firstName": {
"query": "Russ"
}
}
}
}
重要:全文查詢有分析階段。也就是說要分析查詢輸入,然後將分析後產生的術語和反向索引中的術語進行比較。
通過在映射期間給欄位設置分析器,你可以完全控制索引和搜索階段的分析過程。
Combining queries
一個非常常見的情況是,將不同的查詢組合在一起形成一個複合查詢。其中最常見的是 bool
查詢
var searchResponse = client.Search<Project>(s => s
.Query(q => q
.Bool(b => b
.Must(mu => mu
.Match(m => m (1)
.Field(f => f.LeadDeveloper.FirstName)
.Query("Russ")
), mu => mu
.Match(m => m (2)
.Field(f => f.LeadDeveloper.LastName)
.Query("Cam")
)
)
.Filter(fi => fi
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01)) (3)
)
)
)
)
);
(1) 匹配開發人員的名字包含 Russ
的所有文檔
(2) ... 並且開發人員的姓氏包含 Cam
(3) ... 並且項目開始於 2017
會生成以下查詢 JSON
{
"query": {
"bool": {
"must": [
{
"match": {
"leadDeveloper.firstName": {
"query": "Russ"
}
}
},
{
"match": {
"leadDeveloper.lastName": {
"query": "Cam"
}
}
}
],
"filter": [
{
"range": {
"startedOn": {
"lt": "2018-01-01T00:00:00",
"gte": "2017-01-01T00:00:00"
}
}
}
]
}
}
}
一份文檔必須滿足三個查詢才算匹配成功
- 對名字和姓氏的
match
查詢有助於計算出相關性分數,因為它們都在查詢上下文中執行 - 針對開始日期的
range
查詢是在篩選上下文中執行的,索引沒有為匹配的文檔計算分數(針對這個查詢的所有文檔具有相同的分數1.0
)
由於 bool
查詢非常常見,因此 NEST 在查詢上重載了運算符,以使得 bool
查詢的形式更加簡潔。前面的 bool
查詢可以更加簡潔地表示為
searchResponse = client.Search<Project>(s => s
.Query(q => q
.Match(m => m
.Field(f => f.LeadDeveloper.FirstName)
.Query("Russ")
) && q
.Match(m => m
.Field(f => f.LeadDeveloper.LastName)
.Query("Cam")
) && +q
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
);
查看 writing bool
queries ,瞭解有關 bool
查詢的更多詳細信息和示例
Search response
搜索查詢返回的響應是一個 ISearchResponse<T>
對象,其中 T
是在調用搜索方法時傳入的泛型參數類型。響應對象有幾個屬性,其中你最可能使用的是 .Documents
,我們將在下麵演示。
Matching documents
獲取匹配搜索查詢的文檔是相當簡單的
var searchResponse = client.Search<Project>(s => s
.Query(q => q
.MatchAll()
)
);
var projects = searchResponse.Documents;
.Documents
是對下麵這段邏輯的一個方便的速記
searchResponse.HitsMetaData.Hits.Select(h => h.Source);
並且可以從命中集合中檢索有關每個命中的其他元數據。下麵的示例在使用 highlighting
時檢索命中的突出顯示
var highlights = searchResponse.HitsMetaData.Hits.Select(h => h
.Highlights
);