代码改变世界

Lucene学习笔记

2013-12-02 10:03  hduhans  阅读(543)  评论(0)    收藏  举报

  Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。它是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎。

  lucene-3.5.0.zip下载:http://pan.baidu.com/s/11lKHu

  索引和查看必备工具lukeall-3.5.0.jar下载:http://pan.baidu.com/s/1vgjso 

一、Lucene的基本使用

1、选项说明

   1) 存储域选项Field.Store.YES或Field.Store.NO,表示是否存储原数据信息,一般id存储,文章内容不存储;

   2) 索引选项Field.Index:

    ① Field.Index.ANALYZED: 进行分词和索引,适用于标题、内容等;

    ② Field.Index.NOT_ANALYZED: 进行索引,但是不进行分词,如果是身份证号,姓名,ID等,适用于精确搜索

    ③ Field.Index.ANALYZED_NOT_NORMS: 进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和权值等信息,norms保存了排序的信息;

         ④ Field.Index.NOT_ANALYZED_NOT_NORMS: 既不进行分词也不存储norms信息

    ⑤ Field.Index.NO: 不进行索引

2、创建索引常用方法

   1) 创建索引:① 创建Directory  ② 创建IndexWriter  ③ 创建Document对象  ④ 为Document添加Field  ⑤ 通过IndexWriter添加文档到索引中  ⑥ 关闭writer

   2) 搜索索引:① 创建Directory  ② 创建IndexReader  ③ 根据IndexReader创建IndexSearcher  ④ 创建搜索的Query  ⑤ 根据searcher搜索并且返回TopDocs  ⑥ 根据TopDocs获取ScoreDoc对象  ⑦ 根据searcher和ScoreDoc获取具体的Document对象  ⑧ 根据Document对象获取需要的值  ⑨ 关闭reader

   3) 一个简单示例(示例源码下载),读取文件夹D:\lucene\index01中的文本文档内容并创建索引(需导入包lucene-core-3.5.0.jar)。

package org.itat.test;

import java.io.File;
import java.io.FileReader;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

public class HelloLucene {

    /**
     * 建立索引
     */
    public void index() {
        IndexWriter writer=null;
        try {
            //1、创建Directory
            //Directory directory = new RAMDirectory();  //建立在内存中
            Directory directory = FSDirectory.open(new File("D:/lucene/index01"));
            //2、创建IndexWriter
            IndexWriterConfig iwc =new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35));
            writer = new IndexWriter(directory, iwc);
            //3、创建Document对象
            Document doc = null;
            //4、为Document添加Field
            File f=new File("D:/lucene/data");
            for(File file:f.listFiles()){
                doc = new Document();
                doc.add(new Field("content",new FileReader(file)));
                doc.add(new Field("filename",file.getName(),Field.Store.YES,Field.Index.NOT_ANALYZED));
                doc.add(new Field("path",file.getAbsolutePath(),Field.Store.YES,Field.Index.NOT_ANALYZED));
                //5、通过IndexWriter添加文档到索引中
                writer.addDocument(doc);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(writer!=null)
                try {
                    writer.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }
    
    /**
     * 搜索
     */
    public void searcher(){
        try {
            //1、创建Directory
            Directory directory = FSDirectory.open(new File("D:/lucene/index01"));
            //2、创建IndexReader
            IndexReader reader=IndexReader.open(directory);
            //3、根据IndexReader创建IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);
            //4、创建搜索的Query
            //创建parser来确定要搜索文件的内容,第二个参数表示搜索的域
            QueryParser parser=new QueryParser(Version.LUCENE_35,"content",new StandardAnalyzer(Version.LUCENE_35));
            //创建query,表示搜索域为content中包含java的文档
            Query query=parser.parse("公开");
            //5、根据searcher搜索并且返回TopDocs
            TopDocs tds = searcher.search(query, 10);
            //6、根据TopDocs获取ScoreDoc对象
            ScoreDoc[] sds = tds.scoreDocs;
            for(ScoreDoc sd:sds){
                //7、根据searcher和ScoreDoc获取具体的Document对象
                Document d = searcher.doc(sd.doc);
                //8、根据Document对象获取需要的值
                System.out.println(d.get("filename")+"["+d.get("path")+"]");
            }
            //9、关闭reader
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}
View Code

   4) 删除文档,使用IndexWriter.deleteDocuments(),文档删除后,会存储域回收站中,可用下面的方法进行恢复。

writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35,new StandardAnalyzer(Version.LUCENE_35)));
writer.deleteDocuments(new Term("id", "1"));
//可用forceMergeDeletes()方法强制清空回收站
//writer.forceMergeDeletes();
View Code

     5) 恢复回收站文档,使用IndexReader.undeleteAll(),需设置readonly=false。 

