利用Lucene实现全文检索

利用Lucene实现全文检索

全文检索:

将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而到达搜索相对较快的目的。这部分从非结构化数据中提取出来然后重新组织的信息,称之为索引。

Lucene创建索引的过程

  1. 获得文档
  2. 创建文档对象
  3. 分析文档(分词)
  4. 创建索引

Field

  • 每个文档对象可以有多个Field,不同的文档对象可以有不同的域,同一个文档对象可以有相同的Field(域值和域名都相同)
  • 每个文档都有唯一的编号,就是文档id

分析文档

  • 将原始文档创建为包含域(Field)的文档(document),需要再对域中的内容进行分析。分析的过程是经过对原始文档提取单词,将字母转为小写,去除表点符号,取出停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个一个的单词。
  • 每个单词叫做一个Term,不同域中拆分出来的相同的单词是不同的term。term中包含两部分,一是文档的域名,另一部分是单词的内容。

创建索引

  • 索引库中有两部分,一是文档,二是生成的索引
  • 创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构,也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大。
    /**
     * 创建索引
     * @throws IOException
     */
   @Test
    public void test() throws IOException {
        //创建一个indexwrite对象
        //指定索引存放位置
        //指定一个分词器,对文档内容进行分析
        Directory directory = FSDirectory.open(Paths.get("D:\\Apicture\\index"));
        Analyzer analyzer = new StandardAnalyzer();
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        IndexWriter indexWriter = new IndexWriter(directory,config);
        //创建Field对象,将Field添加到document对象中
        File f = new File("D:\\Apicture\\source");
        File[] listFiles = f.listFiles();
        for(File file : listFiles) {
            Document document = new Document();
            //4个域
            //文件名称
            String fileName = file.getName();
            Field nameField = new TextField("fileName",fileName, Field.Store.YES);
            //文件大小
            long fileSize = FileUtils.sizeOf(file);
            Field sizeField = new LongPoint("sizeField",fileSize);
            //文件路径
            String filePath = file.getPath();
            Field pathField = new StoredField("filePath",filePath);
            //文件内容
            String fileContent = FileUtils.readFileToString(file);
            Field contentField = new TextField("fileContent",fileContent, Field.Store.NO);

            document.add(nameField);
            document.add(sizeField);
            document.add(pathField);
            document.add(contentField);
            //使用indexwriter将document对象写入索引库.此过程创建索引,并将document对象写入索引库
            indexWriter.addDocument(document);
        }
        //关闭IndexWriter对象
        indexWriter.close();
    }

查询索引

用户输入查询关键字执行搜索之前需要先构建一个查询对象,查询对象中可以指定要搜索的Field文档域,查询关键字等,查询对象会生成具体的查询语法。例如:搜索语法为"fileName:lucene"表示在索引上查找域为fileName,并且关键字为lucene的term,并根据trem找到文档id列表。

    /**
     * 执行查询
     * @throws IOException
     */
    @Test
    public void test() throws IOException {
        //创建一个Directory对象,,也就是索引库存放的位置
        Directory directory = FSDirectory.open(Paths.get("D:\\Apicture\\index"));
        //创建一个IndexReader对象,需要指定Directory
        IndexReader indexReader = DirectoryReader.open(directory);
        //创建一个indexSearcher对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //创建一个TermQuery对象,指定查询的域和查询的关键词
        Query query = new TermQuery(new Term("fileName","hello.txt"));
        //执行查询
        TopDocs topDocs = indexSearcher.search(query,2);
        //遍历查询结果
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(ScoreDoc scoreDoc : scoreDocs) {
            //文件id
            int doc = scoreDoc.doc;
            Document document = indexReader.document(doc);
            //文件名称
            String fileName = document.get("fileName");
            System.out.println(fileName);
            //文件路径
            String filePath = document.get("filePath");
            System.out.println(filePath);
            //文件大小,(没有存储,会打印null)
            String fileSize = document.get("fileSize");
            System.out.println(fileSize);
        }
        //关流
        indexReader.close();
    }

支持中文分词

  • Lucene自带的标准分词器StandardAnalyzer对中文一个字一个字地进行分词。
  • 第三方分词器:IK分词器
  • 注意点: 高版本的Lucene使用IK Analyzer时会出现冲突,解决办法是重新写IKAnalyzer的jar包下org.wltea.analyzer.lucene下的IKANalyzer.java和IKTokenizer.java
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;

/**
 * 重写IKAnalyzer
 */
public final class IKAnalyzer extends Analyzer {

    private boolean useSmart;

    public boolean useSmart() {
        return useSmart;
    }

    public void setUseSmart(boolean useSmart) {
        this.useSmart = useSmart;
    }

    public IKAnalyzer() {
        this(false);
    }

    public IKAnalyzer(boolean useSmart) {
        super();
        this.useSmart = useSmart;
    }

    @Override
    protected TokenStreamComponents createComponents(String fieldName) {
        Tokenizer _IKTokenizer = new IKTokenizer(this.useSmart());
        return new TokenStreamComponents(_IKTokenizer);
    }

}
import java.io.IOException;

import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;

import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

/**
 * 重写IKTokenizer
 */
public final class IKTokenizer extends Tokenizer {
    private IKSegmenter _IKImplement;
    private final CharTermAttribute termAtt;
    private final OffsetAttribute offsetAtt;
    private final TypeAttribute typeAtt;
    private int endPosition;


    public IKTokenizer(boolean useSmart) {
        super();
        offsetAtt = addAttribute(OffsetAttribute.class);
        termAtt = addAttribute(CharTermAttribute.class);
        typeAtt = addAttribute(TypeAttribute.class);
        _IKImplement = new IKSegmenter(input, useSmart);
    }

    @Override
    public boolean incrementToken() throws IOException {
        clearAttributes();
        Lexeme nextLexeme = _IKImplement.next();
        if (nextLexeme != null) {
            termAtt.append(nextLexeme.getLexemeText());

            termAtt.setLength(nextLexeme.getLength());

            offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());

            endPosition = nextLexeme.getEndPosition();

            typeAtt.setType(nextLexeme.getLexemeTypeString());

            return true;
        }
        return false;
    }

    @Override
    public void reset() throws IOException {
        super.reset();
        _IKImplement.reset(input);
    }
    @Override
    public final void end() {
        int finalOffset = correctOffset(this.endPosition);
        offsetAtt.setOffset(finalOffset, finalOffset);
    }
}

在创建索引的时候只需要将StandardAnalyzer更改为自己写的IKAnalyzer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值