在搜索引擎输入框中输入搜索词时,会实时得到搜索建议:
这些建议词是从搜索引擎的搜索记录中按照一定算法挖掘出的高关联度短语。如果没有搜索记录,能不能实现相关搜索建议呢?本文介绍一个简单的处理方法,下面将分别说明如何得到相关建议词和前端的实现方式。
1.相关词语的获取
最直观的相关词语是:两个词语(短语)之间具有一部分相同字,另一部分不同,它们之间相同的部分所占比例越高,则两个词语越相关。例如,在这种情况下"计算机"和"计算器"是相关的,"计算机"和"计算机网络"是相关的。如果有一个词典包含足够的词语,并且支持这种模糊的词语匹配方式,就可以实现词语建议。
Lucene专门提供了模糊查询FuzzyQuery实现上述功能[1]。它采用编辑距离算法计算两个字符串之间的相似度,编辑距离用来计算从一个字符串转换到另一个字符串所需的最少插入、删除和替换的字母个数。例如,"three"和"tree"之间的编辑距离为1。使用FuzzyQuery查询时,和查询词距离越小的词评分越高,在结果中排名越高。这样就可以通过模糊搜索获得词库中最相近的词语。
实际操作中,建立Lucene索引时每个document的索引域中只索引一个词,这样从返回的结果文档中可以直接取出该域中的词语。
《Lucene实战》中给出了下面的代码展示FuzzyQuery的用法:
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();
}
虽然模糊搜索相比分析搜索log不够智能、缺乏时效性,但是在面向特定领域、有现成领域词典的情况下,这种方式简单而有效。例如,对专利领域,国际专利分类(IPC)的官方文件对整个技术领域做了全面的分类描述,里面出现的词语对专利方面的应用做输入建议会有不错的效果。
2.前端效果实现
ExtJs中的下拉选择框组件ComboBox可以实现输入时动态更新推荐词语列表的效果。
ComboBox的输入框右侧有一个下拉箭头(见下图),可以修改配置项使其不显示,模拟输入框的效果。

一个配置示例如下所示,几个关键的配置项做了注释:
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%'
});
为了根据用户输入动态更新列表内容,需要监听键盘输入事件,在每次输入一个字时发送当前输入内容到后端,后端获取相关词语后返回前端,重新展示。事件处理部分的代码为:
suggestionCombo.on('keyup',function(textField,e){
//如果键盘输入不是方向键,则更新列表
if(!e.isNavKeyPress()){
textField.getStore().load({params:{inputText:textField.getRawValue()}});
}
});
上面的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
转载请标明出处:马楠 | Journey of Little Horse
Will you grow up into someone like Li Yanhong君?
想太远没有用,重要的是做好该做的事。
Cool。
计算机工作站编辑距离不是3么?为什么比编辑距离2的排名还靠前?
所谓领域词库,只是为了分词吗?现在分类词库能否做到基本全覆盖甚至多重覆盖?
即可否根据两词共属词库的数量及大小来计算距离?
这周一直没空解决这个问题,今天才看了代码,久等了。
排名的问题是我的代码有bug,在预处理和写索引时用了两个不同的分词器,所以出现了文档域中内容和索引内容不一致的情况。总之,修改了bug,正确的排序结果是:
计算机,score:2.9181852
计算器,score:1.8373759
计算机房,score:1.8373759
计算机构,score:1.8373759
计算尺,score:1.8373759
带计算机,score:1.8373759
计算,score:1.2969714
拖拉机,score:0.7565666
发动机,score:0.7565666
挖掘机,score:0.7565666
Lucene的模糊搜索基于编辑距离但也做了改动,计算时会考虑词的长度,所以上面,计算尺和计算相对计算机的距离都是1,但是计算尺的排名更高。
领域词库就是用来推荐的词语来源。“可否根据两词共属词库的数量及大小来计算距离”,这是什么意思呢?
多谢提问哈,帮我解决了bug:)