IndexReader reader = IndexReader.open(directory,false);
reader.undeleteAll();
reader.close();
View Code

   6) 设置权值,在建立索引时使用Document..setBoost(1.0f),其中数值为浮点型,需跟一个字母f。权值越大搜索结果排序越靠前。

   7) 数字和日期进行索引。

//对数字进行索引
doc.add(new NumericField("attach",Field.Store.YES,true).setIntValue(888));
//对日期进行索引
doc.add(new NumericField("attach",Field.Store.YES,true).setLongValue(new java.util.Date().getTime()));
View Code

3、IndexReader的实时更新设置。IndexReader每次打开消耗的资源较大,因此在频繁查询的情况下,建议IndexReader使用单例模式,一个项目周期使用一个reader,但因此会带来一个问题,就是当IndexReader打开后,搜索结果不会随着索引的改变而改变,须判断IndexReader是否被改变,使用方法 IndexReader.openIfChanged(),通常写法如下:

//声明全局reader变量,一次打开,多次使用
private IndexReader reader = null;
//获取IndexSearch对象,根据方法openIfChanged判断reader是否更新,使得查询的结果总是最新的
public IndexSearcher getSearcher() {
    try {
        if (reader == null) {
            reader = IndexReader.open(directory);
        } else {
            IndexReader ir = IndexReader.openIfChanged(reader);
            if (ir != null){
                reader.close();
                reader = ir;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return new IndexSearcher(reader);
}
View Code

注:有些程序会对IndexWrite也使用单例模式,此时,writer必须手动提交,即调用writer.commit()

4、常用搜索方法(示例源码下载)。

   1) TermQuery 精确搜索。例:Query query = new TermQuery(new Term("name", "hans"));   --查询name=hans的记录,必须相等,不是包含

   2) TermRangeQuery 字符串范围搜索。例:Query query = new TermRangeQuery("name", "a", "h", true, true);   --查询name从a开头到h开头的记录,最后两个true表示包含a和h

   3) NumericRangeQuery 数字范围搜索。例:Query query = NumericRangeQuery.newIntRange("attach", 1, 5, true, true);    --attach建立索引时保存是数字类型,查询1<=attach<=5的记录

   4) PrefixQuery 前缀搜索。例:Query query = new PrefixQuery(new Term("name", "han"));   --查询name以"han"字符串开头的记录

   5) WildcardQuery 通配符搜索。例:Query query = new WildcardQuery(new Term("email","*@pccpa.cn"));    --查询email以@pccpa.cn结尾的所有记录

   6) BooleanQuery 多条件搜索。本例查询name!=hanlong且content包含pccpa单词且email以"@qq.com"结尾的记录。

//多条件查询 示例:查询name!=hanlong 且 content包含pccpa单词 且 emal以"@qq.com"结尾 的数据
BooleanQuery query = new BooleanQuery();
query.add(new TermQuery(new Term("name", "hanlong")),Occur.MUST_NOT);
query.add(new TermQuery(new Term("content", "pccpa")),Occur.MUST);
query.add(new WildcardQuery(new Term("email","*@qq.com")),Occur.MUST);
//Occur条件说明:1) Occur.MUST 条件必须成立   2) Occur.SHOULD 条件可以成立,也可以不成立    3) Occur.MUST_NOT 条件必须不成立
View Code

   7) PhraseQuery 短语搜索,可以查询两个间隔一定数目的单词的记录,对中文分词不起作用,性能开销较大,不太建议使用。

