Django之使用haystack+whoosh實現搜尋功能
為了實現專案中的搜尋功能,我們使用的是全文檢索框架haystack+搜尋引擎whoosh+中文分詞包jieba
安裝和配置
安裝所需包
pip install django-haystack pip install whoosh pip install jieba
去settings檔案註冊haystack應用
INSTALLED_APPS = [ 'haystack', # 註冊全文檢索框架 ]
在settings檔案中配置全文檢索框架
# 全文檢索框架的配置 HAYSTACK_CONNECTIONS = { 'default': { # 使用whoosh引擎 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', # 索引檔案路徑 'PATH': os.path.join(BASE_DIR, 'whoosh_index'), } } # 當新增、修改、刪除資料時,自動生成索引 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
索引檔案的生成
要生成索引檔案,首先你要配置,對哪些內容進行索引,比如商品名稱,簡介和詳情;為了配置對資料庫指定內容進行索引,我們要做如下步驟:
配置search_indexes.py檔案
因為在django中資料庫一般都是通過ORM生成的,首先我們在要在資料表對應的應用中建立一個 search_indexes.py 檔案,例如,我現在要檢索商品對應的表就是GoodsSKU表,而表是在goods應用下的,所以我在goods應用下新建 search_indexes.py 檔案,截圖如下:
在 search_indexes.py 檔案中加入以下內容
# 定義索引類 from haystack import indexes # 匯入你的模型類 from goods.models import GoodsSKU # 指定對於某個類的某些資料建立索引 # 索引類名格式:模型類名+Index class GoodsSKUIndex(indexes.SearchIndex, indexes.Indexable): # 索引欄位 use_template=True指定根據表中的哪些欄位建立索引檔案的說明放在一個檔案中 text = indexes.CharField(document=True, use_template=True) def get_model(self): # 返回你的模型類 return GoodsSKU # 建立索引的資料 def index_queryset(self, using=None): return self.get_model().objects.all()
指定要檢索的內容
在templates資料夾下面新建search資料夾,在search資料夾下面新建indexes資料夾,在indexes資料夾下面新建要檢索應用名的資料夾比如goods資料夾,在goods資料夾下面新建 表名_text.txt,表名小寫,所以目前的目錄結構是這樣的 templates/search/indexes/goods/goodssku_text.txt ,截圖如下:
在goodssku_text.txt 檔案中指定你要根據表中的哪些欄位建立索引資料,現在我們要根據商品的名稱,簡介,詳情來建立索引,如下配置
# 指定根據表中的哪些欄位建立索引資料 {{ object.name }} # 根據商品的名稱建立索引 {{ object.desc }} # 根據商品的簡介建立索引 {{ object.goods.detail }} # 根據商品的詳情建立索引
其中的objects可以理解為資料表對應的商品物件。
生成索引檔案
使用pycharm自帶的命令列terminal執行以下命令生成索引檔案:
python manage.py rebuild_index
執行成功後,你可以在專案下看到類似如下索引檔案
使用全文檢索
通過如上的配置,我們的資料索引已經建立了,現在我們要在專案中使用全文檢索。
在需要使用檢索的地方進行 form 表單改造
<form action="/search" method="get"> <input type="text" class="input_text fl" name="q" placeholder="搜尋商品"> <input type="submit" class="input_btn fr" name="" value="搜尋"> </form>
如上所示,其中要注意的是:
- 傳送方式必須使用get;
- 搜尋的input框 name 必須是 q;
配置檢索對應的url
在專案下的urls.py檔案中新增如下url配置
urlpatterns = [ url(r'^search/', include('haystack.urls')), # 全文檢索框架 ]
檢索成功後生成的引數
當haystack自動檢索成功後,會給我們返回三個引數;
- query引數,表示你查詢的引數;
- page引數,當前頁的Page物件,是查詢到的物件的集合,可以通過for迴圈類獲取單個商品,通過 商品.objects.xxx 獲取商品對應的欄位;
- paginator引數,分頁paginator物件。
可以通過如下程式碼測試引數
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 搜尋的關鍵字:{{ query }}<br/> 當前頁的Page物件:{{ page }}<br/> <ul> {% for item in page %} <li>{{ item.object }}</li> {% endfor %} </ul> 分頁paginator物件:{{ paginator }}<br/> </body> </html>templates/indexes/search.html
注意,位置和檔名都是固定的,並且這只是測試檔案,後面使用全文檢索時記得不能使用search.html,改成其他名字。
資料+search.html返回渲染後頁面
當haystack全文檢索後會返回資料,現在我們需要一個頁面來接收這些資料,並且在頁面渲染後返回這個頁面給使用者觀看,渲染並返回頁面的工作haystack已經幫我們做了,那麼我們現在只需要準備一個頁面容納資料即可。
在templates資料夾下的indexes資料夾下新建一個search.html,注意路徑和檔名是固定的,如下圖
利用檢索返回的引數在search.html中定義要渲染出的模板和樣式,我的頁面如下
<div class="breadcrumb"> <a href="#">{{ query }}</a> <span>></span> <a href="#">搜尋結果如下:</a> </div> <div class="main_wrap clearfix"> <ul class="goods_type_list clearfix"> {% for item in page %} <li> <a href="{% url 'goods:detail' item.object.id %}"><img src="{{ item.object.image.url }}"></a> <h4><a href="{% url 'goods:detail' item.object.id %}">{{ item.object.name }}</a></h4> <div class="operate"> <span class="prize">¥{{ item.object.price }}</span> <span class="unit">{{ item.object.price}}/{{ item.object.unite }}</span> <a href="#" class="add_goods" title="加入購物車"></a> </div> </li> {% endfor %} </ul> <div class="pagenation"> {% if page.has_previous %} <a href="/search?q={{ query }}&page={{ page.previous_page_number }}"><上一頁</a> {% endif %} {% for pindex in paginator.page_range %} {% if pindex == page.number %} <a href="/search?q={{ query }}&page={{ pindex }}" class="active">{{ pindex }}</a> {% else %} <a href="/search?q={{ query }}&page={{ pindex }}">{{ pindex }}</a> {% endif %} {% endfor %} {% if page.has_next %} <a href="/search?q={{ query }}&page={{ page.next_page_number }}">下一頁></a> {% endif %} </div> </div>search.html
至此,我們可以在頁面上搜索一下內容,應該是能成功的,但也有可能不會返回任何資料就算name就是你搜索的內容,這是因為我們現在使用的主要還是為英語服務的分詞包,接下來我們要配置使用中文分詞包了。
使用中文分詞包jieba
在前面的配置中我們已經安裝了jieba;
建立 ChineseAnalyzer.py 檔案
進入虛擬環境下的 Lib\site-packages\haystack\backends 目錄下新建 ChineseAnalyzer.py 檔案
目錄如下圖
在檔案中新增如下內容
import jieba from whoosh.analysis import Tokenizer, Token class ChineseTokenizer(Tokenizer): def __call__(self, value, positions=False, chars=False, keeporiginal=False, removestops=True, start_pos=0, start_char=0, mode='', **kwargs): t = Token(positions, chars, removestops=removestops, mode=mode, **kwargs) seglist = jieba.cut(value, cut_all=True) for w in seglist: t.original = t.text = w t.boost = 1.0 if positions: t.pos = start_pos + value.find(w) if chars: t.startchar = start_char + value.find(w) t.endchar = start_char + value.find(w) + len(w) yield t def ChineseAnalyzer(): return ChineseTokenizer()ChineseAnalyzer.py
編寫haystack可使用的 whoosh_cn_backend.py 檔案
直接在 虛擬環境下的 Lib\site-packages\haystack\backends 目錄下複製一份 whoosh_backend.py 檔案 並且重新命名複製檔案為 whoosh_cn_backend.py;
在 whoosh_cn_backend.py 中匯入我們編寫的 ChineseAnalyzer 類
from .ChineseAnalyzer import ChineseAnalyzer
更改haystack使用的分詞包為 jieba 編寫的中文分詞類,大概在第160行左右
# schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer(), field_boost=field_class.boost, sortable=True) schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True)
配置whoosh引擎使用 whoosh_cn_backend.py
在settings檔案中更改原來的配置如下
# 全文檢索框架的配置 HAYSTACK_CONNECTIONS = { 'default': { # 使用whoosh引擎 # 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine', # 索引檔案路徑 'PATH': os.path.join(BASE_DIR, 'whoosh_index'), } } # 當新增、修改、刪除資料時,自動生成索引 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
重新生成索引檔案
python manage.py rebuild_index
至此,就可以放心的使用搜索功能了,如圖,搜尋成功的顯示頁面
可以通過如下配置控制每個分頁顯示的搜尋出來物件的數目
# 指定搜尋結果每頁顯示的條數 HAYSTACK_SEARCH_RESULTS_PER_PAGE = 1
&n