自動完成功能通常都伴隨搜索框出現,就是用戶在輸入時幫助其自動補全。redis
好比對成語進行補全,現有以下成語:一心一意,一心二用,一路順風。數組
兩種實現方式:編碼
實現方式一:spa
爲每一個成語的每一個前綴都使用一個集合類型鍵來存儲該前綴對應的成語名,而且爲了實現排序,咱們使用有序集合,並score都爲0,這樣就按元素值的字典序排序。若是想要實現按照詞的熱度排序,須要再建立一個有序集合,存放詞和score,最後把查詢結果和這個集合作交集便可,這樣能夠避免更新一個詞的熱度須要更新多個集合的狀況,由於一個詞會出如今多個集合中。code
作法:blog
對於上邊的成語就會建立以下形式的有序集合(key : value,...),score都爲0:排序
一 :一心一意,一心二用,一路順風utf-8
一心:一心一意,一心二用it
一心一:一心一意io
一心二:一心二用
一帆:一路順風
一帆風:一路順風
下邊實現生成這些前綴對應的有序集合的代碼
$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個集合存儲,但每一個集合數據量較小,方式二隻用一個集合實現,但數據量大,同時實現熱度排序比較困難,這個看具體使用場景和數據量選擇吧。