lucene基本架构

[b]lucene各个模块用途[/b]
[img]http://dl2.iteye.com/upload/attachment/0106/8088/aea06d24-8be4-30a2-95fd-5ac8ee8f1e48.png[/img]
[b]建立索引和检索图解[/b]
[img]http://dl2.iteye.com/upload/attachment/0106/8092/9a6e9a74-b50e-3be4-8a96-1ad9a5dcaf0a.png[/img]
[b]以一个Demo来查看查询代码粗略逻辑[/b]

public class TestDemo extends LuceneTestCase {

public void testDemo() throws IOException, ParseException {
//创建一个分词器使用Whitespace-lowercasing analyzer,且无停用词.
Analyzer analyzer = new MockAnalyzer( random);

//保存索引到内存:
//Directory directory = newDirectory();
//会创建2个文件segements.gen(内容为索引的field字段)/segement_N(内容为空)
// 如果要保存到硬盘,用下面替换:
Directory directory = FSDirectory. open( new File("D:\\index" ));
RandomIndexWriter iwriter = new RandomIndexWriter( random, directory, analyzer);
//maxFieldLength 长度溢出将在这里打印
iwriter.w.setInfoStream(VERBOSE ? System. out : null);
//新建文档
Document doc = new Document();
//需要索引的值
String text = "This is the text to be indexed.";
//建立索引,此时不创建文件放内存,第三个参数设置用来判断是否要分词。
doc.add(newField("fieldname" , text, Field.Store.YES, Field.Index.ANALYZED));
//创建2个文件.fdt.ftx但是没有填充值
iwriter.addDocument(doc);
//这里默认commit了,填充了值,合并段,合并文档等,还创建了其他各种文件
iwriter.close();

// 查询索引:
IndexReader ireader = IndexReader.open(directory); // read-only=true
IndexSearcher isearcher = new IndexSearcher(ireader);
//创建个默认查询,文字:"text":
QueryParser parser = new QueryParser( TEST_VERSION_CURRENT, "fieldname", analyzer);
Query query = parser.parse("text");
TopDocs hits = isearcher.search(query, null, 1);

// 循环结果集:
for (int i = 0; i < hits. scoreDocs. length; i++) {
Document hitDoc = isearcher.doc(hits. scoreDocs[i]. doc);
assertEquals("This is the text to be indexed." , hitDoc.get("fieldname"));
}

// 测试短语
query = parser.parse("\"to be\"");
assertEquals (1, isearcher.search(query, null, 1).totalHits);

//记得手动关闭
isearcher.close();
ireader.close();
directory.close();
}
}

[b]Document.add()方法流程[/b]
[img]http://dl2.iteye.com/upload/attachment/0106/8094/8053ca55-ebfa-38b2-be28-eb7f5cc30cd9.png[/img]
这个方法主要功能是将field字段和值保存到缓存中去,等待close()方法调用时写入文件。其中为了增加效率启用了多线程创建索引分词。分词组件可以自己实现只要继承Analyzer类。在分词组件里面重载incrementToken(不大记得好像是)或者自己实现一个类继承Tocken,在类里面重载incrementToken方法。这样,如果索引字段的定义了需要分词的话,就会调用你重载的这个方法,以此实现自定义分词效果。

public TokenStream reusableTokenStream(String fieldName, Reader reader)
throws IOException;

看了4.9+的代码,这个方法以及被定义为final。作者开发了另一个方法来提供用户重载,实现一样效果。

protected Analyzer.TokenStreamComponents createComponents(String fieldName, Reader reader);

