用Lucene模糊搜索和ExtJs ComboBox实现搜索建议词效果
在搜索引擎输入框中输入搜索词时,会实时得到搜索建议:
这些建议词是从搜索引擎的搜索记录中按照一定算法挖掘出的高关联度短语。如果没有搜索记录,能不能实现相关搜索建议呢?本文介绍一个简单的处理方法,下面将分别说明如何得到相关建议词和前端的实现方式。
1.相关词语的获取
最直观的相关词语是:两个词语(短语)之间具有一部分相同字,另一部分不同,它们之间相同的部分所占比例越高,则两个词语越相关。例如,在这种情况下”计算机”和”计算器”是相关的,”计算机”和”计算机网络”是相关的。如果有一个词典包含足够的词语,并且支持这种模糊的词语匹配方式,就可以实现词语建议。
Lucene专门提供了模糊查询FuzzyQuery实现上述功能[1]。它采用编辑距离算法计算两个字符串之间的相似度,编辑距离用来计算从一个字符串转换到另一个字符串所需的最少插入、删除和替换的字母个数。例如,”three“和”tree“之间的编辑距离为1。使用FuzzyQuery查询时,和查询词距离越小的词评分越高,在结果中排名越高。这样就可以通过模糊搜索获得词库中最相近的词语。
实际操作中,建立Lucene索引时每个document的索引域中只索引一个词,这样从返回的结果文档中可以直接取出该域中的词语。
《Lucene实战》中给出了下面的代码展示FuzzyQuery的用法:
[code lang=”java”] public void testFuzzy() throws Exception { indexSingleFieldDocs(new Field[] { new Field("contents", "fuzzy", Field.Store.YES, Field.Index.ANALYZED), new Field("contents", "wuzzy", Field.Store.YES, Field.Index.ANALYZED) });
IndexSearcher searcher = new IndexSearcher(directory);
Query query = new FuzzyQuery(new Term("contents", "wuzza"));
TopDocs matches = searcher.search(query, 10);
assertEquals("both close enough", 2, matches.totalHits);
assertTrue("wuzzy closer than fuzzy",
matches.scoreDocs[0].score != matches.scoreDocs[1].score);
Document doc = searcher.doc(matches.scoreDocs[0].doc);
assertEquals("wuzza bear", "wuzzy", doc.get("contents"));
searcher.close();
}[/code]
虽然模糊搜索相比分析搜索log不够智能、缺乏时效性,但是在面向特定领域、有现成领域词典的情况下,这种方式简单而有效。例如,对专利领域,国际专利分类(IPC)的官方文件对整个技术领域做了全面的分类描述,里面出现的词语对专利方面的应用做输入建议会有不错的效果。
2.前端效果实现
ExtJs中的下拉选择框组件ComboBox可以实现输入时动态更新推荐词语列表的效果。
ComboBox的输入框右侧有一个下拉箭头(见下图),可以修改配置项使其不显示,模拟输入框的效果。
一个配置示例如下所示,几个关键的配置项做了注释:
[code lang=”javascript”] var suggestionCombo = new Ext.form.ComboBox({ hiddenName : ‘cname’, fieldLabel : ‘名称’, triggerAction : ‘all’, store : suggestionCombostore, displayField : ‘text’, mode : ‘local’, forceSelection : false, minListWidth : 270, resizable : true, allowBlank : false, editable : true, //允许输入 enableKeyEvents:true, //允许键盘输入事件 hideTrigger: true, //隐藏下拉箭头 anchor : ‘100%’ }); [/code]
为了根据用户输入动态更新列表内容,需要监听键盘输入事件,在每次输入一个字时发送当前输入内容到后端,后端获取相关词语后返回前端,重新展示。事件处理部分的代码为:
[code lang=”javascript”]suggestionCombo.on(‘keyup’,function(textField,e){ //如果键盘输入不是方向键,则更新列表 if(!e.isNavKeyPress()){ textField.getStore().load({params:{inputText:textField.getRawValue()}}); } }); [/code]
上面的textField.getRawValue()即获取当前的输入文本内容,用于后台查询建议词。
最终实现的效果如下图所示:
如果能完善索引的领域词库,改善中文分词效果,可以得到更好的搜索建议词列表。
参考文献
[1] 《Lucene实战》(第二版),P94
[2] extjs google suggest效果, http://wodar.iteye.com/blog/553067
[3] Ext.form.ComboBox实现google Suggestion效果, http://love4j.iteye.com/blog/516399