搜尋¶
Web 應用程式常見的工作是根據使用者輸入,在資料庫中搜尋資料。在簡單的情況下,這可能是依類別篩選物件列表。更複雜的使用案例可能需要權重、分類、醒目提示、多種語言等等的搜尋。本文檔說明一些可能的使用案例以及您可以使用的工具。
我們將參考 建立查詢 中使用的相同模型。
使用案例¶
標準文字查詢¶
文字型欄位具有多種比對操作。例如,您可能希望允許像這樣查詢作者
>>> Author.objects.filter(name__contains="Terry")
[<Author: Terry Gilliam>, <Author: Terry Jones>]
這是一個非常脆弱的解決方案,因為它要求使用者知道作者姓名的確切子字串。更好的方法可能是區分大小寫的比對(icontains
),但這只稍微好一點。
資料庫更進階的比對函數¶
如果您使用 PostgreSQL,Django 提供一系列資料庫特定的工具,讓您可以利用更複雜的查詢選項。其他資料庫有不同的工具選擇,可能透過外掛程式或使用者定義函數提供。Django 目前不包含任何對它們的支援。我們將使用 PostgreSQL 的一些範例來示範資料庫可能具有的功能類型。
在其他資料庫中搜尋
django.contrib.postgres
提供的所有搜尋工具都完全建構在公開 API 上,例如自訂查詢和資料庫函數。根據您的資料庫,您應該能夠建構查詢以允許類似的 API。如果有些特定的事情無法透過這種方式達成,請開啟一張問題單。
在上面的範例中,我們確定區分大小寫的查詢會更有用。處理非英文名稱時,進一步的改進是使用unaccented comparison
>>> Author.objects.filter(name__unaccent__icontains="Helen")
[<Author: Helen Mirren>, <Author: Helena Bonham Carter>, <Author: Hélène Joy>]
這顯示了另一個問題,我們比對的是名稱的不同拼寫。在這種情況下,我們有一個不對稱的情況 - 搜尋 Helen
會找到 Helena
或 Hélène
,但反之則不然。另一個選項是使用 trigram_similar
比對,它比較字母序列。
例如
>>> Author.objects.filter(name__unaccent__lower__trigram_similar="Hélène")
[<Author: Helen Mirren>, <Author: Hélène Joy>]
現在我們有一個不同的問題 - 更長的名稱「Helena Bonham Carter」沒有顯示,因為它更長。Trigram 搜尋會考慮所有三個字母的組合,並比較搜尋字串和來源字串中出現多少個組合。對於較長的名稱,有更多組合沒有出現在來源字串中,因此不再被認為是密切的比對。
此處比對函數的正確選擇取決於您的特定資料集,例如使用的語言和搜尋的文字類型。我們看到的所有範例都是針對短字串,使用者可能會輸入與來源資料接近(透過不同的定義)的內容。
基於文件的搜尋¶
當您開始考慮大量的文字區塊時,標準資料庫操作就不再是有效的方法。上面的範例可以被認為是對字元串的操作,而全文搜尋則著眼於實際的單字。根據所使用的系統,它可能會使用以下一些想法
忽略「停用字」,例如「a」、「the」、「and」。
詞幹還原,使「pony」和「ponies」被認為相似。
根據不同的標準(例如它們在文字中出現的頻率,或欄位的重要性,例如標題或關鍵字)來加權單字。
有許多替代方案可使用搜尋軟體,其中一些最突出的是 Elastic 和 Solr。這些是完整的基於文件的搜尋解決方案。若要將它們與 Django 模型中的資料一起使用,您需要一個層,將您的資料轉換為文字文件,包括返回資料庫 ID 的參照。當使用引擎搜尋傳回特定文件時,您可以接著在資料庫中查找它。有各種旨在協助此過程的第三方函式庫。
PostgreSQL 支援¶
PostgreSQL 有其內建的全文搜尋實作。雖然不像其他一些搜尋引擎那麼強大,但它的優點是位於您的資料庫內,因此可以輕鬆地與其他關聯式查詢(例如分類)結合。
django.contrib.postgres
模組提供了一些輔助函數來進行這些查詢。例如,查詢可能會選取所有提到「cheese」的部落格文章
>>> Entry.objects.filter(body_text__search="cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
您也可以根據欄位組合和相關模型進行篩選
>>> Entry.objects.annotate(
... search=SearchVector("blog__tagline", "body_text"),
... ).filter(search="cheese")
[
<Entry: Cheese on Toast recipes>,
<Entry: Pizza Recipes>,
<Entry: Dairy farming in Argentina>,
]
有關完整詳細資訊,請參閱 contrib.postgres
全文搜尋文件。