使用 Azure Search 輕鬆完成站內搜尋
- 2015-10-18
- 11213
- 0
- Microsoft Azure
其實本部落格原本就有規劃「站內搜尋」在6.0版之前使用了 Google 站內搜尋,但 Google 站內搜尋其實並不堪用,因為它本質上還是一個公開的搜尋引擎,只是搜尋範圍鎖定到指定的網站內而已,為什麼說這樣不堪用呢?因為當網站有使用「標籤雲」「推薦連結」這種屬於內部相互連結的設計,就會讓 Google 站內搜尋會搜到非常非常多毫無相關的結果,例如本文沒有提到「SkillTree」但卻因為側欄放了SkillTree的課程推薦,而導致使用者搜尋了「SkillTree」關鍵字後可以得到幾乎全站的文章,這種搜尋除了毫無意義以外,還大幅降低了使用者瀏覽體驗,非常的遭,所以在6.0上線後雖然有規劃站內搜尋的區塊,但一直沒有實做(因為我不想自己寫搜尋),終於在最近因為因緣際會開始研究 Azure Search 後發現這是一個非常好用的服務,於是花了一點點時間把站內搜尋完成了,因為 Azure Search 是屬於 PaaS (Platform-as-a-Service) ,所以整個開發的過程中,我只需要關心我的搜尋要怎麼完成即可,其他東西都交給 Azure 處理,實在是爽度破表。
Aazur Search 可以完成的功能很多,但因為demo只拿來當成 Blog 的站內搜尋所以實做的非常少,有興趣的朋友建議參考官方文件,或關注小弟我再來的公開課程(MS & twMVC),本文章只有著重在最基礎的搜尋功能。
Azure Search申請
前往 Azure 新版入口網站,找到 Azure Search 服務
輸入必要的資訊後請記得點選「定價層」修改定價
各位可以看到目前的 Azure Search 只有「標準」和「免費」兩種定價,因為我們的需求並不高,所以選擇免費的即可(一個帳號只可以建立一個免費 Azure Search)
選擇好定價後就可以按下「確定」讓系統建立 Azure Search 服務,建立完畢請點選您的服務進入設定畫面
因為 demoshop 已經放置於 Azure 並且資料庫也是使用 Azure SQL 所以我不會用「加入索引」我會使用「匯入資料」
選擇了匯入 Azure SQL 資料時系統會提示您建立資料來源,輸入您的資料
- 名稱的意義只是識別,想一個OK的名字即可
- 請把 Web.config 內的連線字串貼過來從
data source=tcp:
開始包含密碼 - 如果連線字串貼對了就可以在「資料表/檢視」看到下拉選單,就把要拿來做索引的選取即可
過幾秒後系統會自動把選擇的資料表欄位秀出來
- 索引名稱很重要,因為你在來搜尋的時候會使用到該名稱,請不要亂寫
- 金鑰就選擇 PK 欄位吧
請仔細挑選擷取、篩選、排序、FACET、搜尋欄位,如果不懂這些是什麼的話,你可以很大器的全勾
再來就是建立索引子
- 一樣是給一個識別用的名稱
- 看您需要資料多快更新一次,因為我一天很少寫超過兩篇文,所以我就選擇了「每日」
- 高水位線資料行,白話一點解釋就是用哪個欄位判斷資料被更新,如果您的資料表中有設計最後更新時間的欄位,就選它吧
終於搞定了一切,按下確定吧
這時候您的 Azure Search 已經建立完畢了,索引也會開始自動更新,因為 Azure Search 提供了 REST API 所以有很大的機會開發人員會打算只用 javascript 直接操作,但這時候你會遇到瀏覽器的「同源政策」您可以利用以下方式設定 CORS
開發程式(使用C#)
在開發前先說明:
- 因為 Azure Search 提供 REST API,所以後端程式非常無聊,就只是用 WebRequest 去叫用而已,沒有什麼太大的可看性 XD
- Azure Search 支援 OData 查詢語法,建議各位先利用 AzureSearchTool 來測試真正要的查詢參數
- 範例程式使用到了 JSON.NET 做 JSON Parse
- 範例程式使用到了 PagedList 做分頁列的產生
public ActionResult Index(string q, int? page) { if (string.IsNullOrWhiteSpace(q)) { return RedirectToAction("index", "Home"); } //設定一頁幾筆 var pagesize = 20; //PagedList 用的分頁數 var pageIndex = page.HasValue ? page.Value < 1 ? 1 : page.Value : 1; //Azure Search 用的Skip數量 var skip = pageIndex * pagesize - pagesize; //搜尋網址 var searchUrl = "https://{服務名稱}.search.windows.net/indexes/demotc/docs?" + "api-version=2015-02-28&" + "search={搜尋字串}&" + "$top={一頁幾筆}&" + "$skip={跳過幾頁}&" + "$count=true&" +//這行是要求提供資料總筆數 "$orderby=LastEditTime desc";//這行是使用 LastEditTime 做排序 var request = WebRequest.Create(searchUrl); //增加 api-key 到 HTTP Header request.Headers.Add("api-key", "你的查詢金鑰可以在網站上的「設定」找到"); var getResponse = (request.GetResponse()).GetResponseStream(); if (getResponse != null) { var encodResponse = new StreamReader(getResponse, System.Text.Encoding.UTF8); //使用 JSON.NET 解析 JSON 字串 var jsonObj = JObject.Parse(encodResponse.ReadToEnd()); var data = new List<SearchViewModel>(); //取得資料總筆數(呼叫時必須有 $count=true才會有此欄位) var totalItemCount = Convert.ToInt32(jsonObj["@odata.count"]); foreach (var item in jsonObj["value"].Children()) { data.Add(new SearchViewModel { Id = Convert.ToInt32(item["Id"]), a = Convert.ToString(item["a"]), b = Convert.ToString(item["b"]), c = Convert.ToString(item["c"]), LastEditTime = Convert.ToDateTime(item["LastEditTime"]), }); } //利用 PagedList 提供的方法把組合好的資料塞進去,這主要只是要在前端利用Html.PagedListPager做分頁列而已 var source = new StaticPagedList<SearchViewModel>(data, pageIndex, pagesize, totalItemCount); return View(source); } return RedirectToAction("index", "Home"); }
前面 View 的部分我想就不用多介紹了,都已經使用ViewModel傳了不會有太大的難度的,如果在使用上有問題的話,就留言討論吧。
回應討論