Lucene 3.0 学习(一)

本文介绍 Lucene 3.0 的实战入门经验,通过对官方 demo 的详细解析,帮助初学者快速掌握 Lucene 的核心原理及使用技巧。

最近因项目需要,开始学习 Lucene 。先从官网上下载了相关资源(Lucene 3.0.1 )。然后,上网搜索相关的入门指南、学习笔记等,想直接利用前辈们的经验迅速上手,但这些文档大都是基于 2.X 甚至更老的版本,与新版本并不兼容 (Lucene 的向下兼容似乎做得不是太好 ) 。最后,还是通过 Lucene 自带的 demo 代码的分析及修改,对 Lucene 有了些初步的理解。这里把自己的心得作个总结,希望会对刚刚接触 Lucene 的人有所帮助,也希望能够和更多的 Lucene 爱好者一起交流学习,共同进步。

 

一、启动 Demo

Lucene 自带的 demo 程序中,与搜索相关的主要有三个文件

java org.apache.lucene.demo.IndexFiles

功能:对指定文件夹下的所有文件建立索引,并将结果保存到指定的索引文件夹中。

用法:只要指定一个要索引文档的路径就可以。

java org.apache.lucene.demo.SearchFiles

功能:理论上是给出查询结果,包括 txt html xls doc .js,.htm 等文件。

用法:不用设置参数。

org.apache.lucene.demo.FileDocument

功能:确定每个文档中要索引的内容以及索引的方式。

用法:被 IndexFiles 调用

 

运行 demo 的时候,先导入相关 jar 文件,然后执行 IndexFiiles 建立索引,然后执行 SearchFiles 搜索,能够以文件路径形式返回查询结果,并且支持 txt html doc xls 等多种文件格式。

 

进一步测试,会发现 demo 版本有如下问题:

1、 中文不支持,如果文本内容里面有中文,查询的时候并不能返回正确结果。

2、 不支持文件名的查询。

3、 结果显示并不是常见的文本内容片段的形式,如 google baidu 等。

以及进一步需要解决的问题:

4、 索引如何与文本同步?也就是增量索引的处理。

5、 更多文件类型支持?

 

二、基本原理

要解决这些问题,首先要了解 Lucene 的工作原理【 2 】:

demo 一样, Lucene 工作过程分为两部分,建立索引与查询索引。建立索引,是指对于给定内容,进行语言处理后,得到一系列的 Term (这里 Term 指的是被索引文档的基本元素,对于英文就是单词,并且是大小写,单复数,时态等统一后的单词;对于中文,是一系列有意义的词。)并以此为基础建立字典。查询索引,首先要分析用户输入的查询语句,同样得到一系列的 Term ,然后到建好的索引中查询,并对结果进行相关性分析并排序,最终得到输出结果。

 

三、源码分析

结合工作原理进行源码分析 ( 省略部分原有的注释以及与搜索无关的代码 )

1 org.apache.lucene.demo.IndexFiles

public class IndexFiles {

  // 定义索引所在的路径

  static final File INDEX_DIR = new File("index");

 

  /** Index all text files under a directory. */

  public static void main(String[] args) {

      

    final File docDir = new File(args[0]);  // 被索引的文档路径

try {

// 定义 IndexWriter 用于建立索引

// new StandardAnalyzer(Version.LUCENE_CURRENT) Lucene 自带的一种分词器

      IndexWriter writer = new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED);   // -------------

     

      System.out.println("Indexing to directory '" +INDEX_DIR+ "'...");

// 递归为指定路径下所有的文件建立索引

indexDocs(writer, docDir);      // --------

      System.out.println("Optimizing...");

      writer.optimize();       // 索引优化

      writer.close();      

 

    } catch (IOException e) {

      System.out.println(" caught a " + e.getClass() +

       "/n with message: " + e.getMessage());

    }

  }

 

// 递归方法为每个文件建立索引

  static void indexDocs(IndexWriter writer, File file)

    throws IOException {

    if (file.canRead()) {

      if (file.isDirectory()) {

        String[] files = file.list();

        if (files != null) {

          for (int i = 0; i < files.length; i++) {

            indexDocs(writer, new File(file, files[i]));

          }

        }

      } else {

        System.out.println("adding " + file);

        try {

                     // 添加索引

          writer.addDocument(FileDocument.Document(file));  // -------

        }

        catch (FileNotFoundException fnfe) {

          ;

        }

      }

    }

  }

 

}

 

