lucene中Field简析

 

http://blog.csdn.net/zhaoxiao2008/article/details/14180019 

 

先看一段lucene3代码java

Document doc = new Document();  算法

doc.add(new Field("fullpath", f.getCanonicalPath(), Field.Store.YES, Field.Index.NOT_ANALYZED))  数据库

 

Field类是文档索引期间很重要的类,控制着被索引的域值数组

Field.Store.* 域存储选项经过倒排序索引来控制文本是否能够搜索 
变量名 释义
Index.ANALYZED 使用分析器将域值分解成独立的语汇单元流,并使每一个语汇单元都能被搜索,适用于普通文本域
Index.NOT_ANALYZED 对域进行索引,但不对String进行分析,将域值做为单一的语汇单元,适用于索引那些不能被分解的域值,如URL,文件路径,电话号码等
Index.ANALYZED_NO_NORMS 不会在索引中存储norms信息,norms记录了索引中的index-time boost信息,当你进行搜索时比较费内存
Index.NOT_ANALYZED_NO_NORMS 同上,也不存储norms信息,在搜索时减小索引空间和内存耗费Index.No 使对应的域值不被搜索
Index.No 使对应的域值不被搜索

 

Field.Index.* 域索引选项肯定是否要存储域的真实值,以便后续继续搜索时能恢复这个
变量名 释义
Stroe.YES 存储域值,该状况下原始字符串所有被保存在索引中,对须要展现搜索结果的域有用,如URL,标题
Stroe.NO 不存储域值,一般跟Index.ANALYZED共同用来索引大的文本域值,不用恢复为初始格式

 

 

 

在lucene4中 ,这种写法已经不合适了缓存

 

doc.add(new Field("contents", new FileReader(f), TextField.TYPE_NOT_STORED));      //索引文件内容  安全

 

 

变量名 释义
TYPE_NOT_STORED 索引,分词,不存储
TYPE_STORED

索引,分词,存储多线程

 

 

TextField源代码对应的定义,能够看出来,它其实包含了lucene3中域存储选项和域索引选项性能

 

static {  优化

    TYPE_NOT_STORED.setIndexed(true);  ui

    TYPE_NOT_STORED.setTokenized(true);  

    TYPE_NOT_STORED.freeze();  

  

    TYPE_STORED.setIndexed(true);  

    TYPE_STORED.setTokenized(true);  

    TYPE_STORED.setStored(true);  

    TYPE_STORED.freeze();  

 

  }

 

 

可是对于特殊的要求,好比路径,则不须要索引,要分类,咱们就得手动去设置Field的熟悉了

FieldType fieldType = new FieldType();  

fieldType.setIndexed(false);//set 是否索引  

fieldType.setStored(true);//set 是否存储  

fieldType.setTokenized(true);//set 是否分类  

 

doc.add(new Field("fullpath", f.getCanonicalPath(), fieldType));     //索引完整路径  

 

 

 

 

可是对于特殊的要求,好比路径,则不须要索引,要分类,咱们就得手动去设置Field的熟悉了

FieldType fieldType = new FieldType();  

fieldType.setIndexed(false);//set 是否索引  

fieldType.setStored(true);//set 是否存储  

fieldType.setTokenized(true);//set 是否分类  

 

doc.add(new Field("fullpath", f.getCanonicalPath(), fieldType));     //索引完整路径  

下一篇lucene4.5近实时搜索

_______________________________________________________________________

 

从概念理解Lucene的Index(索引)文档模型  

 

来源:  http://blog.csdn.net/duck_genuine/article/details/6053430
 
 
Lucene主要有两种文档模型:Document和Field,一个Document可能包含若干个Field。
 
每个Field有不一样的策略:

1.被索引 or not,将该字段(Field)通过分析(Analyisi)后,加入索引中,并非原文 。

2.若是被索引,可选择是否保存“term vector”(向量),用于类似检索。

3.可选择是否存储(store),将原文直接拷贝 ,不作索引,用于检索后的取出。

Lucene中的文档模型相似于数据库,可是又不彻底相同,体如今以下几方面:

1.无规范格式,即无需固定的Schema,无列等预先设计,同一个索引中加入的Document可包含不一样的Field 。

2.非正规化,Lucene中的文档模型是一个平面化 的结构,没有递归定义,天然链接等等复杂的结构。

2.2  理解索引过程

整体来讲,索引过程为:

1.提取摘要:从原文提取,并建立Document和Field对象。Tika 提供了PDF、Word等非文本的文本提取。

2.分析:Analysis,首先对Document的Field进行分解,产生token流,而后通过一系列Filter(如小写化)等。

3.创建索引:经过IndexWriter的addDocument写入到索引中。Lunece使用了反向索引 ,即“那个Document包含单词X”,而不是“Document包含哪些Word”

索引文件组成

为了保证效率,每一个索引由若干segments组成:

_X.cfs  每一个segments由若干个cfs组成,X为0,1,2….若是开启了useCompoundFile,则只有一个.cfs文件。

segments_<N>:记载每一个分区对应的cfs文件。

每一个一段时间后,在调用IndexWriter时,会自动合并这些segment

2.3  索引的基本操做

首先建立IndexWriter

IndexWriter(dir,new WhiteSpaceAnalyser(),IndexWriter.MaxField.UNLIMITED);

dir是索引的保存路径,WhiteSpaceAnalyser是基于空白的分词 ,最后部限定Field的数量。

依次建立文档Document和Field

Document doc = new Document();

doc.add(new Filed(key,value,STORE?,INDEX?)

key就是field的检索字段名,value就是待写入/分析的文本。

STORE ,与索引无关,是否额外存储原文 ,能够在搜索结果后调用出来,NO不额外存储;YES,额外存储。

INDEX ,NO,不索引;ANALYZED,分词后索引;NOT_ANALYZED,不分词索引;ANALYZED_NO_NORMS,分词索引,不存储NORMS;NOT_ANALYZED_NO_NORMS,不分词,索引,不存储NORMS。除了NO外都算索引,能够搜索 。NORMS存储了boost所需信息,包含了NORM可能会占用更多内存?

删除索引

IndexWriter提供了删除Document的功能:

deleteDocumen(Term) 

deleteDocumen(Term[])

deleteDocumen(Query)

deleteDocumen(Query [])

特别注意Term不必定是惟一的,因此有可能误删除多个 。另外最好选择惟一的、非索引的Term 以防混乱(好比惟一ID)。

删除后commit()而后close才能真正写入索引文件中。

删除后只是标记为删除,maxDoc()返回全部文档(含已经删除,但未清理的);numDocs:未删除的文档数量

使用delete后,再optimize():压缩删除的空间、再commit才真正的删除释放空间。

更新索引

updateDocument(Term,Document),Lunce只支持所有替换,即整个Docuemnt要被替换掉,无法更新单独的Field。

2.4  Field的选项

选项分为三类:index、storing和term vector。

Index选项

Index.ANALYZED :分词后索引

Index.NOT_ANALYZED : 不分词直接索引,例如URL、系统路径等,用于精确检索 。

Index.ANALYZED_NO_NORMS : 相似Index.ANALYZED,但不存储NORM TERMS,节约内存但不支持Boost。

Index.NOT_ANALYZED_NO_NORMS : 相似Index.NOT_ANALYZED,但不存储NORM TERMS,节约内存但不支持Boost,很是经常使用 。

Index.NO : 根本不索引,因此不会被检索到

默认状况,Luncene会存储全部单词的出现位置,能够用Field.setOmitTermFreqAndPositions(true)关闭,可是会影响PhraseQuery和SpanQuery。

Store选项

Store.YES :存储原始value数值,可在检索后被提取 。

Store.NO :不存储原始数值,检索后没法从新提取。

CompressionTools 可用于压缩、解压缩byte数组。

Term Vector选项

Term Vector主要用于为类似搜索 提供支持 ,例如搜索cat,返回cat。

TermVector.YES :记录Term Vector

TermVector.WITH_POSITIONS :记录Term Vector以及每一个Term出现的位置

TermVector.WITH_OFFSETS :记录Term Vector以及每一个Term出现的偏移

TermVector.WITH_POSITIONS_OFFSETS :记录Term Vector以及出现的位置+偏移

TermVector.NO :不存储TermVector

若是Index选择了No,则TermVector必须选择No

将String外的类型做为Field的数据源

Reader:没法被STORE,默认TokenStream始终被分词和索引。

TokenStream:分词以后的结果做为源,没法被Store,始终analyzed并索引。

byte[] :没法被索引,没有TermVector,必须被Store.YES

与排序相关选项

数字Field能够用NumericField,若是是文本Field必须Field.Index.NOT_ANALYZED,才能排序,即保证这个Field只含有一个Token才能排序 

多值Field(Multi-valued Fields)

好比一本书有多个做者,怎么办呢?

一种方法是,添加多个同一key,不一样value的Field

  Document doc = new Document();
    for (int i = 0; i < authors.length; i++) {
      doc.add(new Field(“author”, authors[i],
                        Field.Store.YES,
                        Field.Index.ANALYZED));
    }

还有一种方法在第4章中提出。

2.5  Boost(提高)

boost能够对影响搜索返回结果的排序 

boost能够在index或者搜索时候完成,后者更具备灵活性可独立制定但耗费更多CPU。

Booost Doument

index时候boost将存储在NORMS TERM中。默认状况下,全部Document有相等的Boost,即1.0,能够手动提高一个Docuemnt的Boost数值。

Document.settBoost(float bei),bei是1.0的倍数。

Boost Field

也能够对Field进行索引,使用Document的Boost,对下属的Field都执行相同的Field。

单独对Field进行Boost

Field.boost(float)

注意:Lucene的Rank算法由多种因素组成,Boost只是一个因素之一,不是决定性因素 

Norms

boost的数值存储在Norms中,可能会致使Search时占用大量内存。所以可将其关闭:

设置NO_NORMS,或者再Field中指定Field.setOmitNorms(true)。

 2.6  对数字、日期、时间等进行索引

索引数字

有两种场景:

1.数字嵌入在Text中,例如“Be sure to include Form 1099 in your tax return”,而你想要搜索1099这个词。此时须要选择不分解数字的Analyzer ,例如WhitespaceAnalyzer或者StandardAnalyzer。而SimpleAnalyzer和StopAnalyzer会忽略数字,没法经过1099检出。

2.数字式单独的Field,2.9以后,Lucene支持了数字类型,使用NumericField便可:doc.add(new NumericField(“price”).setDoubleValue(19.99));此时,对数字Field使用字典树存储,

可向document中添加同样的NumericField数值,在NumericRangeQuery、NumericRangeFilter中以or的方式支持,可是排序中不支持。所以若是要排序,必须添加惟一的NumericField。

precisionStep控制了扫描精度,越小越精确但速度越慢。

索引日期和时间

方法是:将日期转化为时间戳(长整数) ,而后按照NumericField进行处理。

或者,若是不须要精确到毫秒,能够转化成秒处理

  doc.add(new NumericField(“day”) .setIntValue((int) (new Date().getTime()/24/3600)));

甚至对某一天进行索引而不是具体时间。

    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    doc.add(new NumericField(“dayOfMonth”)
            .setIntValue(cal.get(Calendar.DAY_OF_MONTH)));

 2.7  Field截断

   Lucene支持对字段的截断。IndexWriter.MaxFieldLength表示字段的最大长度,默认为MaxFieldLength.UNLIMITED,无限。

而MaxFieldLength.LIMITED表示有限制,能够经过setMaxFieldLength(int n)进行指定。

上述设定以后,只保留前n个字符。

能够经过setInfoStream(System.out)得到详细日志信息。

2.8  实时搜索

2.9后支持实时搜索,或者说很快的入索引–检索过程 

IndexReader  IndexWriter.getReader()

本方法将当即刷新Index的缓存,生效后当即返回IndexReader用于搜索。

2.9  优化索引

索引优化能够提高搜索速度 ,而非索引速度。它指的是将小索引文件合并成几个。

IndexWriter提供了几个优化方法:

optimize():将索引合并为一个段,完成前不会返回。可是太耗费资源。

optimize(int maxNumSegments):部分优化,优化到最多maxNumSegments个段?是优化于上述极端状况的这种,例如5个。

optimize(boolean doWait):通optimize(),可是它将当即返回。

optimize(int maxNumSegments, boolean doWait):同optimize(int maxNumSegments),可是将当即返回。

另外:在优化中会耗费大量的额外空间 。即旧的废弃段直到IndexWriter.commit()以后才能被移除 

2.10  Directory

Directory封装了存储的API,向上提供了抽象的接口,有如下几类:

SimpleFSDirectory:存储于本地磁盘使用java.io,不支持多线程,要本身加锁 

NIOFSDirectory:多线程可拓展,使用java.nio,支持多线程安全,可是Windows下有Bug 

MMapDirectory:内存映射存储(将文件映射到内存中进行操做,相似nmap)。

RAMDirectory:所有在内存中存储。

FileSwitchDirectory:使用两个目录,切换交替使用。

使用FSDirectory.open将自动挑选合适的Directory。也能够本身指定:

Directory ramDir = new RAMDirectory();
IndexWriter writer = new IndexWriter(ramDir, analyzer,  IndexWriter.MaxFieldLength.UNLIMITED);

RAMDirectory适用于内存比较小的状况。

能够拷贝索引以用于加速:

Directory ramDir = new RAMDirectory(otherDir);

或者

Directory.copy(Directory sourceDir,
               Directory destDir,
               boolean closeDirSrc);

2.11  线程安全、锁

线程、多JVM安全

任意多个IndexReaders可同时打开,能够跨JVM。

同一时间 只能打开一个 IndexWriter,独占写锁 。内建线程安全机制。

IndexReaders能够在IndexWriter打开的时候打开。

多线程间可共享IndexReader或者IndexWriter,他们是线程安全的,内建同步机制且性能较高。

经过远程文件系统共享IndexWriter

注意不要反复打开、关闭,不然会影响性能。

Index的锁

以文件锁的形式,名为write.lock。

若是在已经被锁定的状况下再建立一个IndexWriter,会遇到LockObtainFailedException。

也支持其余锁定方式,可是通常状况下无需改变它们。

IndexWriter.isLocked(Directory):检查某目录是否被锁。

IndexWriter.unlock(Directory):对某目录解锁,危险!。

注意!每次IndexWriter不管执行了什么操做,都要显示的close !不会自动释放锁的!

2.12  调试索引

2.14  高级的索引选项

IndexReader能够用来完全删除已经去除的Index,优势以下:

1.经过Document的具体Number来删除,更精确而IndexWriter不行。

2.IndexReader能够在删除后当即显示出来,而IndexWriter必须从新打开才能显示出来。

3.IndexReader拥有undeleteAll,能够撤销全部删除的索引(只对还没有merged的有效 )。

释放删除索引后的空间

能够调用expungeDeletes显示的释放空间,它将执行merge从而释放删除但仅仅作了标记,还没有释放的空间。

缓存和刷新

当添加索引、删除索引时候,在内存中创建了一个缓存以减小磁盘I/O,Lucene会按期把这些缓存中的改动放入Directory中便造成了一个segment(段)。

IndexWriter刷新缓存的条件是:

当内存中数据已经大于setRAMBufferSizeMB的指定。

当索引中的Document数量多于setMaxBufferedDocs的指定。

当索引被删除的数量多于setMaxBufferedDeleteTerms的指定。

上述条件之一发生时,即触发缓存刷进,它将创建新的Segment但不存入磁盘,只有当commit后才写入磁盘的index。

索引的commit

commit将改动持久化到本次索引中。只有调用commit后,再打开的IndexReader或者IndexSearcher才能看到最近一次commit以后的结果。

关闭close也将间接调用commit。

与commit相对的是rollback方法,它将撤销上次commit以后的全部改动。

commit很是耗时,不能常常调用。

“双缓冲”的commit

在图形界面开发中,常常有双缓冲技术,即一个用于被刷新,一个用于显示,两个之间互换使用。Lucene也支持这样的机制。

Lucene暴露了两个接口:

prepareCommit

Commit

prepareCommit比较慢,而调用prepareCommit后再调用Commit则会很是快。

删除策略

IndexDeletionPolicy决定了删除策略。能够决定是否保留以前的commit版本。

Lucene对ACID的事务支持

这主要是经过“同时只能打开一个IndexWriter”来实现的。

若是JVM、OS或者机器挂了,Lucene会自动恢复到上一个commit版本。

合并Merge

当索引有过多的Segnmnet的时候,须要进行合并Merge。优势:

1.减小了Segnment的文件数量

2.减小索引文件占用的空间大小。

MERGEPOLICY决定什么时候须要执行合并Merge

MERGEPOLICY

选择那些文件须要被合并,默认有两种策略:

LogByteSizeMergePolicy :根据Index大小决定是否须要合并

LogDocMergePolicy :根据Document的数量决定是否须要合并

分别经过

setMergeFactor

和setMaxMergeDocs来指定,具体参数见API。

MERGESCHEDULER

决定如何进行合并:

ConcurrentMergeScheduler,后台额外线程进行合并,可经过waitForMerges得知合并完成。

SerialMergeScheduler,在addDocument时候串行合并,使用统一线程。

 
 
 
 
相关文章
相关标签/搜索