采用Lucene.Net与盘古分词器的网上书城站内搜索方法

2015-10-19 14:08马军杨维明周民
电脑知识与技术 2015年20期

马军 杨维明 周民

摘要:该文针对网上书城对信息实时性与准确性高的要求,提出了使用lucene与盘古分词器相结合的站内搜索系统解决方案。通过分析lucene内置分词器与盘古分词器的性能差异,选择了针对中文开发的盘古分词器,提高了搜索的准确性;通过采用“生产者与消费者”多线程模式与“单例”设计模式相结合的方法,实现了数据的实时更新。实验结果证明了设计方案的有效性。

关键词:lucene;盘古分词;网上书城;站内搜索

中图分类号:TP393 文献标识码:A 文章编号:1009-3044(2015)20-0184-04

站内搜索是指对网站内部信息的精确检索和资源挖掘, 它为用户提供准确、快速的站内信息检索服务,站内搜索效果直接决定着网站商品的销量。现有的网上书城网站大多采用链接google和baidu网站的方法实现搜索,不利于数据的实时更新,此外,还存在着书籍信息准确性不高的缺点。

Lucene是目前最流行开源检索工具包之一, 已经在许多搜索项目中得到了应用。盘古分词也是一个比较成熟的中文分词组件,而且采用多元分词技术,可以很好的实现对中文的分词。因而本文针对中小网上书城提出了一个基于lucene.net与盘古分词的站内搜索技术方案,提高网站的竞争力。

1 站内搜索方案设计

网上书城站内搜索系统框图如图1所示。

本文提出的站内搜索方案将系统分为书籍编辑模块、索引模块、检索模块和展示模块4个部分。书籍编辑模块主要负责收集书籍的信息并且转换为纯文本信息,即lucene可识别的信息。索引模块主要针对数据库创建和维护索引库,即每当增加或修改书籍的信息到数据库时,就更新索引库。 检索模块主要针对用户输入的关键字进行分析,然后查询索引库找到相关联书籍,并且按相关性程度排序。展示模块主要负责接受用户输入的关键字,并展示搜索的结果中书籍的信息以及如何展示。根据搜索结果,可以定制个性化的展示。

图1 网上书城站内搜索系统框图

2 盘古分词器性能分析

盘古分词是一个中英文分词组件。它的作者通过分析比较中文分词的一元分词、二元分词,多元分词和精确分词的性能,得出多元分词适用性更强。但采用多元分词产生了一些问题,第一,多元分词和搜索引擎结合得到较多的匹配结果,同时也增加了索引文件的大小;第二,由于将一些单词进行了拆分,搜索结果的排序会受到影响。为了克服这两个缺点,盘古分词提出了多元分词的冗余度(Redundancy)和多元分词结果的权重级别(Rank)的概念。盘古分词支持3级冗余。比如“湖北大学”,冗余度为0、1、2时,分词结果分别是“湖北大学”、“湖北,湖北大学,大学”、“湖北,湖北大学,大,大学,学”。盘古分词将多元分词出来的单词根据其词长,词的间隔以及未登录词的取舍等条件给定了不同的权重。在搜索时对分解出来的关键字,我们指定权重来影响搜索结果,以实现结果有效排序。比如搜“湖北大学”时,可以将“湖北大学”设置较高的权重,而“大学”和“湖北”设置较低权重,则包含“湖北大学”的记录就优先于包含“湖北”或“大学”的记录,这样就解决了排序问题。

为了说明盘古分词优于lucene内置分词器,做了如下表格的对比。从表中可以看出,StopAnalyzer针对非字母字符拆分文本,然后小写英文字母,再过滤掉停用词;KeywordAnalyzer将整个文本当作一个词处理;SimpleAnalyzer和StopAnalyzer类似;WhitespaceAnalyzer根据空格拆分词汇单元;StandardAnalyzer按每个汉字拆分词,PanGuAnalyzer按有意义的中文词语分词,显然效果最佳。因而本文选择盘古分词器,提高搜索的准确性。