//查询content中单词"welcome"和单词"pccpa"之间间隔<=4个单词的记录
PhraseQuery query = new PhraseQuery();
query.setSlop(4);
query.add(new Term("content", "welcome"));
query.add(new Term("content", "pccpa"));
View Code

   8) FuzzyQuery 模糊搜索。例:Query query = new FuzzyQuery(new Term("name", "make"));   --对name进行模糊搜索

   9) QueryParser 强大搜索。QueryParser搜索字符串格式说明如下:

hans 默认域包含hans
hans pccpa 或 hans OR pccpa 默认域包含hans或pccpa
+name:hanlong +content:pccpa  或 name:hanlong AND content:pccpa name=hanlong并且content包含pccpa                                              
name:hanlong  name=hanlong                                            
content:pccpa -name:hanlong  或 content:pccpa AND NOT name:hanlong content包含pccpa且name!=hanlong                                       
(pccpa OR welcome) AND name:hanlong 默认域包含pccpa或welcome并且name=hanlong
content:"welcome to pccpa"  默认域包含字符串"welcome to pccpa"
content:"welcome pccpa"~3  content域中单词"welcome"与单词"pccpa"间隔距离小于等于3 
han*  默认域是han开头 
id:[1 TO 3]  ID从1到3 
public void searchByQueryParse(Query query,int num) throws Exception {
    IndexSearcher searcher = getSearcher();
    TopDocs tds = searcher.search(query, num);
    System.out.println("一共查询了:" + tds.totalHits);
    for(ScoreDoc sd:tds.scoreDocs) {
        Document doc = searcher.doc(sd.doc);
        System.out.println("id="+doc.get("id")+",name="+doc.get("name")+",email="+doc.get("email")+",attach="+doc.get("attach"));
    }
    searcher.close();
}

@Test
public void testSearchByQueryparse() throws Exception {
    //1、创建QueryParse对象,默认搜索域为content
    QueryParser parser = new QueryParser(Version.LUCENE_35,"content",new StandardAnalyzer(Version.LUCENE_35));
    /********************************************************************************/
    //可以改变默认空格操作符为AND,默认为OR
    parser.setDefaultOperator(Operator.AND);
    //正常搜索 如默认搜索符为OR,则搜索content域包含welcome或包含pccpa的记录,如默认搜索符为AND,则搜索content域既包含welcome又包含pccpa的记录
    //当默认为OR时,"welcome pccpa"相当于"welcome OR pccpa"
    //当默认为AND时,"welcome pccpa"相当于"welcome AND pccpa"或"+welcome +pccpa"
    Query query = parser.parse("welcome pccpa");  
    /********************************************************************************/
    /********************************************************************************/
    //可以通过冒号改变在搜索字符串中改变搜索域  此处搜索name=hanlong的记录
    query = parser.parse("name:hanlong");
    /********************************************************************************/
    /********************************************************************************/
    //开启首位为*的通配符匹配,默认关闭,因为首位为*比较消耗资源
    parser.setAllowLeadingWildcard(true);   
    //可以使用通配符 使用*和?
    query = parser.parse("email:*@pccpa.cn");
    /********************************************************************************/
    /********************************************************************************/
    //多条件查找,查找content中包含pccpa,且name中不包含hanlong且id不等于6的记录
    query = parser.parse("content:pccpa -name:hanlong -id:6");
    //上下两句相等
    query = parser.parse("content:pccpa AND NOT name:hanlong AND NOT id:6");
    /********************************************************************************/
    /********************************************************************************/
    //查询默认字段中包含American或China,但一定包含country的记录
    query = parser.parse("(American OR China) AND country");
    /********************************************************************************/
    /********************************************************************************/
    //区间范围查找只能匹配字符串 不能匹配数字范围 匹配数字需自己扩展
    //区间范围查找,查找id从1到3的记录(包含1和3) 闭区间 TO必须大写
    query = parser.parse("id:[1 TO 3]");
    //区间范围查找,查找id从1到3的记录(不包含1和3) 开区间
    query = parser.parse("id:{1 TO 3}");
    /********************************************************************************/
    /********************************************************************************/
    //查询必须包含"welcome pccpa"字符的记录,注意使用""
    query = parser.parse("content:\"welcome pccpa\"");
    /********************************************************************************/
    /********************************************************************************/
    //查询content中"welcome"和"pccpa"之间距离小于等于4的记录
    query = parser.parse("content:\"welcome pccpa\"~4");
    /********************************************************************************/
    /********************************************************************************/
    //模糊查询 查询content中包含"welcome"的记录
    //query = parser.parse("content:welcome~");
    /********************************************************************************/
    su.searchByQueryParse(query, 10);
}
View Code

