又是一篇關於 Autocomplete 的文章,不過這次我們來講如何用 Redis 來處理熱門景點的 autocomplete。Funliday 是一個排行程的服務,當然會有使用者在安排時的熱門景點,所以當使用者輸入「北門」的時候,如果在 autocomplete 可以出現下面這些 candidates 的話,對使用者一定更方便。
Name |
---|
北門口肉圓 |
北門綠豆沙 |
北門肉羹 |
即使如此,這些 candidate 也一定有先後順序,所以加上熱門程度 (可以用加入或觀看的次數做為基礎) 之後就會變成下面這樣。
Name | Hot |
---|---|
北門口肉圓 | 79 |
北門綠豆沙 | 84 |
北門肉羹 | 82 |
寫入階段
以前在 用 Redis 來處理 City 的 autocomplete 功能 - 1 的時候有提過,在做 Autocomplete 的時候,我們可以利用 sorted set 來處理字母順序的問題,而 sorted set 在新增元素的時候,其實也可以加分數 (score) 上去,以上面為例,我們可以使用 ZADD
這樣處理。
1 | ZADD ac_poi:北 79 北門口肉圓 |
兩種不同的寫入方式
大家應該有發現到這裡的儲存方式和以前不同,之前在儲存的時候是固定的 key 搭配不同的 member prefix,以及固定為 0 的 score。
1 | # 以前 |
主要是為了可以讓 score 做排序,所以將 prefix 移往 key。雖然 key 看起來會有點奇怪,但這是一個還蠻巧妙的設計。
寫入後的結果
內容排不下,我們就以「北門口」為例,存到 redis 之後會變成下面這樣子。
ac_poi:北 | ac_poi:北門 | ac_poi:北門口 | |||
---|---|---|---|---|---|
score | member | score | member | score | member |
79 | 北門口肉圓 | 79 | 北門口肉圓 | 79 | 北門口肉圓 |
82 | 北門肉羹 | 82 | 北門肉羹 | ||
84 | 北門綠豆沙 | 84 | 北門綠豆沙 |
所以 sorted set 在同一個 key 存入不同 member 之後,會以 score 由小排到大。到這邊為止,算是寫入階段完成。
查詢階段
在搜尋的時候就是仿照 City 那邊的做法,只不過會將原本需要用 ZRANK
將 cursor 定位至特定的 member 再往後用 ZRANGE
取得資料,現在只要直接用 ZRANGE
取得就可以了。雖然 City 也可以用 Lua 來達成 atomic operation,但為了讓系統單純一點,這裡就先不這樣處理了。
1 | # 以前 |
取回來的 results
會變下面這樣子 score 由大排到小,然後再做一些處理,就可以輸出回前端了。
- 84 北門綠豆沙
- 82 北門肉羹
- 79 北門口肉圓
結論
原文章還有提到可以用 ZINCRBY
來更新 score,但 Funliday 的使用情境其實不用到這麼即時,所以我們的做法是每天重新產生新的 autocomplete 資料。
現在這個功能也已經出現在最新版的 App 了,大家可以在首頁點進「住宿」做搜尋,就會看到這個功能囉。至於原本的「景點瀏覽」要再給我們一些時間,會儘快將這個功能上線!