對於一個網站來講,不管是商城網站仍是門戶網站,搜索框都是有一個比較重要的地位,它的存在能夠說是javascript
爲了讓用戶更快、更方便的去找到本身想要的東西。對於常常逛這個網站的用戶,固然也會想知道在這裏比較「火」html
的東西是什麼,這個時候咱們搜索框上的熱詞就起做用了。其實我以爲這一塊的完善會對這個網站帶來許多益處。java
可能如今比較廣泛的作法是把這些相應的信息存到咱們的關係型數據庫中,如sql server 和 oracle。方便起見jquery
的話,可能每搜索一次就往表裏插一次數據,用的時候要先統計數據,統計完後再排序,最後才展現。這種狀況下,ajax
若是搜索量很大的話,表的膨脹速度就會很是快,若是sql沒寫好,查詢的時候估計會。。相比Redis,同等條件下,redis
Redis的速率確定是會較優,畢竟是從內存中拿出來的。sql
案例用到的一些相關技術和說明:數據庫
技術 | 說明 |
.NET Core | 網站嘛,你懂的。有事沒事用Core寫寫Demo,省得跟不上發展的腳步。 |
Redis | 存儲搜索詞,用了主從的模式,主寫從讀 |
Jquery-ui | 主要是用了裏面的autocomplete |
開始正題以前,咱們要肯定用Redis中的那種數據結構,五種之中比較合適的應該是SortedSet,咱們能夠用成員來json
做爲搜索詞,成員分數來做爲搜索詞的搜索次數,這樣就能夠很方便的來操做相關的數據了。api
下面開始正題:
咱們在開始的時候須要初始化一下數據。這裏就直接在第一次運行的時候初始化。用上流水線的技術,速度仍是
很可觀的。初始化了70個搜索關鍵詞(NBA球星),而後用隨機數做爲關鍵字的下標,去隨機給這個關鍵字加1分。這
個分數就是這個關鍵字被搜索的次數。下面來看看初始化的相關代碼:
1 public IActionResult Index() 2 { 3 //keys 4 IList<string> keys = new List<string>() 5 { 6 "kobe","johnson","jabbar","west","o'neal","baylor","mccann","worthy","gasol","chamberlain", 7 "fisher","odom","bynum","horry","rambis","riley","clarkson","Williams","young","Russell", 8 "ingram","randle","nance","brown","deng","yi","ariza","artest","walton","vujacic", 9 "james","paul","curry","park","yao","kevin","wade","rose","popovich","leonard", 10 "aldridge","ginobili","duncan","lavine","rubio","garnett","wiggins","westbrook","durant","ibaka", 11 "nowitzki","pierce","crawford","love","smith","iguodala","barnes","green","thompson","harden", 12 "lillard","mccollum","lin","jackson","nash","stoudemire","whiteside","dragic","Howard","batum" 13 }; 14 15 //init 16 Random random = new Random(); 17 var tran = _redis.GetTransaction(); 18 for (int i = 0; i < 1000000; i++) 19 { 20 tran.SortedSetIncrementAsync(_searchKey, keys[random.Next(0, 70)], 1); 21 } 22 tran.ExecuteAsync(); 23 24 return View(); 25 }
這裏是在加載這個頁面的時候就把這些熱搜詞存進Redis中,這樣咱們纔能有數據來演示啊。這裏還用到了一個
非事務型的流水線。就是把要操做的指令存放到一個隊列中,最後把這個隊列扔到服務端去執行,這樣就有效的減小
了沒必要要的網絡傳輸,同時也提升了執行速度。
好了,初始數據有了,下面要作的就是用戶在搜索的時候,根據用戶的輸入去匹配搜索次數多的關鍵字,展現最
Hot的10個,固然這個展現的個數是隨咱們定的,最後能夠考慮把這個放到咱們的配置文件中去,甚至是放到數據庫中,
爲的是靈活和方便維護。下面是咱們在後臺的處理邏輯:
1 public IActionResult GetHotKey(string key="") 2 { 3 if (string.IsNullOrEmpty(key)) 4 {//default 5 var res = _redis.ZRevRange(_searchKey, 0, 9); 6 var list = (from i in res select i.ToString()); 7 return Json(list); 8 } 9 else 10 {//by user input 11 var res = _redis.ZRevRange(_searchKey, 0, -1); 12 var list = (from i in res select i.ToString()).Where(x => x.Contains(key)).Take(10).ToList(); 13 return Json(list); 14 } 15 }
對於查詢的處理是很是的簡單的,用戶不當心輸入空格的時候就展現最熱的10個關鍵詞,若是用戶有輸入的話,就把
關鍵詞中包含用戶輸入的展現出來。那麼咱們在頁面上要作些什麼呢?下面就是咱們演示用的搜索框。
1 <div class="row"> 2 <div class="col-md-6 col-md-offset-4" style="padding-top:50px;"> 3 <input id="key" name="key" placeholder="search" class="form-control col-md-4"> 4 <button class="btn btn-primary" type="button" id="searchSubmit">Search</button> 5 <div id="result"></div> 6 </div> 7 </div>
相應的js是寫到 scripts 這個section中的,js的話是比較簡單的就是用ajax去請求咱們要展現的數據。更多的應該是
jquery-ui的api問題,你們也能夠換用本身比較熟悉的組件,觸類旁通便可。下面是autocomplete的api ,若是有須要可
以去看一下。
1 @section scripts{ 2 <script type="text/javascript"> 3 $(function () { 4 //show hot keyword 5 $("#key").autocomplete({ 6 source: function (request, response) { 7 $.ajax({ 8 url: "@Url.Action("GetHotKey", "Auto")", 9 dataType: "json", 10 data: { 11 key: request.term 12 }, 13 success: function (data) { 14 response(data); 15 } 16 }); 17 }, 18 }); 19 </script> 20 }
那麼用戶點擊了搜索以後咱們要作些什麼處理呢?不管是新的關鍵字仍是已有的關鍵字,咱們都是要作處理的,固然redis
中zincrby命令來處理這個是十分合適的,存在的就把分數加1,不存在就建立一個分數爲1的成員。下面是搜索時的後臺邏輯處理:
1 [HttpPost] 2 public IActionResult SetHotKey(string key) 3 { 4 if (!string.IsNullOrWhiteSpace(key)) 5 { 6 _redis.ZIncrby(_searchKey,key); 7 //other 8 //... 9 return Json(new { code = "000", msg = "OK" }); 10 } 11 else 12 { 13 return Json(new { code = "999", msg = "keyword can not be empty!" }); 14 } 15 }
限制了用戶不能搜索空關鍵字,在把這個關鍵字存儲或者分數加一以後,就是展現咱們的搜索的結果。這個搜索的結果通常
是從solr等全文檢索的地方查出來的,不是咱們講的重點,因此就忽略了。而後咱們還要加一段js去處理咱們搜索的時候應該作的
操做。固然,都是些比較簡單的操做。
1 //search 2 $("#searchSubmit").click(function () { 3 $.ajax({ 4 url: "@Url.Action("SetHotKey", "Auto")", 5 dataType: "json", 6 type: "POST", 7 data: { key: $("#key").val() }, 8 success: function (data) { 9 if (data.code == "000") { 10 $("<p>search successful!</p>").appendTo("#result"); 11 } else { 12 $("<p>"+data.msg+"</p>").appendTo("#result"); 13 } 14 } 15 }); 16 });
在演示的時候,咱們搜索了「我愛你」和「我不信」,在Redis的客戶端咱們找出搜索次數最少的6個,而後就能夠看到咱們那兩
個關鍵字最的分數都是1。肯定是剛插入的數據。
到這裏,咱們作的這個熱搜詞能夠說是大功告成了。固然這能夠說是最最最簡單的一個雛形。咱們還能夠適當的添加一些
東西讓這個功能變得更加完善。好比我能夠在搜索展現的時候顯示一下搜索的次數等。
最後是完整的控制器和頁面代碼:
1 using AutoCompleteDemo.Common; 2 using Microsoft.AspNetCore.Mvc; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 7 namespace AutoCompleteDemo.Controllers 8 { 9 public class AutoController : Controller 10 { 11 private readonly IRedis _redis; 12 private readonly string _searchKey = "search"; 13 public AutoController(IRedis redis) 14 { 15 _redis = redis; 16 } 17 18 public IActionResult Index() 19 { 20 //keys 21 IList<string> keys = new List<string>() 22 { 23 "kobe","johnson","jabbar","west","o'neal","baylor","mccann","worthy","gasol","chamberlain", 24 "fisher","odom","bynum","horry","rambis","riley","clarkson","Williams","young","Russell", 25 "ingram","randle","nance","brown","deng","yi","ariza","artest","walton","vujacic", 26 "james","paul","curry","park","yao","kevin","wade","rose","popovich","leonard", 27 "aldridge","ginobili","duncan","lavine","rubio","garnett","wiggins","westbrook","durant","ibaka", 28 "nowitzki","pierce","crawford","love","smith","iguodala","barnes","green","thompson","harden", 29 "lillard","mccollum","lin","jackson","nash","stoudemire","whiteside","dragic","Howard","batum" 30 }; 31 32 //init 33 Random random = new Random(); 34 var tran = _redis.GetTransaction(); 35 for (int i = 0; i < 2000000; i++) 36 { 37 tran.SortedSetIncrementAsync(_searchKey, keys[random.Next(0, 70)], 1); 38 } 39 tran.ExecuteAsync(); 40 41 return View(); 42 } 43 44 public IActionResult GetHotKey(string key="") 45 { 46 if (string.IsNullOrEmpty(key)) 47 {//default 48 var res = _redis.ZRevRange(_searchKey, 0, 9); 49 var list = (from i in res select i.ToString()); 50 return Json(list); 51 } 52 else 53 {//by user input 54 var res = _redis.ZRevRange(_searchKey, 0, -1); 55 var list = (from i in res select i.ToString()).Where(x => x.Contains(key)).Take(10).ToList(); 56 return Json(list); 57 } 58 } 59 60 [HttpPost] 61 public IActionResult SetHotKey(string key) 62 { 63 if (!string.IsNullOrWhiteSpace(key)) 64 { 65 _redis.ZIncrby(_searchKey,key); 66 //other 67 //... 68 return Json(new { code = "000", msg = "OK" }); 69 } 70 else 71 { 72 return Json(new { code = "999", msg = "keyword can not be empty!" }); 73 } 74 } 75 } 76 }
1 @{ 2 ViewData["Title"] = "Auto Complete"; 3 } 4 <div class="row"> 5 <div class="col-md-6 col-md-offset-4" style="padding-top:50px;"> 6 <input id="key" name="key" placeholder="search" class="form-control col-md-4"> 7 <button class="btn btn-primary" type="button" id="searchSubmit">Search</button> 8 <div id="result"></div> 9 </div> 10 </div> 11 @section scripts{ 12 <script type="text/javascript"> 13 $(function () { 14 //show hot keyword 15 $("#key").autocomplete({ 16 source: function (request, response) { 17 $.ajax({ 18 url: "@Url.Action("GetHotKey", "Auto")", 19 dataType: "json", 20 data: { 21 key: request.term 22 }, 23 success: function (data) { 24 response(data); 25 } 26 }); 27 }, 28 }); 29 30 //search 31 $("#searchSubmit").click(function () { 32 $.ajax({ 33 url: "@Url.Action("SetHotKey", "Auto")", 34 dataType: "json", 35 type: "POST", 36 data: { key: $("#key").val() }, 37 success: function (data) { 38 if (data.code == "000") { 39 $("<p>search successful!</p>").appendTo("#result"); 40 } else { 41 $("<p>"+data.msg+"</p>").appendTo("#result"); 42 } 43 } 44 }); 45 }); 46 }); 47 </script> 48 }