分词比较的结果如下表1所示。

表1 盘古分词器与lucene内置分词器性能比较

[分词器

结果

分词内容\&Lucen.NET分析与应用 机械工业出版社 吴众欣

\&StopAnalyzer\&lucene|net分析与应用|机械工业出版社|吴众欣\&KeywordAnalyzer\&lucene分析与应用 机械工业出版社 吴众欣\&SimpleAnalyzer\&lucene|net分析与应用|机械工业出版社|吴众欣\&WhitespaceAnalyzer\&lucene.NET分析与应用|机械工业出版社|吴众欣\&StandardAnalyzer\&|lucene|分|析|与|应|用|机|械|工|业|出|版|社|吴|众|欣\&PanGuAnalyzer\&|lucene|分析|与|应用|机械|工业出版社|吴众欣\&]

3 站内搜索的实现

3.1 书籍编辑模块

为了可以批量添加书籍信息,使用XML存储书籍信息。网站管理员使用后台管理界面添加或修改书籍信息。本文从XML读文件信息的使用.NET内置操作XML文件的库函数。管理员输入的书籍信息是纯文本格式,无需解析。

本文从XML文件获得纯文本信息的核心代码如下:

//创建一个XML对象

XMLDocument doc=new XMLDocument();

//加载指定的XML文档

doc.Load(@"E:\books.xml");

//创建读取器

XMLNodeReader reader=new XMLNodeReader();

//读取节点的信息

While(reader.Read())

{

Switch(reader.NodeType)

{

Case XmlNodeType.Element

If(reader.Name="title")

bookTitle=reader.Value;

...

}

}

3.2 索引模块

索引库相当于关系数据库,非常关键和重要,直接决定搜索的响应速度与准确性。针对网上书城,书籍关键信息包括书名、出版社、作者、出版时间、书籍简介等,因此在建立索引库的时候一定要包含这些关键信息,方便用户快速查询,其它信息可根据需求添加。

为了实现网上书城实现实时的数据更新,本文采用“单例设计模式”和“生产者与消费者”多线程模型相结合的方式。主线程创建单例管理器,整个程序中是唯一的,保证了数据更新数据的一致性。同时主线程扮演生产者的角色,当添加或修改书籍的信息时,主线程(生产者)就会调用管理器,向任务列表新增任务,不用关心任务是否执行了。整个网站一旦运行,主线程(生产者)调用管理器创建消费者线程并启动该线程。消费者线程就会循环的检测任务列表,如果有任务,就执行索引任务,向索引库中添加或修改书籍信息,否则,可根据需要设置该线程休眠的时间,防止占用cpu,造成浪费。索引系统原理框图如图2所示。

图2 索引系统原理框图

具体索引功能使用lucene提供的核心类IndexWriter、Direcrtory、Analyzer、Document、Field实现索引。IndexWriter这个类负责创建新索引或打开已有索引,以向索引中添加、删除或更新被索引文档的信息。Direcrtory类描述了lucene索引存放位置。它是一个抽象类,它的子类负责具体指定索引的存储路径。Analyzer这个类负责分词,它是一个抽象类,需要具体类实现它。Document类似于关系数据库的记录,是一些字段(field)的集合。Field类似于关系数据库的字段。索引建立过程:创建IndexWriter类,指明索引库位置以及使用的分词器;创建一个文件记录类Document;把要记录的字段加入Document;把Document写入到索引库并且关闭索引。

核心代码如下:

//单例索引管理器

public class IndexAdministration

{

//创建单例

public static readonly IndexAdministration Instance=new

IndexAdministration();

//私有化构造器

Private IndexAdministration(){}

//创建索引任务列表

private List tasks = new List();

//启动消费者线程

public void StartComsumeThread()

{Thread threadIndexWork = new Thread(IndexWork);

threadIndexWork.Start();}

//执行索引任务

private void IndexWork(){...}

//添加索引任务,生产者调用

public void AddTask(IndexTask task){ ...tasks.Add(task);...}

//删除索引任务,生产者调用

public void RemoveTask(IndexTask task){...tasks.Remove(task)...}

}

