Django整合搜尋引擎Elasticserach

Simon_Zhou發表於2019-06-04

1.背景

當使用者在搜尋框輸入關鍵字後,我們要為使用者提供相關的搜尋結果。可以選擇使用模糊查詢like關鍵字實現,但是 like 關鍵字的效率極低。查詢需要在多個欄位中進行,使用 like 關鍵字也不方便,另外分詞的效果也不理想。

全文檢索方案

  • 全文檢索即在指定的任意欄位中進行檢索查詢。

  • 全文檢索方案需要配合搜尋引擎來實現。

搜尋引擎原理

  • 搜尋引擎進行全文檢索時,會對資料庫中的資料進行一遍預處理,單獨建立起一份索引結構資料

  • 索引結構資料類似字典的索引檢索頁,裡面包含了關鍵詞與詞條的對應關係,並記錄詞條的位置。

  • 搜尋引擎進行全文檢索時,將關鍵字在索引資料中進行快速對比查詢,進而找到資料的真實儲存位置

2.Elasticsearch介紹

實現全文檢索的搜尋引擎,首選的是Elasticsearch

  • Elasticsearch 是用 Java 實現的,開源的搜尋引擎。

  • 它可以快速地儲存、搜尋和分析海量資料。維基百科、Stack Overflow、Github等都採用它。

  • Elasticsearch 的底層是開源庫 Lucene。但是,沒法直接使用 Lucene,必須自己寫程式碼去呼叫它的介面。

分詞說明

  • 搜尋引擎在對資料構建索引時,需要進行分詞處理。

  • 分詞是指將一句話拆解成多個單字,這些字或詞便是這句話的關鍵詞。

  • Elasticsearch 不支援對中文進行分詞建立索引,需要配合擴充套件elasticsearch-analysis-ik來實現中文分詞處理。

3.整合Elasticsearch

3.1. Haystack介紹和安裝配置

  • Haystack 是在Django中對接搜尋引擎的框架,搭建了使用者和搜尋引擎之間的溝通橋樑。

    • 我們在Django中可以通過使用 Haystack 來呼叫 Elasticsearch 搜尋引擎。

  • Haystack 可以在不修改程式碼的情況下使用不同的搜尋後端(比如 ElasticsearchWhooshSolr等等)。

Haystack安裝

$ pip install django-haystack
$ pip install elasticsearch==2.4.1複製程式碼

Haystack註冊應用和路由

django的配置檔案中註冊。

INSTALLED_APPS = [    'haystack', # 全文檢索註冊]​複製程式碼

在總路由中新建haystack的路由。

urlpatterns = [url(r'^search/', include('haystack.urls')),]複製程式碼

Haystack配置

  • 在配置檔案中配置Haystack為搜尋引擎後端

# Haystack
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://192.168.103.158:9200/', # Elasticsearch伺服器ip地址,埠號固定為9200
        'INDEX_NAME': 'serach_mall', # Elasticsearch建立的索引庫的名稱
    },
}

# 當新增、修改、刪除資料時,自動生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# 搜尋的每頁大小
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 3複製程式碼

HAYSTACK_SIGNAL_PROCESSOR 配置項保證了在Django執行起來後,有新的資料產生時,Haystack仍然可以讓Elasticsearch實時生成新資料的索引。

3.2 Haystack建立資料索引

1.建立索引類
  • 通過建立索引類,來指明讓搜尋引擎對哪些欄位建立索引,也就是可以通過哪些欄位的關鍵字來檢索資料。

  • 本專案中對模型類SKU資訊進行全文檢索,所以在該模型類的應用(goods)中新建search_indexes.py檔案,用於存放索引類。索引類必須繼承haystack.indexes.SearchIndexhaystack.indexes.Indexable.

from haystack import indexes

from .models import SKU


class SKUIndex(indexes.SearchIndex, indexes.Indexable):
    """SKU索引資料模型類"""
    text = indexes.CharField(document=True, use_template=True)

    def get_model(self):
        """返回建立索引的模型類"""
        return SKU

    def index_queryset(self, using=None):
        """返回要建立索引的資料查詢集"""
        return self.get_model().objects.filter(is_launched=True) 複製程式碼
  • 索引類SKUIndex說明:

    • SKUIndex建立的欄位,都可以藉助HaystackElasticsearch搜尋引擎查詢。

    • 其中text欄位我們宣告為document=True,表名該欄位是主要進行關鍵字查詢的欄位。

    • text欄位的索引值可以由多個資料庫模型類欄位組成,具體由哪些模型類欄位組成,我們用use_template=True表示後續通過模板來指明。

2.建立text欄位索引值模板檔案

  • 在專案templates目錄中建立text欄位使用的模板檔案

  • 具體在templates/search/indexes/goods/sku_text.txt檔案中定義,其中goods為應用名,sku_text.txt中的sku為模型類小寫。

{{ object.id }}
{{ object.name }}
{{ object.caption }}複製程式碼
  • 模板檔案說明:當將關鍵詞通過text引數名傳遞時

    • 此模板指明SKU的idnamecaption作為text欄位的索引值來進行關鍵字索引查詢。

3.手動生成初始索引

$ python manage.py rebuild_index複製程式碼

第一次需要生成索引需要執行上述命令,後續會自動生成索引。

3.3 全文檢索測試

準備測試表單

  • 請求方法:GET

  • 請求地址:/search/

  • 請求引數:q

<div class="search_wrap fl">
    <form method="get" action="/search/" class="search_con">
        <input type="text" class="input_text fl" name="q" placeholder="搜尋商品">
        <input type="submit" class="input_btn fr" name="" value="搜尋">
    </form>
    ...
    ...
</div>複製程式碼

然後在templates/search/目錄下新建search.html接收和渲染全文檢索的結果.

3.4 渲染搜尋結果

Haystack返回的資料包括:

  • query:搜尋關鍵字

  • paginator:分頁paginator物件

  • page:當前頁的page物件(遍歷page中的物件,可以得到result物件)

  • result.objects: 當前遍歷出來的SKU物件。

<div class="main_wrap clearfix">
    <div class=" clearfix">
        <ul class="goods_type_list clearfix">
            {% for result in page %}
            <li>
                {# object取得才是sku物件 #}
                <a href="/detail/{{ result.object.id }}/"><img src="{{ result.object.default_image.url }}"></a>																
                <h4><a href="/detail/{{ result.object.id }}/">{{ result.object.name }}</a></h4>
                <div class="operate">
                    <span class="price">¥{{ result.object.price }}</span>
                    <span>{{ result.object.comments }}評價</span>
                </div>
            </li>
            {% else %}
                <p>沒有找到您要查詢的商品。</p>
            {% endfor %}
        </ul>
        <div class="pagenation">
            <div id="pagination" class="page"></div>
        </div>
    </div>
</div>複製程式碼

這裡Elasticsearch替我們把django中的檢視函式寫了。

搜尋頁分頁器

<div class="main_wrap clearfix">
    <div class=" clearfix">
        ......
        <div class="pagenation">
            <div id="pagination" class="page"></div>
        </div>
    </div>
</div>

<script type="text/javascript">
    $(function () {
        $('#pagination').pagination({
            currentPage: {{ page.number }},
            totalPage: {{ paginator.num_pages }},
            callback:function (current) {
                window.location.href = '/search/?q={{ query }}&page=' + current;
            }
        })
    });
</script>複製程式碼

這裡使用的jquery.pagination.js接收要渲染的資料,當然也可以使用其他框架的分頁器或自定義的來接收。


相關文章