Lucene4.3.1拼写检查SpellChecker源码解析

概述

SpellChecker包含在Lucene的扩展包lucene-suggest中,可以用来对输入的检索内容进行校正,利用SpellChecker可以很方便的实现拼写检查功能,但是检查的效果需要开发者进行调整、优化。

构建SpellChecker

构建SpellChecker需要三个参数

  • org.apache.lucene.store.Directory:拼写检查索引存放目录,如果目录不存在会创建。
  • org.apache.lucene.search.spell.StringDistance:字符串距离接口,用于计算两个字符串之间的距离,默认为org.apache.lucene.search.spell.LevensteinDistance。
  • java.util.Comparator<org.apache.lucene.search.spell.SuggestWord>:建议词比较器,用于对结果进行排序,默认为org.apache.lucene.search.spell.SuggestWordScoreComparator。
  public SpellChecker(Directory spellIndex, StringDistance sd) throws IOException {
    this(spellIndex, sd, SuggestWordQueue.DEFAULT_COMPARATOR);
  }
  
  public SpellChecker(Directory spellIndex) throws IOException {
    this(spellIndex, new LevensteinDistance());
  }

  public SpellChecker(Directory spellIndex, StringDistance sd, Comparator<SuggestWord> comparator) throws IOException {
    setSpellIndex(spellIndex);
    setStringDistance(sd);
    this.comparator = comparator;
  }

构建过程主要在setSpellIndex(spellIndex)。

public void setSpellIndex(Directory spellIndexDir) throws IOException {
    //获取索引修改锁
    synchronized (modifyCurrentIndexLock) {
      //确保索引可打开
      ensureOpen();
      //如果目录下不存在索引则,通过IndexWriter关闭可能存在的资源
      if (!DirectoryReader.indexExists(spellIndexDir)) {
          IndexWriter writer = new IndexWriter(spellIndexDir,
            new IndexWriterConfig(Version.LUCENE_CURRENT,
                null));
          writer.close();
      }
      //交换searcher
      swapSearcher(spellIndexDir);
    }
  }

 

  private void swapSearcher(final Directory dir) throws IOException {
    //创建新的IndexSearcher
    final IndexSearcher indexSearcher = createSearcher(dir);
    //获取searcher锁
    synchronized (searcherLock) {
      if(closed){
        indexSearcher.getIndexReader().close();
        throw new AlreadyClosedException("Spellchecker has been closed");
      }
      //如果searcher不为空,关闭reader
      if (searcher != null) {
        searcher.getIndexReader().close();
      }
      //完成交换
      searcher = indexSearcher;
      this.spellIndex = dir;
    }
  }

 

数据来源

SpellChecker通过实现org.apache.lucene.search.spell.Directory接口支持多种数据来源。该接口将数据来源抽象为一个字典,getWordsIterator方法返回字典中单词的迭代器。

public interface Dictionary {

  /**
   * Return all words present in the dictionary
   * @return Iterator
   */
  BytesRefIterator getWordsIterator() throws IOException;
}

Lucene提供了以下四种数据来源。

加载数据

SpellChecker通过indexDictionary方法从数据源加载数据。

  public final void indexDictionary(Dictionary dict, IndexWriterConfig config, boolean fullMerge) throws IOException {
	//获取索引修改锁
    synchronized (modifyCurrentIndexLock) {
	  //确保索引可打开
      ensureOpen();
      final Directory dir = this.spellIndex;
      final IndexWriter writer = new IndexWriter(dir, config);
      IndexSearcher indexSearcher = obtainSearcher();
      final List<TermsEnum> termsEnums = new ArrayList<TermsEnum>();
	  //查询原本已存在的索引数据
      final IndexReader reader = searcher.getIndexReader();
	  //如果存在数据则添加到termsEnums中
      if (reader.maxDoc() > 0) {
        for (final AtomicReaderContext ctx : reader.leaves()) {
          Terms terms = ctx.reader().terms(F_WORD);
          if (terms != null)
            termsEnums.add(terms.iterator(null));
        }
      }
      
      boolean isEmpty = termsEnums.isEmpty();

      try {
		//从词典接口获取单词迭代器
        BytesRefIterator iter = dict.getWordsIterator();
        BytesRef currentTerm;
        //遍历
        terms: while ((currentTerm = iter.next()) != null) {
  
          String word = currentTerm.utf8ToString();
          int len = word.length();
		  //单词长度小于3忽略
          if (len < 3) {
            continue; // too short we bail but "too long" is fine...
          }
		  //精确查找,如果索引中已存在,则忽略
          if (!isEmpty) {
            for (TermsEnum te : termsEnums) {
              if (te.seekExact(currentTerm, false)) {
                continue terms;
              }
            }
          }
  
          //创建Document,写入索引
          Document doc = createDocument(word, getMin(len), getMax(len));
          writer.addDocument(doc);
        }
      } finally {
		//遍历结束释放searcher
        releaseSearcher(indexSearcher);
      }
      if (fullMerge) {
		//合并为一个段
        writer.forceMerge(1);
      }
      //关闭writer
      writer.close();
      //交换searcher
      swapSearcher(dir);
    }
  }

检查建议

SpellChecker通过suggestSimilar方法进行检查建议,建议有以下三种模式:

public enum SuggestMode {
  //只对索引中没有的词生成建议,默认为此模式
  SUGGEST_WHEN_NOT_IN_INDEX,
  //只返回比搜索词频率相同或更高的词
  SUGGEST_MORE_POPULAR,
  //总是进行搜索建议,但是也可能受其他参数影响
  SUGGEST_ALWAYS
}

suggestSimilar需要六个参数

  • word:想要一个拼写检查的单词。
  • numSug:建议词数量。
  • ir:索引的IndexReader,可以为null。
  • field:索引中的字段,如果不为空,建议的单词仅限于该字段的单词。
  • suggestMode:建议模式,如果ir或field为空,则会被重置为SuggestMode.SUGGEST_ALWAYS,默认为SuggestMode.SUGGEST_WHEN_NOT_IN_INDEX。
  • accuracy:建议词的最低分数即精确度,低于次分数的结果会被过滤,默认为0.5。

待完成。。。。

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值