执行索引任务的核心代码:

private void IndexWork()

{

while(ture)

{...

//若无任务,睡眠

if (tasks.Count <= 0){ Thread.Sleep(3 * 1000);continue;}

//创建或打开索引目录

FSDirector IndexDirectory=FSDirectory.Open(

new DirectoryInfo(indexFilePath),new NativeFSLockFactory());

//创建索引写入器对象,参数1:索引库位置,参数2:分词器(盘古分词),参数3:更新或添加

IndexWriter writer=new IndexWriter(IndexDirectory, new PanGuAnalyzer(),isUpdate);

//创建Document对象

Document doc=new Document();

//添加字段到Docment对象中,根据需要添加必要的字段

doc.Add(new Field("title",bookTitle,Field.Store.YES,

Field.Index.ANALYZED));

...

//将doc添加到索引库

writerAddDocument(doc);

//关闭写入器

writer.Close();

//关闭索引库

IndexDirectory.Close();

}

}

3.3 检索模块

lucene检索功能使用lucene提供检索的核心类IndexSearcher、Term、Query、TopScoreDocCollector实现。IndexSearcher类用于搜索使用IndexWriter类建立的索引库,它是连接索引的核心,以只读的方式打开索引库。Term对象是检索功能的最基本单元,包含域名和域文本值。Query是抽象父类,它有很多具体子类,最基本的子类TermQuery,用来匹配指定域中包含特定项的文档。TopScoreDocCollector是一个简单指针容器,指向匹配查询条件的前N个搜索结果,N可以根据需要选择,方便分页展示。

检索过程:打开索引库FSDirectory;创建一个搜索器IndexSearcher指向索引库;创建一个查询类Query的子类并对输入的内容进行分词;组装查询类,lucene提供多种查询类,根据需要选择;通过调用搜索器的Search方法执行查询,将结果放到TopScoreDocCollector指针容器;根据需要获得查询结果的文档内容。

核心代码

//打开已创建的索引库,

FSDirectory IndexDirectory=FSDirectory.Open(

new DirectoryInfo(indexFilePath),new NoLockFactory());

//创建搜索器

IndexSearcher indexSearcher = new IndexSearcher(IndexDirectory);

//创建Query的一个实现类,根据需要创建

PhraseQuery query = new PhraseQuery();

//对输入内容进行分词

List cutWords=CutWord(inputWords);

//组装查询条件,根据需要设置

foreach(string word in cutWords){ query.Add(new Term("bookTitle", word));}

...

//创建存储查询结果的指针容器

TopScoreDocCollector collector = TopScoreDocCollector.create(100, true);

//查询,第一个参数:查询条件,第二个:过滤条件,第三个:指针容器

indexSearcher .Search(query, null, collector);

//从指针容器中获得结果,第一个参数:结果的起始位置,第二个:获取数量

ScoreDoc[] documents = collector.TopDocs(startIndex,Count).scoreDocs;

//从搜索结果中获取Document

for (int i = 0; i < documents .Length; i++){

//得到文档编号,lucene内部分配的,为了降低内存,结果只有编号

int documentId = documents [i].doc;

/根据id找到Document

Document doc = searcher.Doc(docId);

//获取Document的具体内容,根据需求获取所需内容,便可以展示给用户了

string bookTitle= doc.Get("title");

....

}

分词函数:

List cutWords(string words, Analyzer analyzer)

//存放分词结果的集合

{List results = new List();

//调用分词器分词

TokenStream ts = analyzer.ReusableTokenStream("", new StringReader(words));

Token token;

while ((token = ts.Next()) != null)

//添加到分词结果

{results.Add(token.TermText());}

//关闭分词器

ts.Close();

return results;}

