Lucene 同义词

网友投稿 524 2022-05-29

项目原来是基于一个挺老的项目基础做的迭代二次开发,原来使用的是庖丁分词,这个分词库已经很久没人更新了,而且和高版本的lucene也有兼容问题,所以处理同义词问题之前,我把原来使用的padding中文分词替换成word中文分词;

同义词处理逻辑实际代码主要参考的这两个帖子:

https://blog.csdn.net/yax405/article/details/43246237

https://blog.csdn.net/winnerspring/article/details/37567739

Lucene 同义词

部分代码如下:

package org.apdplat.word.lucene; import java.io.IOException; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer.TokenStreamComponents; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.core.LowerCaseFilterFactory; import org.apache.lucene.analysis.synonym.SynonymFilterFactory; import org.apache.lucene.analysis.util.FilesystemResourceLoader; import org.apache.lucene.util.Version; import org.apdplat.word.segmentation.Segmentation; import org.apdplat.word.segmentation.SegmentationAlgorithm; import org.apdplat.word.segmentation.SegmentationFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SynonymsAnalyzer   extends Analyzer {   private static final Logger LOGGER = LoggerFactory.getLogger(ChineseWordAnalyzer.class);   private Segmentation segmentation = null;      public SynonymsAnalyzer()   {     this.segmentation = SegmentationFactory.getSegmentation(SegmentationAlgorithm.BidirectionalMinimumMatching);   }      public SynonymsAnalyzer(String segmentationAlgorithm)   {     try     {       SegmentationAlgorithm sa = SegmentationAlgorithm.valueOf(segmentationAlgorithm);       this.segmentation = SegmentationFactory.getSegmentation(sa);     }     catch (Exception e)     {       this.segmentation = SegmentationFactory.getSegmentation(SegmentationAlgorithm.BidirectionalMinimumMatching);     }   }      public SynonymsAnalyzer(SegmentationAlgorithm segmentationAlgorithm)   {     this.segmentation = SegmentationFactory.getSegmentation(segmentationAlgorithm);   }      public SynonymsAnalyzer(Segmentation segmentation)   {     this.segmentation = segmentation;   }      private static SynonymFilterFactory factory = null;      protected static SynonymFilterFactory getSynonymsFactory()   {     if (factory == null)     {       Map paramsMap = new HashMap();              Version ver = Version.LUCENE_5_5_1;              paramsMap.put("luceneMatchVersion", ver.toString());       paramsMap.put("synonyms", "./synonyms.txt");       paramsMap.put("expand", "true");       factory = new SynonymFilterFactory(paramsMap);       try       {         FilesystemResourceLoader loader = new FilesystemResourceLoader(Paths.get("D:/RobotK/tomcat/synonyms", new String[0]));         factory.inform(loader);       }       catch (IOException e)       {         e.printStackTrace();       }     }     return factory;   }      public static void reloadSynonymFilterFactory()   {     Map paramsMap = new HashMap();          Version ver = Version.LUCENE_5_5_1;          paramsMap.put("luceneMatchVersion", ver.toString());     paramsMap.put("synonyms", "./synonyms.txt");     paramsMap.put("expand", "true");     factory = new SynonymFilterFactory(paramsMap);     try     {       FilesystemResourceLoader loader = new FilesystemResourceLoader(Paths.get("D:/RobotK/tomcat/synonyms", new String[0]));       factory.inform(loader);     }     catch (IOException e)     {       e.printStackTrace();     }   }      private static LowerCaseFilterFactory caseFactory = null;      public static LowerCaseFilterFactory getCaseFilterFactory()   {     if (caseFactory == null)     {       Version ver = Version.LUCENE_5_5_1;       Map filterArgs = new HashMap();       filterArgs.put("luceneMatchVersion", ver.toString());       caseFactory = new LowerCaseFilterFactory(filterArgs);     }     return caseFactory;   }      protected Analyzer.TokenStreamComponents createComponents(String fieldName)   {     Tokenizer tokenizer = new ChineseWordTokenizer(this.segmentation);     TokenStream casefilter = getCaseFilterFactory().create(tokenizer);     TokenStream filter = getSynonymsFactory().create(casefilter);     return new Analyzer.TokenStreamComponents(tokenizer, filter);   } }

