来源:http://hi.baidu.com/shengtaiquan/blog/item/b3673538dc987924b9998f7d.html

结构-查询逻辑 入库逻辑
2007-01-10 12:01

Lucene包结构功能表

包名

功能

org.apache.lucene.analysis

语言分析器,主要用于的切词,支持中文主要是扩展此类

org.apache.lucene.document

索引存储时的文档结构管理,类似于关系型数据库的表结构

org.apache.lucene.index

索引管理,包括索引建立、删除等

org.apache.lucene.queryParser

查询分析器,实现查询关键词间的运算,如与、或、非等

org.apache.lucene.search

检索管理,根据查询条件,检索得到结果

org.apache.lucene.store

数据存储管理,主要包括一些底层的I/O操作

org.apache.lucene.util

一些公用类

 

Lucene功能强大,但从根本上说,主要包括两块:一是文本内容经切词后索引入库;二是根据查询条件返回结果。

查询逻辑

按先后顺序,查询逻辑可分为如下几步: 

1.  查询者输入查询条件
条件之间可以通过特定运算符进行运算,比如查询希望查询到与“中国”和“北京”相关的记录,但不希望结果中包括“海淀区中关村”,于是输入条件为“中国+北京-海淀区中关村”;

2.  查询条件被传达到查询分析器中,分析器将将对“中国+北京-海淀区中关村”进行分析,首先分析器解析字符串的连接符,即这里的加号和减号,然后对每个词进行切词,一般最小的词元是两个汉字,则中国和北京两个词不必再切分,但对海淀区中关村需要切分,假设根据切词算法,把该词切分为“海淀区”和“中关村”两部分,则最后得到的查询条件可以表示为:“中国” AND “北京” AND NOT(“海淀区” AND “中关村”)

3.  查询器根据这个条件遍历索引树,得到查询结果,并返回结果集,返回的结果集类似于JDBC中的ResultSet

4.  将返回的结果集显示在查询结果页面,当点击某一条内容时,可以链接到原始网页,也可以打开全文检索库中存储的网页内容。 

这就是查询的逻辑过程,需要说明的是,Lucene默认只支持英文,为了便于说明问题,以上查询过程采用中文举例,事实上,当Lucene被扩充支持中文后就是这么一个查询过程。 

入库逻辑 

入库将把内容加载到全文检索库中,按顺序,入库逻辑包括如下过程: 

1.  入库者定义到库中文档的结构,比如需要把网站内容加载到全文检索库,让用户通过“站内检索”搜索到相关的网页内容。入库文档结构与关系型数据库中的表结构类似,每个入库的文档由多个字段构成,假设这里需要入库的网站内容包括如下字段:文章标题、作者、发布时间、原文链接、正文内容(一般作为网页快照)。

2.  包含N个字段的文档(DOCUMENT)在真正入库前需要经过切词(或分词)索引,切词的规则由语言分析器(ANALYZER)完成。

3.  切分后的“单词”被注册到索引树上,供查询时用,另外也需要也其它不需要索引的内容入库,所有这些是文件操作均由STORAGE完成。 

以上就是记录加载流程,索引树是一种比较复杂的数据存储结构,将在后续章节陆续介绍,这里就不赘述了,需要说明的一点是,Lucene的索引树结构非常优秀,是Lucene的一大特色。


语言分析包org.apache.lucene.analysis
2007-01-10 12:31
语言分析包org.apache.lucene.analysis

Analyzer是一个抽象类,司职对文本内容的切分词规则。

切分后返回一个TokenStreamTokenStream中有一个非常重要方法next(),即取到下一个词。简单点说,通过切词规则,把一篇文章从头到尾分成一个个的词,这就是org.apache.lucene.analysis的工作。

对英文而言,其分词规则很简单,因为每个单词间都有一个空格,按空格取单词即可,当然为了提高英文检索的准确度,也可以把一些短语作为一个整体,其间不切分,这就需要一个词库,对德文、俄文也是类似,稍有不同。