3.4 展示模块

展示模块主要用来展示查询到匹配的结果。lucene并没用提供展示内容的接口函数,需要我们自己设计如何展示内容,由于本文是针对网上书城开发的站内搜索,所以选择一网页的形式展现。

本文使用.NET平台设计展示网页。实现思路:首先在展示页面拖放一个Reapter控件,其次把搜所到数据转换成一个集合listResult,然后将listResult绑定到Reapter数据源便可显示了。

为了更好的客户体验,像百度、Google、360搜索一样,将结果中的关键字用特殊的颜色标出,醒目的提示用户。本文使用盘古分词的高亮插件 PanGu.HighLight.dll实现。

4 实验结果与分析

基于上述设计与实现方法创建了一个虚拟的系统,进行实验验证。

使用vs2010平台搭建,lucene.net版本3.0.3,盘古分词版本2.3.1。

首先向索引库中添加1000本书的信息,其中两本书如表2所示。

表2 待搜索的书籍信息

[书名\&作者\&简介\&设计模式\&GOF4\&可复用面向对象软件的基础\&面向对象的程序设计\&Grady Booch\&面向对象分析与设计,纤细介绍了,面向对象原则\&]

输入“面向对象”,根据简介搜索,两本书都可搜出,且排在前两位,说明准确性高,及相关性设计合理。如图3所示为搜索结果截图。

图3 输入“面向对象”的搜索结果

其次输入“lucene”进行搜索,没有任何结果。说明索引库中没有这本书的信息。添加如表3的信息。

表3 添加的书籍信息

[书名\&作者\&简介\&lucene实战\&Erik Hatacher\&lucene实战这本书,从基础讲起,并运用大量例子说明使用方法,很实用。\&]

添加书籍信息后,立刻输入“lucene”,根据书名进行搜索,搜到了这本书,如图4所示为搜索结果截图。说明了实时性高。

图4 输入“lucene”进行搜索的结果

5 结束语

本文针对网上书城对数据更新实时性高与书籍信息准确性高的要求,提出了使用lucene与盘古分词器相结合的站内搜索系统解决方案。分析了盘古分词器的性能优势,选择盘古分词器,提高了搜索的准确性;采用“生产者与消费者”多线程模式与“单例”设计模式相结合的方法,实现了数据的实时更新。并通过实验验证了该方案的有效性。该方案主要针对中小型网上书城而设计的,对大型网上书城大数据不适应,这也是下一步继续研究探索的方向。

参考文献:

[1] 邱哲, 符滔滔, 王学松. 开发自己的搜索引擎Lucene+Heritrix[M]. 北京: 人民邮电出版社, 2010.

[2] 吴众欣, 沈家立. Lucene分析与应用[M]. 北京: 机械工业出版社, 2008.

[3] 李天平. 项目中的.NET[M]. 北京: 电子工业出版社, 2012.

[4] Martin Fowler. 重构改善既有代码的设计[M]. 北京: 人民邮电出版社.

[5] 周海松, 刘建明, 李龙. 基于Lucene的垂直搜索引擎研究与实现[J]. 桂林电子科技大学学报, 2014, 34(3): 226-229.

[6] 李永春, 丁华福. Lucene 的全文检索的研究与应用[J]. 计算机技术与发展, 2010, 20(2): 12-15.

[7] 朱学昊, 王儒敬, 余锋林, 唐昱. 基于 Lucene的站内搜索设计与实现[J]. 计算机应用与软件, 2008, 25(10): 6-9.

[8] 李建林. 基于Lucene的Web搜索引擎的研究[D]. 兰州: 兰州理工大学, 2010.

[9] 阴晓昱. 基于Lucene 多核并行索引方法的设计与实现[D]. 上海: 上海交通大学, 2010.

[10] 胡鹏飞. Lucene与中文分词技术的研究及应用[D]. 北京: 北京交通大学, 2010.