coreseek使用記錄

項目需要用到全文檢索。
之前接觸的都是java中lucene + 分詞庫的解決方案?,F(xiàn)在公司使用的都是coreseek,且項目使用mongodb作為數(shù)據(jù)庫,團(tuán)長讓我研究一下解決的方案,所以記錄一下學(xué)習(xí)與使用過程。

Coreseek

Coreseek是一個開源的中文全文檢索引擎,基于Sphinx研發(fā)并獨(dú)立發(fā)布。在Sphinx在基礎(chǔ)上增加了LibMMSeg中文分詞包,實現(xiàn)了對中文的分詞與檢索。

Sphinx是由俄羅斯人開發(fā)的高性能全文檢索引擎。
全文檢索引擎本身的原理是類似的,網(wǎng)上有很多文章介紹的挺好,這里直接引用一下:

Sphinx的一大特點是與mysql數(shù)據(jù)庫結(jié)合良好,只需在配置文件中寫好SQL查詢語句就能定義索引數(shù)據(jù)庫源。
但此次項目使用mongodbSphinx不能直接支持。

配置索引數(shù)據(jù)源

按照官方手冊配置索引數(shù)據(jù)源,例如一個mysql數(shù)據(jù)源的示例配置:

#源定義
source mysql
{
    type                = mysql    #表示使用mysql數(shù)據(jù)源

    sql_host                = localhost    #表示數(shù)據(jù)庫服務(wù)器的鏈接地址
    sql_user                = root          #表示數(shù)據(jù)庫的用戶名
    sql_pass                = 123456      #表示數(shù)據(jù)庫的密碼
    sql_db              = test          #表示數(shù)據(jù)庫的名稱
    sql_port                = 3306         #表示數(shù)據(jù)庫的端口
    sql_query_pre           = SET NAMES utf8

    #從數(shù)據(jù)庫之中讀取數(shù)據(jù)的SQL語句設(shè)置
    sql_query               = SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content FROM documents
                                        #sql_query第一列id需為整數(shù),且被系統(tǒng)使用,無需再設(shè)置sql_attr_uint

    #使用sql_attr設(shè)置的字段,只能作為屬性,使用SphinxClient::SetFilter()進(jìn)行過濾;未被設(shè)置的字段,自動作為全文檢索的字段,使用SphinxClient::Query("搜索字符串")進(jìn)行全文搜索;
    #title、content作為字符串/文本字段,被全文索引

    sql_attr_uint           = group_id                 #從SQL讀取到的值必須為整數(shù);sql_attr_uint表示該字段是數(shù)值屬性
    sql_attr_timestamp      = date_added      #從SQL讀取到的值必須為整數(shù),作為時間屬性;sql_attr_timestamp表示該字段是時間屬性;可以不用該配置

    sql_query_info_pre      = SET NAMES utf8                                        #命令行查詢時,設(shè)置正確的字符集,3.2.14開始支持
    sql_query_info          = SELECT * FROM documents WHERE id=$id  #命令行查詢時,從數(shù)據(jù)庫讀取原始數(shù)據(jù)信息
}

#index定義
index mysql
{
    source            = mysql             #對應(yīng)的source名稱
    path            = var/data/mysql #索引存放的位置,路徑為var/data
    docinfo            = extern
    mlock            = 0
    morphology        = none
    min_word_len        = 1
    html_strip                = 0
    #charset_dictpath = /usr/local/mmseg3/etc/    #BSD、Linux環(huán)境下設(shè)置,/符號結(jié)尾
    charset_dictpath = etc/ #Windows環(huán)境下設(shè)置,/符號結(jié)尾
    charset_type        = zh_cn.utf-8

}

建立索引數(shù)據(jù)

執(zhí)行indexer建立索引數(shù)據(jù),命令格式為:

indexer -c 配置文件的路徑 index名稱

例如:

/usr/local/coreseek/bin/indexer -c etc/csft.conf questions

啟動檢索服務(wù)

檢索服務(wù)需要配置守護(hù)進(jìn)程的相關(guān)選項,示例為:

#定義searchd守護(hù)進(jìn)程的相關(guān)選項
searchd
{
           #定義監(jiān)聽的IP和端口
   #listen            = 127.0.0.1
   #listen            =172.16.88.100:3312
    listen            = 3312
    listen            = /var/run/searchd.sock
           #定義log的位置
   log                =/usr/local/coreseek/var/log/searchd.log
           #定義查詢log的位置
   query_log          =/usr/local/coreseek/var/log/query.log
           #定義網(wǎng)絡(luò)客戶端請求的讀超時時間
   read_timeout       = 5
           #定義子進(jìn)程的最大數(shù)量
   max_children       = 300
           #設(shè)置searchd進(jìn)程pid文件名
   pid_file           =/usr/local/coreseek/var/log/searchd.pid
           #定義守護(hù)進(jìn)程在內(nèi)存中為每個索引所保持并返回給客戶端的匹配數(shù)目的最大值
   max_matches        = 100000
           #啟用無縫seamless輪轉(zhuǎn),防止searchd輪轉(zhuǎn)在需要預(yù)取大量數(shù)據(jù)的索引時停止響應(yīng)
    #也就是說在任何時刻查詢都可用,或者使用舊索引,或者使用新索引
   seamless_rotate    = 1
           #配置在啟動時強(qiáng)制重新打開所有索引文件
   preopen_indexes    = 1
           #設(shè)置索引輪轉(zhuǎn)成功以后刪除以.old為擴(kuò)展名的索引拷貝
   unlink_old         = 1
           # MVA更新池大小,這個參數(shù)不太明白
   mva_updates_pool   = 1M
           #最大允許的包大小
   max_packet_size    = 32M
           #最大允許的過濾器數(shù)
   max_filters        = 256
           #每個過濾器最大允許的值的個數(shù)
   max_filter_values  = 4096
}

配置完畢后,執(zhí)行searchd啟動或關(guān)閉檢索服務(wù),命令格式為:

searchd -c 配置文件的路徑
searchd -c 配置文件的路徑 --stop

例如:

/usr/local/coreseek/bin/searchd -c etc/csft.conf
/usr/local/coreseek/bin/searchd -c etc/csft.conf --stop

程序中api的調(diào)用

官方的安裝內(nèi)提供了Php, Python, Java, C, Ruby等語言的api,這里列出一個項目中使用的Java版api調(diào)用示例:

SphinxClient cl = new SphinxClient();
cl.SetServer("192.168.19.135", 9314);  //sphinx服務(wù)器與端口
cl.SetMatchMode(SphinxClient.SPH_MATCH_ANY);    //匹配任意
cl.SetSortMode(SphinxClient.SPH_SORT_RELEVANCE, "");  //按匹配程序排序
cl.SetLimit(0, 20);                     //分頁參數(shù)
int[] users = {0, 839017};
cl.SetFilter("check_user", users, false);  //根據(jù)條件過濾

SphinxResult res = cl.Query("一元一次方程", "faq_question"); //指定哪個索引集中檢索
System.out.println("Query retrieved " + res.total + " of " + res.totalFound + " matches in " + res.time + " sec.");
System.out.println("Query stats:");
for(int i=0; i<res.words.length; i++){
    SphinxWordInfo wordInfo = res.words[i];   //words返回分詞結(jié)果
    System.out.println("\t'" + wordInfo.word + "' found " + wordInfo.hits + " times in " + wordInfo.docs + " documents");
}
for(int i=0; i<res.matches.length; i++){
    SphinxMatch info = res.matches[i];   //matches返回搜索結(jié)果
    System.out.println((i + 1) + ". id=" + info.docId + ", weight=" + info.weight);
}

詳細(xì)的api參考。

在Coreseek/Sphinx中支持Mongodb數(shù)據(jù)源

Sphinx中支持Mongodb主要有兩種方式,xmlpipepython數(shù)據(jù)源。

xmlpipe數(shù)據(jù)源

編寫一個自己的服務(wù),將Mongodb數(shù)據(jù)轉(zhuǎn)換成Sphinx能夠識別的xml文本數(shù)據(jù)源,再調(diào)用api生成索引數(shù)據(jù)。
xml數(shù)據(jù)示例:

