NEST - 編寫查詢

Writing queries

Version:5.xhtml

英文原文地址:Writing queriesjson

將數據索引到了 Elasticsearch 以後,就能夠準備搜索它們了。Elasticsearch 提供了一個強大的查詢 DSL ,使得用戶能夠定義個性化的搜索邏輯。這個 DSL 是基於 JSON 的,NEST 提供了 Fluent API 和 Object Initializer 語法來實現 DSL 。c#

Match All query

最簡單的查詢應該就是 match_all 了,這種查詢會返回全部的文檔,並給每份文檔的 _score 統一賦值爲 1.0api

匹配的文檔並非都會在一次響應中所有返回,默認狀況下只返回前十份。你可使用 fromsize 來將結果分頁緩存

var searchResponse = client.Search<Project>(s => s
    .Query(q => q
        .MatchAll()
    )
);

上面的請求會被序列化成下面這個 JSON 對象elasticsearch

{
  "query": {
    "match_all": {}
  }
}

因爲 match_all 查詢很常見,所以前面的栗子有一個簡單的寫法,兩種方式序列化的結果是同樣的ide

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 以得到詳細的信息。ui

Common queries

默認狀況下,文檔會根據 _score 降序返回。每一個命中的 _score 是根據文檔和查詢條件的匹配程度計算的關聯分數。數字越大,表示越符合查詢條件。code

NEST 提供了許多搜索查詢,它們都記錄在 Query DSL 參考部分。在這裏,咱們要強調用戶常常執行的三類查詢操做

  • Structured search
  • Unstructured search
  • Combining queries

結構化搜索是指,查詢具備固定結構的數據。日期、時間和數字都是結構化的,查詢這些類型的字段一般是爲了查找準確的匹配項、某個範圍內的值等等。文本也能夠結構化,好比博客裏使用的關鍵字標籤。

經過結構化搜索,查詢的答案老是 「是」 或者 「否」。也就是說,文檔要麼匹配查詢,要麼就不匹配。

術語級別的查詢一般用於結構化搜索。下面的栗子查找開始日期在指定範圍內的文檔

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"
      }
    }
  }
}

由於這個查詢的答案只有 yesno 兩種狀況,我天然就不須要給查詢計分了。爲此,咱們能夠把這個查詢包裝在一個 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

另外一個常見的用例是,在全文字段中搜索以查找最相關的文檔。

全文查詢用於非結構化的搜索。在這裏,咱們使用 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"
            }
          }
        }
      ]
    }
  }
}

一份文檔必須知足三個查詢纔算匹配成功

  1. 對名字和姓氏的 match 查詢有助於計算出相關性分數,由於它們都在查詢上下文中執行
  2. 針對開始日期的 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 
);
相關文章
相關標籤/搜索