如何為應用程式加入搜尋:從簡單篩選到全文搜尋
要為應用程式加入搜尋,請從最簡單、可行的做法開始:一個搜尋框呼叫伺服器路由,該路由依據查詢字串與資料庫中幾個欄位進行比對以過濾資料列,並加上分類或價格等篩選條件。當這還不夠用時,例如你需要相關性排序、字詞詞幹化,或資料量很大,就改用資料庫的全文搜尋;只有在你需要強大的錯字容忍,或在超大資料上即時出結果時,才考慮代管搜尋引擎。無論選擇哪一種,搜尋都必須遵守權限規則:使用者絕不應找到他們無權看見的紀錄。
「搜尋」對你的應用程式意味著什麼
搜尋涵蓋幾種不同的功能,大多數應用程式都需要不止一種:
- **關鍵字搜尋:**一個文字框,例如「找提到『花園』的刊登」。
- **篩選:**結構化的選項,例如分類、城市、價格範圍、日期、狀態。
- **排序:**最新、最便宜、最相關。
- **自動完成:**在輸入時提供建議。
- 錯字容忍:「restuarant」仍然能找到「restaurant」。
對於目錄、職缺看板或商店來說,篩選通常比文字框更重要。在挑選工具之前,先決定你真正需要哪些功能。
你的選擇
| 做法 | 運作方式 | 適合 | 限制 |
|---|---|---|---|
| 在瀏覽器中過濾 | 載入清單,用 JavaScript 過濾 | 小型公開清單(幾百筆) | 會把所有資料傳給每位訪客;清單大時很慢 |
| 資料庫模式比對 | 伺服器查詢,例如「名稱包含該文字」,不分大小寫 | 小到中型應用程式、管理工具 | 沒有排序;大型資料表可能變慢;沒有詞幹化 |
| 資料庫全文搜尋 | Postgres 為字詞建立索引並排序比對結果 | 大多數內容密集的應用程式 | 除非加上模糊比對,否則不擅長處理錯字 |
| 模糊比對(trigram) | Postgres 比較字元序列的相似度 | 名稱、短欄位、錯字容忍 | 需要擴充功能與索引;需要調校 |
| 代管搜尋引擎 | 獨立服務為你的資料建立索引(Algolia、Meilisearch、Typesense、Elasticsearch 等) | 大型目錄、即時搜尋、強大的錯字容忍 | 另一個需要同步、付費與保護的服務 |
如果你的資料已經存放在 Postgres 中,它內建的全文搜尋是很好的中間方案。它會把文字拆成字詞,將字詞化為詞幹(因此「running」能比對到「run」),忽略常見字,並依比對程度為結果排序。只要有正確的索引,在大型資料表上仍然很快。
提示詞範例
簡單的起步:
在刊登列表頁加入搜尋框和篩選條件。搜尋應不分大小寫,比對標題和描述。篩選條件:分類(下拉選單)、城市(下拉選單)和最高價格。搜尋在伺服器上進行,使用分頁的 API 路由,每頁 20 筆結果。把搜尋與篩選條件保留在網址中,讓結果可以分享,且返回按鈕能正常運作。沒有符合的結果時,顯示友善的空白狀態。
升級為全文搜尋:
把刊登搜尋改為 Postgres 全文搜尋,對標題(權重較高)和描述建立索引。加入適當的索引,依相關性排序結果,並保留現有的篩選與分頁。不要更改任何現有資料。
為名稱加入錯字容忍:
為商家名稱加入模糊比對,讓小錯字也能找到結果。如果可用,請使用 trigram 相似度,並加上索引;若不可用,則退回目前的搜尋方式。
逐步執行
- **列出人們會搜尋什麼。**哪些欄位(標題、描述、標籤、名稱)?哪些篩選條件?如果可以,請詢問幾位真實使用者。
- **從伺服器端開始。**新增一個 API 路由,接收查詢字串與篩選條件,並只回傳一頁結果。不要把所有資料載入瀏覽器。
- **在查詢中套用權限。**如果使用者只能看到自己的紀錄,或只能看到已發布的紀錄,該條件應與搜尋放在同一個查詢中。
- 從第一天就加入分頁,可以是頁碼或「載入更多」。
- 把搜尋狀態放進網址(例如
?q=garden&city=leeds)。這樣連結可以分享,返回按鈕也能正常運作,而且如果你希望搜尋引擎抓取篩選後的頁面,它們也能被爬取。 - **輸入時的搜尋要做防抖動(debounce)。**在停止輸入後等待數百毫秒才送出查詢,避免每按一個鍵就送出一次請求。
- 在知道你要篩選和搜尋哪些欄位後,為它們加上索引。
- 逐步升級:先全文搜尋,再模糊比對,最後才是代管引擎——只有在較簡單的步驟無法滿足真實使用者時才升級。
- 用接近真實的資料測試:幾百或幾千筆資料列、奇怪的字元、空白查詢、非常長的查詢,若你支援其他語言也要測試。
常見錯誤
- **把整個資料表載入瀏覽器。**50 筆資料時沒問題,50,000 筆就會很痛苦,而且會暴露每一筆紀錄,包括使用者不應看見的紀錄。
- **忽略權限的搜尋。**搜尋是常見的資料外洩來源:私人或草稿紀錄會出現在結果中,因為搜尋查詢忘了加上「只限自己的」或「只限已發布的」條件。
- **從原始使用者輸入組合 SQL。**務必使用參數化查詢。AI 建置工具通常會這樣做,但若你看到字串被直接拼接,請仔細檢查。
- 沒有空白狀態。「沒有結果」加上建議(清除篩選條件、檢查拼字),能避免人們離開。
- **沒有索引。**用測試資料時搜尋感覺正常,但隨著真實資料增加就會變慢。
- **直接跳到搜尋服務。**你因此必須維護第二份同步的資料副本,處理刪除,並保護另一組金鑰——而且往往看不出有什麼明顯的好處。
- **忘了手機版。**篩選條件必須能在手機上使用;抽屜式或可收合的面板通常比側邊欄更好。
檢查清單
- 你知道哪些欄位會被搜尋,以及有哪些篩選條件
- 搜尋在伺服器上執行,並回傳分頁結果
- 查詢套用的權限與應用程式其他部分相同
- 使用者輸入以參數傳遞,絕不直接拼接進 SQL
- 搜尋狀態保存在網址中
- 輸入時已做防抖動處理
- 被搜尋與被篩選的欄位已建立索引
- 空白、無結果與錯誤狀態都清楚明確
- 已用接近真實的資料量與雜亂的輸入進行測試
在 Mythex 加入搜尋
在 Mythex 建置的應用程式中,資料通常存放在專案自己的 Postgres 資料庫中,因此你可以從上面的提示詞開始:先做模式比對,等需要排序時再改用 Postgres 全文搜尋。如果你想要代管搜尋引擎,請自備帳戶與金鑰,把它儲存為專案 secret,並請代理人從伺服器端建立索引與查詢,做法如 Integrate any API 所述。
搜尋是許多應用程式的核心:如果想了解搜尋如何融入整個產品,請參閱如何建置目錄網站、如何建置職缺看板和如何建置市集。
常見問題
為應用程式加入搜尋,最簡單的方法是什麼?
如果你的資料已存放在資料庫中,最容易開始的方式是:用伺服器路由,對幾個欄位做不分大小寫的比對,過濾資料列。如果是瀏覽器中已載入的小型清單,直接在瀏覽器中過濾甚至更簡單。
搜尋一定要用 Algolia 或 Elasticsearch 嗎?
通常一開始不需要。Postgres 全文搜尋就能很好地處理文字比對、排序,以及數千到數百萬筆資料。只有在你需要強大的錯字容忍、在大量資料上邊打字邊即時出結果,或進階的相關性調校時,才值得使用代管搜尋引擎。
為什麼我的搜尋找不到打錯字的結果?
基本比對和標準全文搜尋會依照輸入的字詞(或其詞幹)去找,因此拼錯就會找不到。你需要模糊比對,例如 Postgres 的 trigram 相似度,或具備錯字容忍功能的搜尋引擎。
搜尋應該在瀏覽器執行,還是在伺服器執行?
除了小型的公開清單之外,都應在伺服器執行。伺服器端搜尋可以擴展、遵守權限規則,而且不會把整份資料集傳給每一位訪客。