<?xml version="1.0" encoding="utf-8"?>
<sphinx:docset>
    <sphinx:schema>
    <sphinx:field name="subject"/>
    <sphinx:field name="content"/>
    <sphinx:attr name="published" type="timestamp"/>
    <sphinx:attr name="author_id" type="int" bits="16" default="1"/>
    </sphinx:schema>
    <sphinx:document id="1">
        <subject>愚人節(jié)最佳蠱惑爆料 谷歌300億美元收購百度</subject>
        <published>1270131607</published>
        <content>據(jù)國外媒體報道,谷歌將巨資收購百度,涉及金額高達(dá)300億美元。谷歌借此重返大陸市場。......
        </content>
        <author_id>1</author_id>
    </sphinx:document>
    <sphinx:document id="2">
        <subject>Twitter主頁改版 推普通用戶消息增加趨勢話題</subject>
        <published>1270135548</published>
        <content>4月1日消息,據(jù)國外媒體報道,Twitter本周二推出新版主頁,目的很簡單:幫助新用戶了解Twitter和增加用戶黏稠度。......
        </content>
        <author_id>1</author_id>
    </sphinx:document>
    <sphinx:document id="3">
        <subject>死都要上!Opera Mini 體驗版搶先試用</subject>
        <published>1270094460</published>
        <content>Opera一直都被認(rèn)為是瀏覽速度飛快,同時在移動平臺上更是占有不少的份額。......
        </content>
        <author_id>2</author_id>
    </sphinx:document>
</sphinx:docset>

Sphinx的配置:

#源定義
source xml
{
    type            = xmlpipe2
    xmlpipe_command = bin\cat var/test/test.xml     #此處也可使用其他可執(zhí)行程序輸出xml數(shù)據(jù)
}

參考:

python數(shù)據(jù)源

Python數(shù)據(jù)源也稱為萬能數(shù)據(jù)源,借助Python強(qiáng)大的靈活性與活躍度,Sphinx做了一層橋接,此方式幾乎可以讀取所有形式的數(shù)據(jù)。
官網(wǎng)的代碼例子:

# -*- coding:utf-8 -*-
# coreseek3.2 python source演示操作mssql數(shù)據(jù)庫
# author: HonestQiao
# date: 2010-06-01 10:05

from os import path
import os
import sys
import pymssql
import datetime

class MainSource(object):
    def __init__(self, conf):
        self.conf =  conf
        self.idx = 0
        self.data = []
        self.conn = None
        self.cur = None

    def GetScheme(self):  #獲取結(jié)構(gòu),docid、文本、整數(shù)
        return [
            ('threadid' , {'docid':True, } ),
            ('title', { 'type':'text'} ),
            ('context', { 'type':'text'} ),
            ('date', {'type':'integer'} ),
        ]

    def GetFieldOrder(self): #字段的優(yōu)先順序
        return [('title', 'context')]

    def Connected(self):   #如果是數(shù)據(jù)庫,則在此處做數(shù)據(jù)庫連接
        if self.conn==None:
            self.conn = pymssql.connect(host='127.0.0.1', user='root', password='123456', database='bbs', as_dict=True)
            self.cur = self.conn.cursor()
            sql = 'SELECT threadid,title,content,date FROM ss_bbs_topic'
            self.cur.execute(sql)
            self.data = [ row for row in self.cur]
        pass

    def NextDocument(self):   #取得每一個文檔記錄的調(diào)用
        if self.idx < len(self.data):
            item = self.data[self.idx]
            self.docid = self.threadid = item['threadid'] #'docid':True
            self.title = item['title'].encode('utf-8')
            self.context = item['context'].encode('utf-8')
            self.date = item['date']
            self.idx += 1
            return True
        else:
            return False

if __name__ == "__main__":    #直接訪問演示部分
    conf = {}
    source = MainSource(conf)
    source.Connected()

    while source.NextDocument():
        print "id=%d, subject=%s" % (source.docid, source.title)
    pass
#eof

其中核心的概念是利用python將目標(biāo)數(shù)據(jù)按規(guī)定的模型讀取至python虛擬機(jī)中,Sphinx再從虛擬機(jī)里讀出索引源數(shù)據(jù)。
相關(guān)配置文件:

#python路徑定義
python
{
    path = /usr/local/coreseek/etc/pysource         #BSD、Linux環(huán)境下設(shè)置
    path = /usr/local/coreseek/etc/pysource/csft_demo   #BSD、Linux環(huán)境下設(shè)置
    #path = etc/pysource            #Windows環(huán)境下設(shè)置,最好給出絕對路徑
    #path = etc/pysource/csft_demo  #Windows環(huán)境下設(shè)置,最好給出絕對路徑
}

#源定義
source python
{
    type = python
    name = csft_demo.MainSource #對應(yīng)etc/pysource/csft_demo/__init__.py中的MainSource
}

參考:

項目服務(wù)架構(gòu)

項目使用xmlpipe的方案支持mongodb數(shù)據(jù),實現(xiàn)后的架構(gòu)如圖:

coreseek1

  • 索引數(shù)據(jù)源服務(wù)定時生成xml,調(diào)用coreseek命令生成索引
  • 客戶端請求Restful接口,服務(wù)端組裝搜索條件,調(diào)用coreseekapi返回數(shù)據(jù)主鍵,
  • mongodb中獲得業(yè)務(wù)數(shù)據(jù),最后組裝Json結(jié)果返回給客戶端

中文分詞

Sphinx自帶LibMMSeg庫實現(xiàn)中文分詞,創(chuàng)建索引的過程中會將field字段內(nèi)容進(jìn)行分詞。
但是在實際應(yīng)用中會發(fā)現(xiàn),一般情況下的分詞無法對單字進(jìn)行檢索,例如一個短語:

學(xué)習(xí)漢語拼音

默認(rèn)的分詞結(jié)果是

學(xué)習(xí)_漢語拼音

此時用單字“學(xué)”,“拼音”等就無法從索引中檢索出數(shù)據(jù)。
雖然我并不認(rèn)為單字搜索是一個合理的全文檢索需求,但有時項目需要,也不得不去尋求解決的方案。

同義詞庫

在分詞組件中啟用同義詞功能,并完善同義詞庫,例如建立一個同義詞條目:

漢語拼音->拼音

這樣檢索“拼音”就能索引到“漢語拼音”的數(shù)據(jù)。
參考資料:

但此方案也無法解決單字檢索的問題。

一元分詞

一元分詞的方案是,不使用詞庫,把每個單字做為一個分詞建立索引數(shù)據(jù),檢索時也對每個字進(jìn)行拆分。
這種方法索引數(shù)據(jù)會變得巨大,查詢開銷也會變大,而且享受不了詞庫所帶來的精準(zhǔn)性。
coreseek核心配置:

#charset_dictpath=/usr/local/mmseg3/etc/ 此行需要注釋掉,從而關(guān)閉可以提升性能和精確度的中文分詞功能!
charset_type=utf-8 #表示啟用使用utf-8字符集,來處理中文字符。
ngram_len=1 #表示使用一元字符切分模式,從而得以對單個中文字符進(jìn)行索引;
ngram_chars=U+4E00..U+9FBF, ......  #表示要進(jìn)行一元字符切分模式的字符集;
charset_table=U+FF10..U+FF19->0..9, 0..9,...... #表示可被一元字符切分模式認(rèn)可的有效字符集;

參考資料:

中綴索引

中綴索引是除了對關(guān)鍵字本身還會對所有可能的中綴(即子字符串)做索引。
舉個例子:

漢語拼音

這個詞將會被切分為

漢,漢語,漢語拼,語,語拼,語拼音,拼音,音

這些子字符串,并創(chuàng)建索引。
這種方法索引數(shù)據(jù)將會更為龐大,但相對一元分詞精準(zhǔn)性相對較高,所以我們的項目最終采用這個方案。
coreseek核心配置:

enable_star=0 #不使用通配符,默認(rèn)不啟用,可以不寫
min_infix_len=1 #使用中綴索引,并且最小索引為1,關(guān)于該項作用不知者可以查詢手冊
infix_fields=字段1,字段2 #因為中綴索引會使索引量急劇膨脹,所以最好選擇你認(rèn)為最主要的少量幾個字段做中綴索引。

參考資料:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容