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,改成其他名字。

(糾正,是templates/search目錄下新建search.html文件)

 

數據+search.html返回渲染后頁面

當haystack全文檢索后會返回數據,現在我們需要一個頁面來接收這些數據,並且在頁面渲染后返回這個頁面給用戶觀看,渲染並返回頁面的工作haystack已經幫我們做了,那么我們現在只需要准備一個頁面容納數據即可。

在templates文件夾下的search文件夾下新建一個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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM