標籤:
自動完成功能一般都伴隨搜尋方塊出現,就是使用者在輸入時協助其自動補全。
比如對成語進行補全,現有如下成語:一心一意,一心二用,一帆風順。
兩種實現方式:
實現方式一:
為每個成語的每個首碼都使用一個集合類型鍵來儲存該首碼對應的成語名,並且為了實現排序,我們使用有序集合,並score都為0,這樣就按元素值的字典序排序。如果想要實現按照詞的熱度排序,需要再建立一個有序集合,存放詞和score,最後把查詢結果和這個集合做交集即可,這樣可以避免更新一個詞的熱度需要更新多個集合的情況,因為一個詞會出現在多個集合中。
做法:
對於上邊的成語就會建立如下形式的有序集合(key : value,...),score都為0:
一 :一心一意,一心二用,一帆風順
一心:一心一意,一心二用
一心一:一心一意
一心二:一心二用
一帆:一帆風順
一帆風:一帆風順
下邊實現產生這些首碼對應的有序集合的代碼
$valueArr = [‘一心一意‘,‘一心二用‘,‘一帆風順‘];//中文字元注意指定編碼,不能用$str[$i]方式擷取中文foreach ($valueArr as $item) { for ($i = 1,$len = mb_strlen($item, "utf-8"); $i < $len; $i++) { $k = $prefix.mb_substr($item,0, $i, "utf-8"); $redis->zadd($k, 0, $item); }}
在通過對應的關鍵字擷取對應的有序集合即可
$search1 = "一";$search2 = "一帆";$ret1 = $redis->zRange($$prefix.$search1, 0, -1);$ret2 = $redis->zRange($$prefix.$search2, 0, -1);
如果需要實現熱度排序,只需要取關鍵字集合和全部成語的熱度集合的交集即可,通過參數指定兩個集合的分數組合方式
$redis->zInter($retZset, [$prefix.$search, $hotZset]); //取交集$ret = $redis->zRevRange($retZset, 0, -1); //倒序
實現方式二:
通過有序集合實現,該方法由redis作者介紹。因為有序集合當元素的score相同時,按照元素的字典序排序,利用這個特性只用一個有序集合就能實現標籤補全。
做法:
把全部成語首碼存入有序集合,再把全部成語後邊加上*號後存入有序集合,分數均為0。
擷取時先取關鍵字在zset中的排名,然後在擷取排名後的N個元素,最後遍曆結果,找出*結尾並且以對應關鍵字開頭的元素即是結果。
先實現存入代碼
//遍曆全部的首碼和成語加入有序集合foreach ($valueArr as $item) { for ($i = 1,$len = mb_strlen($item, "utf-8"); $i <= $len; $i++) { $member = mb_substr($item,0, $i, "utf-8"); if ($i == $len) { $member .= "*"; //非首碼需要在後邊補* } $redis->zadd("auto:zset", 0, $member); }}
再實現擷取代碼
//查詢$search = "一帆";$rank = $redis->zRank("auto:zset", $search);$ret = $redis->zRange("auto:zset", $rank + 1, $rank + 10); //擷取10個$ret = array_filter($ret, function($d){ return ("*" === mb_substr($d, -1)); //此處沒有去掉*,沒有匹配首碼,多出來不匹配條數可以作為推薦});echo "<pre>";print_r($ret);exit;
總結:
方式一需要N個集合儲存,但每個集合資料量較小,方式二隻用一個集合實現,但資料量大,同時實現熱度排序比較困難,這個看具體使用情境和資料量選擇吧。
redis技巧--自動完成功能實現