对中文而言,文字之间都是相连的,没有空格,但我们同样可以把字切分,即把每个汉字作为一个词切分,这就是所谓的“切字”,但切字方式方式的索引没有意义,准确率太低,要想提高准确度一般都是切词,这就需要一个词库,词库越大准确度将越高,但入库效率越低。

若要支持中文切词,则需要扩展Analyzer类,根据词库中的词把文章切分。

简单点说,org.apache.lucene.analysis就是完成将文章切分词的任务


构建索引
2007-01-10 12:43

把一个初始化实例化完毕的一个对象,作为一个参数传递到另一个对象使用。
比如在做Index时需要传递一个实例化的
org.apache.lucene.analysis对象、
org.apache.lucene.document对象和
存储包org.apache.lucene.store对象索引管理包org.apache.lucene.index,org.apache.lucene.index来完成索引过程

在Search时需要
org.apache.lucene.analysis
查询分析包org.apache.lucene.queryParser和
给检索包
org.apache.lucene.search

    //构建存储lucene.store“index”
    String[] stopStrs = {"他奶奶的", "***"}; 
   //构建分析器
    StandardAnalyzer analyzer = new StandardAnalyzer(stopStrs);
    
    //构建结构Document 
    Document doc = new Document();
    doc.add(Field.UnIndexed("id", "1"));//“id”为字段名,“1”为字段值
    doc.add(Field.Text("text", "***,他奶奶的,入门与使用"));

   //构建存储、分析器、结构后==〉执行索引写入
    IndexWriter writer = new IndexWriter("index", analyzer, true);
    writer.addDocument(doc);
    
    //索引完成后的处理
    writer.optimize();
    writer.close();

Lucene的索引接口(Analyzer/IndexWriter/document)
2007-01-10 13:57

4.1 Lucene的索引接口
4.1.1分析器Analyzer

        分析器主要工作是筛选,一段文档进来以后,经过它,出去的时候只剩下那些有用的部分,其他则剔除。而这个分析器也可以自己根据需要而编写。
        org.apache.lucene.analysis.Analyzer:这是一个虚构类,以下两个接口均继承它而来。

        org.apache.lucene.analysis.SimpleAnalyzer:分析器,支持最简单拉丁语言。

        org.apache.lucene.analysis.standard.StandardAnalyzer:标准分析器,除了拉丁语言还支持亚洲语言,并在一些匹配功能上进行完善。在这个接口中还有一个很重要的构造函数:StandardAnalyzer(String[] stopWords),可以对分析器定义一些使用词语,这不仅可以免除检索一些无用信息,而且还可以在检索中定义禁止的政治性、非法性的检索关键词。


4.1.2 IndexWriter

        IndexWriter的构造函数有三种接口,针对目录Directory文件File文件路径String三种情况。
例如IndexWriter(String path, Analyzer a, boolean create),path为文件路径,a为分析器,create标志是否重建索引(true:建立或者覆盖已存在的索引,false:扩展已存在的索引。)

一些重要的方法:

接口名

备注

addDocument(Document doc)

索引添加一个文档

addIndexes(Directory[] dirs)

将目录中已存在索引添加到这个索引

addIndexes(IndexReader[] readers)

将提供的索引添加到这个索引

optimize()

合并索引并优化

close()

关闭


       IndexWriter为了减少大量的io维护操作,在每得到一定量的索引后建立新的小索引文件(笔者测试索引批量的最小单位为10),然后再定期将它们整合到一个索引文件中,因此在索引结束时必须进行wirter. optimize(),以便将所有索引合并优化


4.1.3 org.apache.lucene.document

 以下介绍两种主要的类:
 a)org.apache.lucene.document.Document:
        Document文档类似数据库中的一条记录,可以由好几个字段(Field)组成,并且字段可以套用不同的类型(详细见b)。Document的几种接口:
 

接口名

备注

add(Field field)

添加一个字段(Field)到Document中

String get(String name)

从文档中获得一个字段对应的文本

Field getField(String name)

由字段名获得字段值

Field[] getFields(String name)

由字段名获得字段值的集


 b)org.apache.lucene.document.Field
        即上文所说的“字段”,它是Document的片段section。
        Field的构造函数:
       Field(String name, String string, boolean store, boolean index, boolean token)。
        Indexed:如果字段是Indexed的,表示这个字段是可检索的
        Stored:如果字段是Stored的,表示这个字段的值可以从检索结果中得到
        Tokenized:如果一个字段是Tokenized的,表示它是有经过Analyzer转变后成为一个tokens序列,在这个转变过程tokenization中,Analyzer提取出需要进行索引的文本,而剔除一些冗余的词句(例如:a,the,they等,详见org.apache.lucene.analysis.StopAnalyzer.ENGLISH_STOP_WORDS和org.apache.lucene.analysis.standard.StandardAnalyzer(String[] stopWords)的API)。Token是索引时候的基本单元,代表一个被索引的词,例如一个英文单词,或者一个汉字。因此,所有包含中文的文本都必须是Tokenized的。
     Field的几种接口:

Name

Stored

Indexed

Tokenized

use

Keyword(String name,

        String value)

Y

Y

N

date,url

Text(String name, Reader value)

N

Y

Y

short text fields:

title,subject

Text(String name, String value)

Y

Y

Y

longer text fields,

like “body”

UnIndexed(String name,

String value)

Y

N

N

 

UnStored(String name,

         String value)

N

Y

Y

 

修改索引的代码,请根据Lucene的API解读:
  /**
   * 对已有的索引添加新的一条索引
   * @param idStr String:要修改的id
   * @param doc Document:要修改的值
   */
  public void addIndex(String idStr, String valueStr) {
    StandardAnalyzer analyzer = new StandardAnalyzer();
    IndexWriter writer = null;
    try {
      writer = new IndexWriter(indexPath, analyzer, false);
      writer.mergeFactor = 2; //修正lucene 1.4.2 bug,否则不能准确反映修改

          Document doc = new Document();
          doc.add(Field.UnIndexed("id", idStr));//“id”为字段名,“1”为字段值
          doc.add(Field.Text("text", valueStr));
      writer.addDocument(doc);

      writer.optimize();
      writer.close();
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }

  /**
   * 删除索引
   *
   * @param idStr String
   */
  public void deleteIndex(String idStr) {
    try {
      Directory dirt = FSDirectory.getDirectory(indexPath, false);
      IndexReader reader = IndexReader.open(dirt);
      IndexXML.deleteIndex(idStr, reader);
      reader.close();
      dirt.close();
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }

Lucene的检索接口
2007-01-10 17:23
5.2.1 Query与QueryParser
        主要使用方法:
QueryParser .parse(String query, String field, Analyzer analyzer),例如:
Query query = QueryParser.parse("入门", "text", analyzer);
"入门"为检索词, "text"为检索的字段名, analyzer为分析器,查询关键字也是需要分词的。

5.2.2 Hits与Searcher

       Hits的主要使用接口:
  

接口名

备注

Doc(int n)

返回第n个的文档的所有字段

length()

返回这个集中的可用个数

   简单的检索代码
    //用查询关键字、Document的字段值Field、分析器analyzer构造Query
    // Query对象作为一个入参传入
Searcher 
    Query query = QueryParser.parse("入门", "text", analyzer);
  

    //构造Searcher ,同时指定索引目录
    Searcher searcher = new IndexSearcher("./index");//"index"指定索引文件位置
    //Query对象作为入参执行查询返回结果集。
    Hits hits = searcher.search(query);

    //遍历结果值集
    for (int i = 0; i < hits.length(); i++)
   {
      doc = hits.doc(i);
      String id = doc.get("id");
      System.out.println("found " + "入门" + " on the id:" + id);
   }

Lucene 的检索结果排序
        Lucene的排序主要是对org.apache.lucene.search.Sort的使用。Sort可以直接根据字段Field生成,也可以根据标准的SortField生成,但是作为Sort的字段,必须符合以下的条件:唯一值以及Indexed。可以对Integers, Floats, Strings三种类型排序。
        对整数型的ID检索结果排序只要进行以下的简单操作:
//构造一个排序对象,做为searcher的入参而已
 Sort sort = new Sort("id");
Hits hits = searcher.search(query, sort);

 用户还可以根据自己定义更加复杂的排序,详细请参考API。