NEST 的 Search 方法直接將搜尋結果轉換成強型別來提供給我們使用,可說是非常方便,那我們能不能將搜尋結果轉成另外一個強型別呢?答案是肯定的,不過如果直接將 Search 這個一般化方法指定的類別給換掉,會踩到一些雷,且看接下來的說明。
搜尋結果轉換為其他型別
要將搜尋結果轉換成其他型別,只要將 Search 這個一般化的方法所指定的類別換掉即可,不過得改一些地方。
下面這個範例是我想要將搜尋結果轉換為 TextItemViewModel 這個類別,因為 Search 這個方法預設認定指定的類別名稱就是 document 的 type,所以直接替換掉是會出錯的,我們必須多加一行 Type("textitem")
指定 type 為 textitem。
接著 QueryString 裡面的 OnFields 方法,我們必須將指定要搜尋的欄位改以字串陣列的方式塞進去,因為要轉換的目的類別不一定會有我們指定的搜尋欄位,因此必須以這種方式給入參數。
private IEnumerable<TextItemViewModel> QueryByKeyword()
{
// 搜尋條件為 Content 這個欄位的值包含 "馬英九"
return
this.myindexClient.Search<TextItemViewModel>(s => s
.From(0) // 從第 0 筆
.Size(10) // 抓 10 筆
.Type("textitem")
.Query(q =>
q.QueryString(qs => qs.OnFields(new string[] { "content" }).Query("\"馬英九\""))))
.Hits
.Select(h => h.Source);
}
搜尋結果轉換為其他型別(使用 Fields 方法)
剛剛介紹的這種直接轉換的方式會有一個 overhead,就是從 Elasticsearch 取得的資料還是完整的 document,是已經都取得了之後才由 NEST 幫我們做 Reflection,其他不要的欄位就造成了效能的浪費,如果不要這麼多欄位我們可以加上 Fields 來指定我們想要的欄位就可以了。
下面我使用 Fields 方法指定傳回 Id、AuthorId、AuthorName、Summary 這 4 個欄位,而我自行寫了一個 ReflectTo 的方法將回傳的結果 Reflect 成 TextItemViewModel 這個類別。
private IEnumerable<TextItemViewModel> QueryByKeywordUseFields()
{
// 搜尋條件為 Content 這個欄位的值包含 "馬英九"
return
this.myindexClient.Search<TextItemViewModel>(s => s
.From(0) // 從第 0 筆
.Size(10) // 抓 10 筆
.Type("textitem")
.Query(q =>
q.QueryString(qs => qs.OnFields(new string[] { "content" }).Query("\"馬英九\"")))
.Fields(p => p.Id, p => p.AuthorId, p => p.AuthorName, p => p.Summary))
.Hits
.Where(h => h.Fields.FieldValuesDictionary != null && h.Fields.FieldValuesDictionary.Count > 0)
.Select(h => ReflectTo<TextItemViewModel>(h.Fields.FieldValuesDictionary));
}
private static T ReflectTo<T>(IDictionary<string, object> dict)
{
Type type = typeof(T);
var obj = Activator.CreateInstance(type);
foreach (var fv in dict)
{
var property = type.GetProperties().Where(p => p.Name.ToLower() == fv.Key.ToLower()).FirstOrDefault();
if (property != null && fv.Value != null)
{
property.SetValue(obj, ((Newtonsoft.Json.Linq.JArray)fv.Value).First.ToObject<object>());
}
}
return (T)obj;
}
刪除索引
我們想要刪除索引就簡單了,使用 DeleteByQuery 這個方法就可以了,下面的範例是我想要刪除 AuthorId 為 123456 的索引資料。
private void DeleteDocuments()
{
var deletedResult =
this.myindexClient.DeleteByQuery<TextItem>(d =>
d.Query(q =>
q.Term(p => p.AuthorId, "123456")
));
}
參考資料
< Source Code >