其他参考的帖子有:

https://blog.csdn.net/u011066470/article/details/60963439

http://www.hankcs.com/program/java/lucene-synonymfilterfactory.html

https://blog.csdn.net/yax405/article/details/43246237

https://blog.csdn.net/liyantianmin/article/details/59485799

https://github.com/ysc/word/blob/master/src/main/java/org/apdplat/word/lucene/ChineseWordAnalyzer.java

https://blog.csdn.net/winnerspring/article/details/37567739

https://my.oschina.net/apdplat/blog/228619

http://www.hankcs.com/program/java/lucene-synonymfilterfactory.html

在Lucene4.6中通过SynonymFilterFactory实现中文同义词非常方便,只需几行代码和一个同义词词典。这个词典还能在Lucene中实现一定程度的拼写纠错,提升搜索体验。在下面这个例子中我们从磁盘载入一个同义词词典,并且对“其实hankcs似好人”这句话进行stream化以供索引,同时还对其中的拼写错误“似->是”做出纠正。

首先是位于./data/synonyms.txt路径下的同义词词典:

我,俺,hankcs似,is,are => 是好人,好心人,热心人

可以看出上面有两种词典格式:

通过,分割的可拓展同义词

比如“我,俺,hankcs”代表着这三个词是同义词,并且任何一个词可以被expand(拓展)为其他三个。如果expand设为false的话,则这三个词都会被统一替换为第一个词,也就是“我”。

通过=>收缩的不可拓展同义词

比如“似,is,are => 是”代表这三个词同义,并且无视expand参数,统一会被替换为“是”

然后是加载代码

package com.hankcs.test; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.core.WhitespaceAnalyzer; import org.apache.lucene.analysis.synonym.SynonymFilterFactory; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.analysis.util.FilesystemResourceLoader; import org.apache.lucene.util.Version; import org.apache.uima.annotator.WhitespaceTokenizer;  import java.io.IOException;import java.io.StringReader; import java.util.HashMap;import java.util.Map;  /** * @author hankcs */ public class TestSynonyms{     private static void displayTokens(TokenStream ts) throws IOException    {         CharTermAttribute termAttr = ts.addAttribute(CharTermAttribute.class);                 OffsetAttribute offsetAttribute = ts.addAttribute(OffsetAttribute.class);                 ts.reset();                 while (ts.incrementToken())        {                     String token = termAttr.toString();                               System.out.print(offsetAttribute.startOffset() + "-" + offsetAttribute.endOffset() + "[" + token + "] ");                 }                 System.out.println();                 ts.end();                 ts.close();     }      public static void main(String[] args) throws Exception    {         String testInput = "其实 hankcs 似 好人";         Version ver = Version.LUCENE_46;         Map filterArgs = new HashMap();         filterArgs.put("luceneMatchVersion", ver.toString());         filterArgs.put("synonyms", "./data/synonyms.txt");         filterArgs.put("expand", "true");         SynonymFilterFactory factory = new SynonymFilterFactory(filterArgs);         factory.inform(new FilesystemResourceLoader());         WhitespaceAnalyzer whitespaceAnalyzer = new WhitespaceAnalyzer(ver);         TokenStream ts = factory.create(whitespaceAnalyzer.tokenStream("someField", testInput));         displayTokens(ts);     } }

输出:

0-2[其实] 3-9[我] 3-9[俺] 3-9[hankcs] 10-11[是] 12-14[好人] 12-14[好心人] 12-14[热心人]

由于 我 俺 hankcs 三个词是同一个意思,所以它们被视为同一个term,并且它们的偏移相同,都是3->9,这个长度取决于原来的词 hankcs 的长度。

https://blog.csdn.net/yax405/article/details/43246237

https://blog.csdn.net/u010366796/article/details/44937025

http://www.voidcn.com/article/p-txqtdabn-bbo.html

http://blog.csdn.net/winnerspring/article/details/37521101

http://www.voidcn.com/article/p-pjrzypvg-bbo.html

http://blog.csdn.net/winnerspring/article/details/37567739