2 org.apache.lucene.demo. FileDocument

public class FileDocument {

  public static Document Document(File f)

       throws java.io.FileNotFoundException {

        

     Document doc = new Document();

 

// 这里是为文档的各种域( field )分别建立索引

// 为文件路径添加索引

    doc.add(new Field("path", f.getPath(), Field.Store.YES, Field.Index.NOT_ANALYZED));   // -------------

 

// 为文件修改日期添加索引

    doc.add(new Field("modified",

        DateTools.timeToString(f.lastModified(), DateTools.Resolution.MINUTE),

        Field.Store.YES, Field.Index.NOT_ANALYZED));

   

// Note that FileReader expects the file to be in the system's default encoding.

// If that's not the case searching for special characters will fail.

// 为文件内容添加索引

    doc.add(new Field("contents", new FileReader(f)));  // ----------

    // return the document

    return doc;

  }

 

  private FileDocument() {}

}

 

3 org.apache.lucene.demo. SearchFiles

public class SearchFiles {

  public static void main(String[] args) throws Exception {

    // 执行是可不输入任何参数

    String index = "index";  // 索引所在的路径

String field = "contents";   // 指定要检索的域,此处是内容,也可以是路径、修改时间等,但该域需要先建立索引,如    

String queries = null;

    int repeat = 0;

    boolean raw = false;

    String normsField = null;

    boolean paging = true;

    int hitsPerPage = 10;

   

   // 打开索引

    IndexReader reader = IndexReader.open(FSDirectory.open(new File(index)), true);

    Searcher searcher = new IndexSearcher(reader);   // ----------

    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);

    BufferedReader in = null;

    if (queries != null) {

      in = new BufferedReader(new FileReader(queries));

    } else {

      in = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));

}

// 查询解析器,用于查询关键字的语法解析

    QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, field, analyzer);    // ----------------

    while (true) {

      if (queries == null)                        // prompt the user

        System.out.println("Enter query: ");

 

      String line = in.readLine();

 

      if (line == null || line.length() == -1)

        break;

 

      line = line.trim();

      if (line.length() == 0)

        break;

 

      // 对搜索的关键字进行语法分析

      Query query = parser.parse(line);  // -----------------

      System.out.println("Searching for: " + query.toString(field));

 

           

      if (repeat > 0) {                           // repeat & time as benchmark

        Date start = new Date();

        for (int i = 0; i < repeat; i++) {

          searcher.search(query, null, 100);

        }

        Date end = new Date();

        System.out.println("Time: "+(end.getTime()-start.getTime())+"ms");

      }

 

      if (paging) {

        doPagingSearch(in, searcher, query, hitsPerPage, raw, queries == null);

      } else {

        doStreamingSearch(searcher, query);

      }

    }

    reader.close();

  }

 

public static void doPagingSearch(BufferedReader in, Searcher searcher, Query query, int hitsPerPage, boolean raw, boolean interactive) throws IOException {

 

// 这部分内容主要是实现搜索结果的分页显示

// 真正和搜索相关代码只有下面几行 h

// 搜索结果存储对象

    TopScoreDocCollector collector = TopScoreDocCollector.create(

        5 * hitsPerPage, false);

   // 执行搜索

searcher.search(query, collector);   // -------------

// 搜索结果是按照其相关性得分,从大到小保存的

    ScoreDoc[] hits = collector.topDocs().scoreDocs;  // -----------

   …….

for (int i = start; i < end; i++) {

// 根据 hits ,可以获得各种文档的信息,如得分,内容,路径等等

Document doc = searcher.doc(hits[i].doc); 

              System.out.println("doc="+hits[i].doc+" score="+hits[i].score);

       }

   ………

  }

}

 

源码中标注的 1-10 ,是 Lucene 搜索的核心步骤,围绕核心步骤做简单修改,就可以解决 Demo 运行时遇到的问题,具体的方法会在接下来的总结中与大家分享。

由于刚刚接触Lucene不久,所以对于一些问题的理解也不够深入,希望能够和大家多交流,多沟通,共同提高对搜索引擎,对Lucene的认识和理解。

我的email: whole.againer@gmail.com

 

参考资料:

0Lucene 3.0.1( 目前最新版本应该是3.0.2), http://lucene.apache.org/java/docs/index.html

1 】 《Lucene in ActionERIK HATCHEROTIS GOSPODNETIC

2 】 《Lucene3.0 原理与代码分析》 forfuture1978

3http://forfuture1978.javaeye.com

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值