1. 程式人生 > >利用elasticsearch實現搜尋引擎

利用elasticsearch實現搜尋引擎

  ElasticSearch是一個基於Lucene的搜尋伺服器。它提供了一個分散式多使用者能力的全文搜尋引擎,基於RESTful web介面。Elasticsearch是用Java開發的,並作為Apache許可條款下的開放原始碼釋出,是第二最流行的企業搜尋引擎。設計用於雲端計算中,能夠達到實時搜尋,穩定,可靠,快速,安裝使用方便。本篇是在windows平臺下的運用

起步

1. 安裝jdk

2. 安裝elasticsearch-rtf

解壓後,在bin目錄下開啟命令列工具,輸入elasticsearch.bat執行指令碼,開啟瀏覽器,在位址列輸入http://127.0.0.1:9200

, 如果返回資料則表示執行成功

3.安裝elasticsearch-head

  這是(搜尋引擎)的視覺化管理工具,安裝要用到node.js的npm 外掛管理器,所以要先安裝node.js的npm 外掛管理器。

(1)安裝完後後,開啟命令列工具

    執行命令 npm

  npm命令是node.js的npm外掛管理器,也就是下載外掛和安裝外掛的管理器,國外映象很慢可能會掉線,我們使用淘寶的npm映象cnpm

    執行命令:npm install -g cnpm --registry=https://registry.npm.taobao.org  

啟用淘寶的npm映象cnpm,注意:啟用後當我們要輸入npm命令時,就需要輸入cnpm

(2)(搜尋引擎)的視覺化管理工具elasticsearch-head的安裝

    下載後解壓到指定目錄

(3)cd進入到解壓的elasticsearch-head目錄,安裝elasticsearch-head的依賴包

    執行命令:cnpm install

    
(4)啟動elasticsearch-head(搜尋引擎)的視覺化管理工具,訪問http://localhost:9100/,就可以看到(搜尋引擎)的視覺化管理工具。

    執行命令:cnpm run start  

點選連線會發現是灰色的未連線狀態,這是因為elsaticsearch的安全策略,在5.0之後的版本不允許第三方外掛直接連線,需要修改配置檔案,在config資料夾下字尾為yml的檔案中新增以下配置:

    http.cors.enabled: true
    http.cors.allow-origin: "*"

重啟服務,點選連線,即可成功

應用

  elasticsearch的詳細內容還請參閱相關文件,接下來會直接貼上我在使用elasticsearch來實現搜尋引擎時的步驟和一些注意事項。搜尋引擎是為了資料而存在的,所以你需要準備一個數據源,因此我用scrapy實現了一個簡單的爬蟲。
  在使用前,為了準備資料要先啟動爬蟲將資料提交到elasticsearch,如感興趣具體可前往https://github.com/xinyan818/SimpleSearch-TST

1.宣告doc_type

  doc_type在elasticsearch中類似關係型資料庫中的資料表,我們需要實現它。

# 資料型別
from elasticsearch_dsl import DocType, Completion, Keyword, Text, Boolean, Integer, Date
# 引入連結函式
from elasticsearch_dsl.connections import connections
# 引入elasticsearch中的分析器
from elasticsearch_dsl.analysis import CustomAnalyzer

# 建立Es連結
connections.create_connection(hosts=["127.0.0.1"])


# 自定義分詞器
class MyAnalyzer(CustomAnalyzer):

    def get_analysis_definition(self):
        return {}

# 建立分析器物件
# 忽略大小寫的篩選器


ik_analyzer = MyAnalyzer('ik_max_word', filter=['lowercase'])


class NewsType(DocType):

    # 搜尋建議欄位
    # Completion 用來做搜尋建議的型別
    # 不能直接指定分詞器名,需要指定一個自定義分詞器
    suggest = Completion(analyzer=ik_analyzer)

    # 分詞
    title = Text(analyzer="ik_max_word")
    category = Text()
    f_url = Text()
    intro = Text(analyzer="ik_max_word")
    source = Text()
    time = Date()

    # Meta
    class Meta:
        # 索引名稱
        index = 'news'
        doc_type = 'fashion'


if __name__ == '__main__':
    NewsType.init()

  執行這個python檔案即可在服務中宣告一個名為news的index,和在其之下名為fashion的type,

2.搜尋結果的檢視函式

#搜尋結果頁面
def result(request):
    if request.method == 'GET':
        # 取出關鍵詞 搜尋型別 頁碼
        keyword = request.GET.get('kw', None)
        s_type = request.GET.get('s_type', 'blog')
        page_num = request.GET.get('pn', 1)
        # 沒有關鍵詞,定向到首頁
        if not keyword:
            return redirect('/')
        # 判斷搜尋型別
        # 搜尋
        if s_type == 'news':
            # 1. 搜尋的索引
            index = 'news'
            doc_type = 'fashion'
            fields = ['title', 'intro']
            start = datetime.now()
            rs = es.search(
                index=index,
                doc_type=doc_type,
                body={
                    'query': {
                        'multi_match': {
                            'query': keyword,
                            'fields': fields
                        }
                    },
                    'from': (int(page_num)-1)*10,
                    'size': 10,
                    'highlight': {
                        'pre_tags': ['<span class="KeyWord">'],
                        'post_tags': ['</span>'],
                        'fields': {
                            'title': {},
                            'intro': {}
                        }
                    }
                }
            )
            # 搜尋花費時間
            # total_seconds() 統計秒數
            use_time = (datetime.now() - start).total_seconds()
            hits_list = []
            for hit in rs['hits']['hits']:
                try:
                    h_dic = {}
                    # 判斷highlight中有沒有title
                    if 'title' in hit['highlight'].keys():
                        h_dic['title'] = hit['highlight']['title'][0]
                    else:
                        h_dic['title'] = hit['_source']['title']

                    # 再判斷intro
                    if 'intro' in hit['highlight'].keys():
                        intro_list = hit['highlight']['intro']
                        intro_list.reverse()
                        h_dic['content'] = ''.join(intro_list)
                    else:
                        h_dic['content'] = hit['_source']['intro']

                    # 詳情地址
                    h_dic['detail_url'] = hit['_source']['f_url']
                except:
                    continue
                hits_list.append(h_dic)
            navs = NAVS
            # 計算頁碼
            total = rs['hits']['total']

            page_nums = int(math.ceil(total/10))

            page_num = int(page_num)
            if page_num < 6:
                if page_nums <= 10:
                    pages = range(1, page_nums+1)
                else:
                    pages = range(1, 11)
            elif (page_num >= 6) and (page_num <= page_nums - 5):


                pages = range(page_num - 5, page_num + 5)
            else:
                if page_nums <= 10:
                    pages = range(1, page_nums+1)
                else:
                    pages = range(page_nums-9, page_nums + 1)

            data = {
                'navs': navs,
                'search_type': s_type,
                'hits_list': hits_list,
                'kw': keyword,
                'pages': pages,
                'page_nums': page_nums,
                'pn': page_num,
                'total': total,
                'use_time': use_time
            }
            return render(request, 'result.html', data)