http://blog.csdn.net/hu948162999/article/details/41283597

http://www.voidcn.com/article/p-xrordklc-bah.html

https://iamyida.iteye.com/blog/2197355

https://cloud.tencent.com/info/034aa996312ba4928c57ae831d6acedf.html

lucene 同义词的索引

public interface SynonymEngine {     String[] getSynonyms(String key); }

public class SynonymEngineImpl implements SynonymEngine {          private static HashMap map = new HashMap();     static {         map.put("quick",new String[]{"fast","speedy"});         map.put("jumps",new String[]{"leaps","hops"});         map.put("over",new String[]{"above"});         map.put("lazy",new String[]{"apathetic","sluggish"});         map.put("dog",new String[]{"canine","pooch"});     }     @Override     public String[] getSynonyms(String key) {         // TODO Auto-generated method stub         return map.get(key);     } }

public class SynonymFilter extends TokenFilter {     private SynonymEngine engine;     private CharTermAttribute ct;     private PositionIncrementAttribute pt;     private Stack stack;     private AttributeSource.State current;     protected SynonymFilter(TokenStream input,SynonymEngine engine) {         super(input);         this.engine = engine;         ct = this.addAttribute(CharTermAttribute.class);         pt = this.addAttribute(PositionIncrementAttribute.class);         stack  = new Stack();     }     @Override     public boolean incrementToken() throws IOException {         if(stack.size()>0) {             this.restoreState(current);             String p = stack.pop();             ct.setEmpty();             ct.append(p);             pt.setPositionIncrement(0);             return true;         }         System.out.println("++++++"+ct);         if(!input.incrementToken()) return false;         System.out.println("------"+ct);                  if(addSynonym(ct.toString())) {             current = this.captureState();                      }                                             return true;     }          private boolean addSynonym(String name) {         String[] sa = engine.getSynonyms(name);         if(sa != null && sa.length>0) {             for(String s:sa) {                 stack.push(s);             }             return true;         } else {             return false;         }     } }

public class SynonymAnalyzer extends Analyzer {     private SynonymEngine engine;          public SynonymAnalyzer(SynonymEngine engine) {         this.engine = engine;     }     @Override     public TokenStream tokenStream(String s, Reader reader) {         // TODO Auto-generated method stub         return new SynonymFilter(new StopFilter(Version.LUCENE_35,                 new LowerCaseFilter(Version.LUCENE_35,                         new StandardFilter(Version.LUCENE_35,                                 new StandardTokenizer(Version.LUCENE_35,reader)))                 ,StopAnalyzer.ENGLISH_STOP_WORDS_SET),engine);     } }

public class TestSynonym {     private RAMDirectory directory;     @Test     public void init() {         directory = new RAMDirectory();         SynonymEngine engine = new SynonymEngineImpl();         IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35,new SynonymAnalyzer(engine));         String content = "The quick brown fox jumps over the lazy dog";                  try {             IndexWriter writer = new IndexWriter(directory,config);             Document doc = new Document();             doc.add(new Field("content",content,Field.Store.YES,Field.Index.ANALYZED));             writer.addDocument(doc);             writer.close();                          IndexReader reader = IndexReader.open(directory);             IndexSearcher searcher = new IndexSearcher(reader);             TopDocs docs = searcher.search(new TermQuery(new Term("content","pooch")),10);             for(ScoreDoc sd:docs.scoreDocs) {                 Document d = searcher.doc(sd.doc);                 System.out.println(d.get("content"));             }                      } catch (CorruptIndexException e) {             // TODO Auto-generated catch block             e.printStackTrace();         } catch (LockObtainFailedException e) {             // TODO Auto-generated catch block             e.printStackTrace();         } catch (IOException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }     } }

http://www.itzk.com/b/1109/581983.shtml

Lucene的同义词分析器讲解

这个分析器用SynonymFilter过滤器对StandardAnalyzer类进行封装,当向这个过滤器输入各个项时,会对这些项进行缓冲,并使用栈存储这些项的同义词[code]

public class SynonymFilter extends TokenFilter{

publicstatic final String TOKEN_TYPE_SYNONYM="SYNONYM";

privateStack synonymStack;

privateSynonynEngine engine;

publicSynonymFilter(TokenStream in,SynonymEngine engine){

super(in);

synonymStack=new Stack();//缓存同义词

this.engine=engine;

}

publicTOken next() throws IOException{

if (synonymStack.size()>0){//如何还有当前词的同义词没有输出,则输出

return (Token) synonymStack.pop();

}

Token token=input.next();//读取新词

if (token==null) {

return null;

}

addAliasesToStack(token);//存储新词的同义词

returntoken;

}

private voidaddAliasesToStack(Token token) throws IOException{

String[] synonyms=engine.getSynonyms(token.termText());

if (synonyms==mull) return;

for (int i=0;i

Token synToken=newToken(synonyms[i],token.startOffset(),token.endOffset(),TOKEN_TYPE_SYNONYM);

synToken.setPositionIncrement(0);

synonymStack.push(synToken);

}

}[/code]以下这个接口是关键,可以自由实现,目的是返回s的同义词数组[code]public interface SynonymEngine{

String[] getSynonyms(String s) throws IOException;

}

[/code]对于这个接口要小心使用,在查询时不必列出所有的同义词,如下例[code]Query query=QueryParser.parse("\"foxjumps\"","content",synonymAnalyzer);

Hits hits=searcher.search(query);[/code]是会出错的,找不到任何结果,因为QueryParser不会区别位置增量,所以位置增量为0这一个表明同义的特征无法体现,会将"foxjumps"直接加上同义词解释为"fox jumps hops leaps"

https://stackoverrun.com/cn/q/4705011

我想弄清楚lucene的分析仪是如何工作的? 我的问题是,lucene如何处理同义词?这里的情况是: 我们有一个词和多词

单:富=酒吧 多的话:富巴= foobar的

对于单个的词:

是否Lucene的扩大索引记录或不?我猜如果一个查询有一个像“foo”这样的词,它也会在查询中添加“bar”。我不知道是否索引或不索引?

对于多话:

是否Lucene的扩大查询和索引?例如,如果我们有“富吧”,它是否将foobar添加到索引/查询?

我的第二个问题是:Lucene使用一个标记流并将它们提供给小写过滤器之类的过滤器。我的问题是lucene如何找到多词?比如它是如何发现“foo bar”是一个多词的?

SynonymFilter可任选,保持原有的单词,并添加同义词到的TokenStream中,通过设置keepOrig =真(见SynonymMap.Builder.add())。此行为可能会导致PhraseQueries等问题,请参阅SynonymFilter文档中的第注意事项。

如果您使用相同的Analyzer进行查询和编制索引,那么写入索引的查询和文档当然都会以同样的方式处理。 SynonymFilter与keepOrig设置为true是少数几个Analyzers之一,经常在查询和索引之间不合时宜地应用,但这完全取决于您的实现。

至于如何实施,source code可供您使用。

来源 分享

创建 24 6月. 13 femtoRgon

0

它是如何处理多个同义词的?像“纽约”=“纽约” 沃尔玛=沃尔玛=沃尔玛 ,因为它通过令牌执行过滤令牌。我不知道它是如何找到多个单词的同义词 – Mr.Boy 24 6月. 13

0

有没有你对它的行为感到困惑,或者你想知道实现如何处理令牌流?如果是后者,那就是为什么我提供了链接到源代码的原因。如果前者贪婪地搜索最长匹配,它可以从给定的位置(也就是说,如果你有规则'foo' - >'bar','foo bar' - >'foobar',那么'foo bar'会变成'foobar',而不是'bar bar')。我不相信它支持'wal mart = wal-mart = walmart'这样的东西(同义词规则有一个输入和一个输出)。如果有什么特别的要问的话,继续。 – femtoRgon 24 6月. 13

0

我的问题是它如何处理令牌流?因为我猜同义词过滤器一个接一个地得到令牌,而且它是无状态的。例如,如果当前令牌为“新”,它如何检查下一个令牌以查看它是否是“约克”?

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:如何安全存储口令?了解下Hash加盐的原理
下一篇:我的第一个python web开发框架(15)——公司介绍编辑功能
相关文章