来源: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是一个抽象类,司职对文本内容的切分词规则。
切分后返回一个TokenStream,TokenStream中有一个非常重要方法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。
|