Lucene小记(入门篇),索引创建、更新、删除、查找等操作。

发布时间:2016-06-07 21:40:43 作者:JIALY 602次浏览

      虽然使用Lucene已经有一段时间了,但是仍不敢妄加评论Lucene的好与坏,毕竟目前接触的全文索引技术只有Lucene。而且Lucene并不是真正意义上的引擎,只算的上是Java开发的全文索引工具包。与传统的数据库查询对比,全文索引技术更具有优势。Lucene不仅可以对磁盘文件进行索引,也可以对数据库记录进行索引,并且支持的索引文件格式多种多样(结合其他的文本处理工具)。言归正传,本文将为读者具体讲解Lucene的基本使用,入门级。

      本文Lucene使用的版本为4.0.0


    1.建索引

    索引创建原理大致分为以下几步:

    • 分词,将原文档传给分词组件进行分词,得到词元。

    • 词元处理,将词元传给语言处理组件进行一些语言处理,例如:变小写,转词根。

    • 索引,对处理后的词元建立词典。例如,词‘中国’出现在ID257的文档中,出现频率分别为153次。

    具体实现如下:

      创建索引时需要两个目录,一个是索引文件的存放目录(indexPath),一个是待索引的文件目录(docsPath)。在创建索引时,我们首先要加载或创建索引目录。常用的目录创建方式有两种:

      1.通过RAMDirectory()类创建一个内存目录,内存目录优点是速度快,缺点是程序退出后索引目录数据就会丢失。

    RAMDirectory directory = new RAMDirectory();

      2.通过FSDirectory类加载一个文件目录,该方式创建的索引数据保存在磁盘上,不会因为程序的退出而消失。下文都是针对FSDirectory方式来讲解Lucene的基本使用。 

    Directory dir = FSDirectory.open(new File(indexPath));

      然后通过IndexWriter对象来创建和维护索引。Analyzer为索引分词器,主要是对获取的文本进行分词操作。由于Lucene是由外国人开发的,所以本身对中文的分词效果不是很好。由于中文不像英文那样,词与词之间有明显的分隔符(英文一般以空格区分),所以中文分词在实现上就比英文分词复杂困难的多,而且歧义识别和新词识别一直是中文分词中的难点。

      回归正题,IndexWriterConfig对象用来设置IndexWriter一些初始配置,一旦IndexWriter对象创建完成,改变IndexWriterConfig的配置对已创建的IndexWriter对象不会产生影响。

    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
    IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_40, analyzer);                 
    iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);//设置索引维护的方式
    iwc.setRAMBufferSizeMB(256.0);//设置用来缓冲文档的RAM大小
    IndexWriter writer = new IndexWriter(dir, iwc);

      接下来需要创建Document文档,并通过IndexWriter对象将文档添加到索引库中。Document文档可以是一个HTML页面,一篇文本文档,一封电子邮件。一个Document可以包含多个FieldField代表文档的属性。此处file为文件对象。


    Document doc = new Document();         
    doc.add(new StringField("path", file.getPath(), Field.Store.YES));
    doc.add(new TextField("filename", file.getName(), Field.Store.YES));

    注意:StringField被索引不被分词,整个值被看作为一个单独的token而被索引。

        TextField既被索引又被分词,但是没有词向量。

        Filed类型还包含LongFieldDoubleFieldIntFiled等,具体的含义请参考官方API

        最后需要将文档添加到索引库中。

    writer.addDocument(doc);
    writer.close();

    2.检索索引。

    检索索引的原理大致如下:

    • 对查询语句进行词法分析、语法分析和语言处理。词法分析主要是识别单词和关键字。语法分析主要是根据查询语句的语法规则来形成一颗语法树。语言处理与索引建立过程中语言处理几乎相同。

    • 搜索索引,得到符合语法树的文档

    • 根据得到的文档和查询语句的相关性,对结果进行排序。

    具体实现如下:

      检索索引首先要读取索引文档,并建立IndexSearch对象。

    IndexReader reader = DirectoryReader.open(FSDirectory.open(new File(indexPath)));
    IndexSearcher searcher = new IndexSearcher(reader);

      然后需要对查询语句进行分析,Lucene通过Query接口实现类来对查询条件进行分析。Lucene支持单Filed查询、多Field查询、逻辑组合查询、模糊查询、范围查询、通配符查询等。各种查询方式的具体实现本文暂时不讨论。

    1.单字段查询。

    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
    QueryParser queryParser = new QueryParser(Version.LUCENE_40, " filename",analyzer) ;
    queryParser.setDefaultOperator(QueryParser.OR_OPERATOR);//设置默认逻辑‘或’查询
    Query query = queryParser.parse(condition);

    2.多字段查询

    String[] fields = new String[]{"filename"," path "};
    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
    //MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(Version.LUCENE_40, fields, analyzer);
    //Query query = multiFieldQueryParser.parse(condition);
    BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
                                                         BooleanClause.Occur.MUST};
    Query query = MultiFieldQueryParser.parse(Version.LUCENE_40, condition, fields, flags, analyzer);

      接下来,通过IndexSearcher对象进行查找。对查询出来的结果可以进行高亮显示关键字,取摘要等处理。

        TopDocs results = searcher.search(query,10);
        ScoreDoc[] hits = results.scoreDocs;
        for(ScoreDoc hit:hits){
            Document doc = searcher.doc(hit.doc);
            SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
            Highlighter highlighter = new Highlighter(formatter, new QueryScorer(query));
            Fragmenter fragmenter =new SimpleFragmenter(80);
            highlighter.setTextFragmenter(fragmenter);
            Information information = new Information();
            information.setId(hit.doc);                                 
            information.setPath(doc.get("path"));
            if(highlighter.getBestFragment(analyzer, "filename", doc.get("filename"))==null){
                information.setFileName(doc.get("filename"));
            }else{
                information.setFileName(highlighter.getBestFragment(analyzer, "filename", doc.get("filename")));
            }
            informations.add(information);
        }

      

    3.更新索引。

      Lucene索引的更新为调用IndexWriterupdateDocument方法,该方法接受两个参数,第一个Term词向量,第二个为新增的文档,可以是单个文档,也可以是批量文档。Lucene会先根据Term删除索引库中原有的文档,再向索引库中添加新的documentTerm的第一参数表示要在文档的哪个Field上查找,第二个代表查询关键字。

     writer.updateDocument(new Term("path", file.getPath()), doc);

    4.删除索引。

      Lucene索引的删除的可以根据Query对象来删除,也可以根据Term来删除,也可以一次删除全部索引。

    //根据Term删除
    writer.deleteDocuments(new Term("path", file.getPath()));
    //删除全部索引
    writer.deleteAll();



关键字词: Lucene 全文检索