一、SynonymFilter的使用
Lucene42的SynonmFilter使用方法是很简单的;看下面这个demo就可以明白了。
package com.xh.lucene; import java.util.Map; import java.util.HashMap; import java.io.StringReader; import java.io.IOException; importorg.apache.lucene.analysis.core.StopAnalyzer; import org.apache.lucene.analysis.core.StopFilter; importorg.apache.lucene.analysis.core.LowerCaseFilter; importorg.apache.lucene.analysis.core.WhitespaceTokenizer; importorg.apache.lucene.analysis.standard.StandardTokenizer; import org.apache.lucene.analysis.synonym.SynonymFilterFactory; importorg.apache.lucene.analysis.TokenStream; importorg.apache.lucene.analysis.tokenattributes.CharTermAttribute; importorg.apache.lucene.analysis.util.FilesystemResourceLoader; import org.apache.lucene.util.Version; import org.apache.lucene.util.CharsRef; public final class SynonymFilterDemo { private static voiddisplayTokens(TokenStream ts) throws IOException { CharTermAttribute termAttr = ts.addAttribute(CharTermAttribute.class); ts.reset(); while(ts.incrementToken()) { String token = termAttr.toString(); System.out.print("[" + token + "] "); } System.out.println(); ts.end(); ts.close(); } public static voidmain(String[] args) throws Exception { String testinput = "aaa fooaaa baraaa bazaaa GB gib gigabytegigabytes"; Version ver=Version.LUCENE_42; String synfile="synonyms.txt"; Map<String,String> filterargs=new HashMap<String, String>(); filterargs.put("luceneMatchVersion",ver.toString()); filterargs.put("synonyms", synfile); filterargs.put("ignoreCase", "true"); filterargs.put("format", "solr"); filterargs.put("expand", "false"); SynonymFilterFactory factory= new SynonymFilterFactory(); factory.setLuceneMatchVersion(Version.LUCENE_42); factory.init(filterargs); factory.inform(new FilesystemResourceLoader()); WhitespaceTokenizer tokenizer = new WhitespaceTokenizer(ver, new StringReader(testinput)); TokenStream ts = factory.create(tokenizer); //ts =new LowerCaseFilter(ver,tokenizer); //ts =new StopFilter(ver, ts, //StopAnalyzer.ENGLISH_STOP_WORDS_SET); displayTokens(ts); } }
其中synonyms.txt在lucene的源代码
lucene\analysis\common\src\test\org\apache\lucene\analysis\synonym\synonyms.txt。
把synonyms.txt复制到项目根目录下就可以了。
运行结果如下:
[aaaa] [fooaaa] [fooaaa] [fooaaa] [gb] [gb] [gb][gb]
如果把 filterargs.put("expand","false");换成filterargs.put("expand", "true");
则结果如下:
[aaaa] [fooaaa] [baraaa] [bazaaa] [fooaaa] [baraaa] [bazaaa][fooaaa] [baraaa] [bazaaa] [gb] [gib] [gigabyte] [gigabytes] [gb] [gib][gigabyte] [gigabytes] [gb] [gib] [gigabyte] [gigabytes] [gb] [gib] [gigabyte][gigabytes]
了解这两者之间的差别,我们需要先了解lucene中同义词典的两种格式
格式一:aaa => aaaa
格式二:fooaaa,baraaa,bazaaa
格式一表示如果文本中出现了aaa,那么直接替换成aaaa。expand参数对此没有影响。
格式二表示如果文本中出现了fooaaa或者baraaa或者bazaaa,当expand=true时,把待替换的文本用fooaaabaraaa bazaaa替换;当expand=false时,把待替换文本用第一个单词即fooaaa替换。
这些不同点自己跑一遍就都明白了。
二、SynonymFilter的实现原理
SynonymFilter的实现是很精巧的。运用了Hash表和FST(Finite State Transducer)两种数据结构。
Hash表存储了同义词典。
FST可以视为SortedMap<Word,Pos>
Word表示可以被查询到的词,Pos表示Word对应的同义词在Hash表中的位置。
FST是FSA/FSM的一种变形。 FST的原理可以参考有限自动机。
简单地说,在SynonymFilter中FST的input是词,output是词对应同义词在Hash表中的位置。
例如:有abandon,desert, forsake, leave这组同义词,我们的处理方式是expand
SynonymMap的处理逻辑如下:
第一步:把abandon,desert,forsake,leave作为整体存储到Hash表中。并记录存储起始位置pos。
第二步,构造的map如下:
Map.put(“abandon”,pos);
Map.put(“desert”,pos);
Map.put(“forsake”,pos);
Map.put(“leave”,pos);
这样的话,只要在Map中找到的词,都用Hash中存储的词替换。由于HashMap本身是用Hash表实现的,空间浪费比较严重。而字符串之间的前缀和后缀信息可以对数据进行压缩,所以用FST存储字符串的优点就出来了。(当然,本例的字符串之间没有公共的前缀和后缀,这只是一个jok)
本文出自 “每天进步一点点” 博客,请务必保留此出处http://sbp810050504.blog.51cto.com/2799422/1361539
2014-2-13_lucene42源代码解析之SynonymFilter的应用及实现原理
原文:http://sbp810050504.blog.51cto.com/2799422/1361539