下面贴一个程序运行的关键代码,以及基于4.7.0实现的一个Analyzer.
//保证多个进程同时读取Directory不出问题。对一个lockFactory的引用来锁定文件
public abstract class Directory implements Closeable {
protected LockFactory lockFactory;
}
//锁定文件,因为采用多个线程同时进行分词,同时只能有一个线程对缓存进行读写
public abstract class LockFactory {
public abstract Lock makeLock(String lockName);
abstract public void clearLock(String lockName) throws IOException;
}
//保存索引文件的原文档
public final class Document implements java.io.Serializable {
List<Fieldable> fields = new ArrayList<Fieldable>();
}
//分词组件,有2个重载方法。功能:1、分词。2、停词。3、去掉标点。可以自己实现分词
public final class MockAnalyzer extends Analyzer {
private final int pattern;
private final boolean lowerCase;
private final CharArraySet filter;
private final boolean enablePositionIncrements;
private int positionIncrementGap;
private final Random random;
private Map<String,Integer > previousMappings = new HashMap<String,Integer>();
private boolean enableChecks = true;
private int maxTokenLength = MockTokenizer.DEFAULT_MAX_TOKEN_LENGTH ;

maybePayload() {
//影响词的评分
token.setPayload(new PayLoad((Field)_id.getByte()));
}

//这个方法返回一个TokenStreamComponents,设置reader,filters,token。(lucene4.10这个方法为final不可继承)
public TokenStream reusableTokenStream(String fieldName, Reader reader)
throws IOException {
@SuppressWarnings("unchecked" ) Map<String,SavedStreams> map = (Map) g etPreviousTokenStream();
if (map == null) {
map = new HashMap<String,SavedStreams>();
setPreviousTokenStream(map);
}

SavedStreams saved = map.get(fieldName);
if (saved == null) {
saved = new SavedStreams();
saved.tokenizer = new MockTokenizer(reader, pattern, lowerCase, maxTokenLength);
saved.tokenizer.setEnableChecks( enableChecks);
StopFilter filt = new StopFilter(LuceneTestCase.TEST_VERSION_CURRENT , saved.tokenizer , filter);
filt.setEnablePositionIncrements(enablePositionIncrements );
//这里用的是责任链模式。类似saved.filter=A,A.filter=B,B..filter=c以此类推
saved.filter = filt;
//是否使用跳跃表,不设置的话,如果索引一旦增长(根据分词判断是否使用同一个字典链表)过长,lucene会自动使用。
saved.filter = maybePayload(saved. filter, fieldName);
//关键是这一行,将filter放入map,可以放入多个,类型为TokenFilter
map.put(fieldName, saved);
return saved. filter;
} else {
saved.tokenizer.reset (reader);
return saved. filter;
}
}
}
//filter超类
public class Token {
//默认对是否进行使用跳跃表的实现
private Payload payload;
}

[b]writer.close()方法的流程(用画图工具画的,超级烂。下次引以为戒)[/b]
[img]http://dl2.iteye.com/upload/attachment/0106/8096/3f7bcb71-e4a2-34db-9996-bd72aa65c12b.png[/img]
关键的一部分代码:
//创建、写入文件segments
public class IndexWriter implements Closeable, TwoPhaseCommit {
//这里创建segments.gen,segments_N文件以及保存数据
private synchronized final void finishCommit() throws CorruptIndexException, IOException {
pendingCommit .finishCommit(directory);
}

//入口
private void closeInternal(boolean waitForMerges) throws CorruptIndexException, IOException {
//创建各种文件,(除segments*,.fdt.fdx),先组装数据,后合并数据,写入文件操作
flush(waitForMerges, true);
//会调用到finishCommit()
commitInternal(null);
}
}

[b]英文单词大小写处理[/b]
lucene用他自带的standardAnalyzer在建立索引的时候是区分大小写的。在查询query的时候会通通转成小写,导致查询不到。那么可以复制一份源代码,放到本地改一下。调用本地就可以

protected TokenStreamComponents createComponents(final String fieldName, final Reader reader) {
final StandardTokenizer src = new StandardTokenizer(matchVersion, reader);
src.setMaxTokenLength(maxTokenLength);
src.setReplaceInvalidAcronym(replaceInvalidAcronym);
TokenStream tok = new StandardFilter(matchVersion, src);
//注释掉这一句
//tok = new LowerCaseFilter(matchVersion, tok);
tok = new StopFilter(matchVersion, tok, stopwords);
return new TokenStreamComponents(src, tok) {
@Override
protected boolean reset(final Reader reader) throws IOException {
src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength);
return super.reset(reader);
}
};

[b]参考:[/b]
因为lucene虽然版本升级,方法上有很多改变,我感觉用户体验明显好多了。但是目前他整体的架构没看得大改变。所以以前的一些书籍还是可以看得。记录一下文档地址,这是一本很好的书:http://www.open-open.com/doc/view/f92fa668d8b84626abd20a05b6eb014e
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值