5、分页搜索。

  方法一:使用再查找的办法,即每次分页都查询所有数据再从中挑选符合自己页数要求的记录。Lucene官方强调由于搜索速度足够快,因此可以使用再分页查找。

    方法二:lucene3.5版本后支持searchAfter,可根据上一页的最后一项查询PageSize项。

/**
 * 分页搜索 通过再查询
 * @param pageIndex
 * @param pageSize
 */
public void searcherPaging(int pageIndex,int pageSize){
    try {
        Directory directory = FSDirectory.open(new File("D:/lucene/index01"));
        IndexReader reader=IndexReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(reader);
        QueryParser parser=new QueryParser(Version.LUCENE_35,"content",new StandardAnalyzer(Version.LUCENE_35));
        Query query=parser.parse("pccpa");
        TopDocs tds = searcher.search(query, pageIndex*pageSize);
        ScoreDoc[] sds = tds.scoreDocs;
        int start = (pageIndex-1)*pageSize;
        int end = Math.min(pageIndex*pageSize,sds.length);
        for(int i=start;i<end;i++){
            Document d = searcher.doc(sds[i].doc);
            System.out.println(d.get("filename")+"["+d.get("path")+"]");
        }
        reader.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * 分页搜索 lucene3.5以后版本支持searchAfter(ScoreDoc after, Query query, int n),其中after为上一个页面的最后一项,第一页则after为null
 * 关键步骤
 * int lastIndex=(pageIndex-1)*pageSize-1;
 * if(lastIndex+1>tds.totalHits) return;
 * ScoreDoc lastDoc = lastIndex==-1?null:sds[lastIndex];
 * tds = searcher.searchAfter(lastDoc, query, pageSize);
 * @param pageIndex
 * @param pageSize
 */
public void searcherPagingByAfter(int pageIndex,int pageSize){
    try {
        Directory directory = FSDirectory.open(new File("D:/lucene/index01"));
        IndexReader reader=IndexReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(reader);
        QueryParser parser=new QueryParser(Version.LUCENE_35,"content",new StandardAnalyzer(Version.LUCENE_35));
        Query query=parser.parse("pccpa");
        TopDocs tds = searcher.search(query, pageIndex*pageSize);
        ScoreDoc[] sds = tds.scoreDocs;
        int lastIndex=(pageIndex-1)*pageSize-1;
        //超出下标  返回
        if(lastIndex+1>tds.totalHits) return;
        ScoreDoc after = lastIndex==-1?null:sds[lastIndex];
        tds = searcher.searchAfter(after, query, pageSize);
        for(ScoreDoc sd:tds.scoreDocs){
            Document d = searcher.doc(sd.doc);
            System.out.println(d.get("filename")+"["+d.get("path")+"]");
        }
        reader.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
View Code

6、通过TokenStream显示分词结果(使用CharTremAttribute)。每个分词器都要经过两个工序Tokenizer和TokenFilter,其中Tokenizer主要作用将词分开,将数据划分为不同的语汇单元;而,TokenFilter主要作用是过滤没有意义的语汇单元。

    //显示所有的分词信息
public static void displayAllToken(String str,Analyzer a){
    try {
        TokenStream stream = a.tokenStream("content", new StringReader(str));
        //位置增量的属性,存储语汇单元之间的距离
        PositionIncrementAttribute pia = stream.addAttribute(PositionIncrementAttribute.class);
        //每个语汇单元的位置偏移量
        OffsetAttribute oa = stream.addAttribute(OffsetAttribute.class);
        //存储每一个语汇单元的信息(分词单元信息)
        CharTermAttribute cta = stream.addAttribute(CharTermAttribute.class);
        //使用的分词器的类型信息
        TypeAttribute ta = stream.addAttribute(TypeAttribute.class);
        for(;stream.incrementToken();){
            System.out.print(pia.getPositionIncrement()+":");
            System.out.println(cta+"["+oa.startOffset()+"-"+oa.endOffset()+"] type:"+ta.type());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    
}

//测试
@Test
public void test02(){
    Analyzer a1 = new StandardAnalyzer(Version.LUCENE_35);
    Analyzer a2 = new StopAnalyzer(Version.LUCENE_35);
    Analyzer a3 = new SimpleAnalyzer(Version.LUCENE_35);
    Analyzer a4 = new WhitespaceAnalyzer(Version.LUCENE_35);
    String txt = "how are you thank you";
    
    AnalyzerUtils.displayAllToken(txt, a1);
    System.out.println("--------------------------------");
    AnalyzerUtils.displayAllToken(txt, a2);
    System.out.println("--------------------------------");
    AnalyzerUtils.displayAllToken(txt, a3);
    System.out.println("--------------------------------");
    AnalyzerUtils.displayAllToken(txt, a4);
}
View Code

7、自定义分词器,一个小例子MyStopAnalyzer

//文件MyStopAnalyzer.java
package org.lucene.util;

import java.io.Reader;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.LetterTokenizer;
import org.apache.lucene.analysis.LowerCaseFilter;
import org.apache.lucene.analysis.StopAnalyzer;
import org.apache.lucene.analysis.StopFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.util.Version;

public class MyStopAnalyzer extends Analyzer {

    private Set stops;
    public MyStopAnalyzer(String[] sws){
        stops = StopFilter.makeStopSet(Version.LUCENE_35, sws,true);
        //添加默认停用词
        stops.addAll(StopAnalyzer.ENGLISH_STOP_WORDS_SET);
    }
    
    public MyStopAnalyzer(){
        stops = StopAnalyzer.ENGLISH_STOP_WORDS_SET;
    }
    
    @Override
    public TokenStream tokenStream(String fieldName, Reader reader) {
        return new StopFilter(Version.LUCENE_35, 
               new LowerCaseFilter(Version.LUCENE_35,
               new LetterTokenizer(Version.LUCENE_35, reader)), stops);
    }

}
View Code

8、中文分词器。中文分词器最重要最核心的就是中文词库,市面上有很多中文分词器可供使用,主要的有如下几种:

   ① Paoding:庖丁解牛分词器, 已经停止更新,不太推荐使用;

   ② mmseg:使用了搜狗的词库(可自定义扩展词库),推荐使用

   1) mmseg4j-1.8.5.zip下载;

   2) data文件夹存放中文词库,其中words-my.dic文件可以自定义词库;

   3) 使用时有两个包可供选择,区别是一个带中文词库,另一个不带中文词库,需初始化时指定词库路径,具体如下:

       a、mmseg4j-all-1.8.5.jar 不带中文词库,初始化时需指定词库路径,如:Analyzer a = new MMSegAnalyzer(new File("D:\\mmseg4j-1.8.5\\data"));

       b、mmseg4j-all-1.8.5-with-dic.jar 包中自带中文词库,可直接使用;

9、一个自定义同义词分词器的良好设计方案(示例源码下载,基于MMseg中文分词算法)

   ① 定义我的同义词类MySameAnalyzer,继承Analyzer

package org.lucene.util;

import java.io.Reader;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;

import com.chenlb.mmseg4j.Dictionary;
import com.chenlb.mmseg4j.MaxWordSeg;
import com.chenlb.mmseg4j.analysis.MMSegTokenizer;

//自定义我的同义词MySameAnalyzer,必须继承Analyer
public class MySameAnalyzer extends Analyzer {
    private SamewordContext samewordContext;
    
    public MySameAnalyzer(SamewordContext swc) {
        samewordContext = swc;
    }
    
    //继承并实现获取分词流的方法,新增定义我的同义词处理类MySameTokenFilter,将经过MMseg中文分词得到的语汇单元再次进行加工处理
    @Override
    public TokenStream tokenStream(String fieldName, Reader reader) {
        Dictionary dic = Dictionary.getInstance();
        return new MySameTokenFilter(
               new MMSegTokenizer(
               new MaxWordSeg(dic), reader),samewordContext);
    }

}
View Code 

   ② 定义我的同义词处理类MySameTokenFilter

package org.lucene.util;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.util.AttributeSource;

public class MySameTokenFilter extends TokenFilter {
    private CharTermAttribute cta = null;
    private PositionIncrementAttribute pia = null;
    private AttributeSource.State current;
    private Stack<String> sames = null;
    private SamewordContext samewordContext;
    
    protected MySameTokenFilter(TokenStream input,SamewordContext samewordContext) {
        super(input);
        cta=this.addAttribute(CharTermAttribute.class);
        pia = this.addAttribute(PositionIncrementAttribute.class);
        sames = new Stack<String>();
        this.samewordContext = samewordContext;
    }

    //这里的实现思路是,对于每一个语汇单元先匹配是否存在同义词,若存在,则保存当前的语汇单元状态,等处理跳转到下一个语汇单元的时候,然后将同义词插入语汇单元
    //并返回上一个语汇单元处理时状态
    @Override
    public boolean incrementToken() throws IOException {
        if(sames.size()>0){
            //元素出栈,并获取同义词
            String str = sames.pop();
            restoreState(current);
            //还原状态
            cta.setEmpty();
            cta.append(str);
            //设置位置0
            pia.setPositionIncrement(0);
            return true;
        }
        
        if(!input.incrementToken()) return false;
        
        //获取同义词并进行处理
        if(getSameWords(cta.toString())){
            //如果找到同义词,先保存当前状态
            current = captureState();
        }
        return true;
    }

    //这里自定义了一个同义词获取接口类SamewordContext
    private boolean getSameWords(String name) {
        String[] sws = samewordContext.getSameWords(name);
        if(sws!=null){
            //找到同义词,则将同义词压入栈中
            for (String s : sws) {
                sames.push(s);
            }
            return true;
        }
        return false;
    }
    
}
View Code

   ③ 定义同义词接口类SamewordContext,可供实现不同的同义词处理

package org.lucene.util;

//自定义接口,想扩展自己的分词器只需实现该接口
public interface SamewordContext {
    
    //获取同义词的方法
    public String[] getSameWords(String name);
    
}
View Code

   ④ 定义同义词接口实现类SimpleSameWordContext

package org.lucene.util;

import java.util.HashMap;
import java.util.Map;

public class SimpleSameWordContext implements SamewordContext {
    private Map<String, String[]> maps = new HashMap<String, String[]>();
    
    public SimpleSameWordContext() {
        maps.put("中国", new String[]{"天朝","大陆"});
        maps.put("学生", new String[]{"读书人"});
    }
    
    @Override
    public String[] getSameWords(String name) {
        return maps.get(name);
    }

}
View Code