NEST - 編寫查詢


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

匹配的文檔並不是都會在一次響應中全部返回,默認情況下只返回前十份。你可以使用 fromsize 來將結果分頁

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

結構化搜索是指,查詢具有固定結構的數據。日期、時間和數字都是結構化的,查詢這些類型的字段通常是為了查找准確的匹配項、某個范圍內的值等等。文本也可以結構化,比如博客里使用的關鍵字標簽。

通過結構化搜索,查詢的答案總是 “是” 或者 “否”。也就是說,文檔要么匹配查詢,要么就不匹配。

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

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 
);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM