简单的建立索引和索引查询
org.apache.lucene.index
---IndexWriter
一个IndexWriter创建和维护一个index
参数create会建议构造方法,决定是一个新的index被创建,或打开一个已经存在的index。
甚至IndexReader正在使用这个Index,你也可以设置create=true打开该index,旧的IndexReader 对象会继续查询已经使用“时间点”快照的index,不会看到最新的创建的Index直到IndexReader对象被重新打开.
IndexWriter也可以不使用create参数构造对象,如果在提供给该对象路上Index不存在,IndexWriter对象会创建一个新的索引,否则会打开一个存在的Index。
在上述两种情况中,使用addDocument()增加documents,使用deleteDocuments(Term)或deleteDocuments(Query)移除documents,使用updateDocument()更新document(更新仅仅是删除后添加整个document),在完成文档增删改后,应该调用close()文法关闭IndexWriter.
这些变更会缓存在内存中,定期地flush到Directory,从最近一次flush后当删除的document被缓存到一定量时( setMaxBufferedDeleteTerms(int)),或者添加的document缓存到一定的量,flush会被触发调用。添加documents时,用于存放document内存容量(setRAMBufferSizeMB(double))或数目达到设定的量都会触发flush。默认当内存容量达到16MB会触发flush。你可以通过设定一个大的内存容量提高建立索引的速度。注意,刷新缓冲时,只移除在IndexWriter对象内的缓存状态,输出到INDEX。这些改变在执行commit或close前,对IndexReader是不可见的。一次缓存输出可能引发一次或多次segment合并,合并默认运行在后台的一个线程中,因此不会阻断addDocument的调用。
构造方法的可以选择的autoCommit方法控制IndexReader对象感知同一个index的更改,当为false,更改不会被感知,直到调用close()或commit(),更改会做为一个新的
文件被冲刷到directory,但是不会执行(不会有新的segments_N 文件引用新的文件进行写操作,也不会同步进行持久存储),直到close()被调用。在调用close()前,某个原因导致了一个严重的错误,index不会更改(它会保持它开
始时的状态),你也可以调用rollback(),关闭写入流,不会提交任何的更改,同时删除所有flush而没有被使用的index文件。这是很有用的对实行简单的单一写事务性的semantics.你可以做两个阶段的提交,调用commit()前先调用prepareCommit()。当LUCENE与外部资源(比如:数据库)一起工作时,两个必须一起提交或者回退事务。
当autoCommit为true是,IndexWriter对象会周期性的自己提交(Deprecated:3.0,IndexWriter 将不会再接受autoCommit=true,这会引起hardwire失败)。你可以根据需要一直调用commit()。不能保证自动执行会被执行(这个被使用在每次完全合并后。Version2.4)如果强制提交可以调用commit()或者关闭writer。当运行在这种模式下,注意在优化或者进行segment合并时不要刷新reader,这会占用大量的磁盘空间。
无论是不是autoCommit,IndexReader或者IndexSearcher只会看到在Index打开时的那个时间点的内容。任何在reader打开后提交的内容,对其是不可见的,直到reader被重新打开。
如果Index不会添加大量的documents,同时需要最佳的查询性能,这时用于全部优化的optimize()方法或者用于部分优化的optimize(int)方法应该在Index关闭前调用。
IndexWriter对象在使用中,会生成一个对目录的锁文件,试着在同一个目录打开另一个IndexWriter对象将会导致LockObtainFailedException。如果一个IndexReader对象在同一个目录用于从索引中删除documents时,这个异常也会被抛出。
org.apache.lucene.document
----Document
Document 是建索引和查询的功能单位,一个Document是一组field,每一个field有一个名称和一个文本值,一个field可能存放在Document中,它是
在Document中被查询命中后做为返回值,因此每一个document应该代表性包含一个或多个存储型的field用来唯一标识它。
field没有被存储在document中,不允许从索引中取回(比如使用ScoreDoc.doc Searcher.doc(int) IndexReader.document(int))
Document doc = new Document();
doc.add(Fieldable field)添加一个field到document中
String doc.get(String name),返回指定名称field的字符值,如果不存在则返回NULL
Fieldable doc.getField(String name)返回给定名称的field,如果不存在则返回null
doc.removeField(String name)移除给定名称的field
List getFields()返回在Document中的所有field
doc.setBoost()//设置权重因子
java.lang.Object
org.apache.lucene.document.AbstractField
org.apache.lucene.document.Field
----Field
field是document中的一部分,每一个field有两个部分,名称和值,值可以是做为字符串或者Reader流提供,它们可能是一个原子性的关键字,
它不用进一步处理,这些关键字可能是日期,链接等,字段被选择性地存储在索引中,以使它们可能做为document中的命中点被返回。
Field.Index:详细指明这个field是否要建索引,或者以什么样的方式建索引
Field.Index.NO:不对这个字段建索引,因此这个字段是不会被查询到的,如文档的路径、属性可能我们不想让它被检索到
Field.Index.NO_NORMS:建立索引,但是不对它进行分词索引,存储的规范不起作用,索引时,将不考虑权重因子和Field长度标准,优点就
是它占较小的内存,所在的字段开始索引时都是这种状态。(Index the field's value without an Analyzer, and disable the storing of
norms. No norms means that index-time boosting and field length normalization will be disabled. The benefit is less memory usage as
norms take up one byte per indexed field for every document in the index. Note that once you index a given field <i>with</i> norms
enabled, disabling norms will have no effect. In other words, for NO_NORMS to have the above described effect on a field, all
instances of that field must be indexed with NO_NORMS from the beginning.)
Field.Index.TOKENIZED:分词建索引
Field.Index.UN_TOKENIZED:不使用分词器对其值建索引,如日期,作者等
Field.Store:详细指明这个field是否存储,或都以什么样的方式存储。
Field.TermVector:条件向量
Field(String name, Reader reader) :分词索引,但是不存储,相当于Field(String name,String
value,Field.Store.NO,Field.Index.TOKENIZED)
Field(String name, String value, Field.Store store, Field.Index index) :指定存储情况、索引情况。
Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector) :
org.apache.lucene.search.Searcher
org.apache.lucene.search.IndexSearcher
应用常常只需要通过Searcher.search(Query) 或者Searcher.search(Query,Filter)的方法继承得到。由于执行效率的原因,推荐在使用所有的查询
时,只打开同一个IndexSearcher。
注意:你只能在IndexSearcher没有关闭的期间,可以访问命中点集合,或则会抛IOException
org.apache.lucene.queryParser.QueryParser
这个类是,由JAVACC产生,最重要的方法就是parse(String),查询语法如下:
加号(+)或是减号(-),分别标志一个必须的条件或者一个禁用的条件,
由一个冒号(:)开头的条件,表示查询的field,这能够构造一个查询多个field的查询。
查询的句子可能是其中一种:
一个条件,表示所有的document符合这个条件,或者是一个嵌套的查询,由括号包裹,用+\-做为前缀使用于一组条件
查询语法如下:
Query ::=(Clause)*
Clause ::= ["+","-"][<TERM>":"](<TREM>|"("QUERY")")
评分机制
如果你想对每一个查询都设置权重因子在(评分时加大权重)标题field,使用你的查询句子用Query.setBoost()设置权重因子在你的标题field是最容
易的。
Field.setBoost()应该只使用在不同的Document中给不同的field设置权重因子时才使用,所以在查询时给所有的标题加相同的数值的权重因子是比较
容易的。这种方式可以试验权重数值,而不用重建索引,Field.setBoost()设置的值被建立在索引中,因而很难被改变。因此我推荐使用
Query.setBoost()来代替,为每一个查询的field构造成一个Query(自己编码,或者使用QueryParser),单独为每一个查询的field设置权重因子,然
后在执行时,联合这些查询建立一个逻辑查询(BooleanQuery)。我想这是有意义的。
If you wish to boost the title field for every query then it would be
easiest to boost the title clause of your query, with Query.setBoost().
Field.setBoost() should only be used when you want to give a field
different boosts in different documents, but since you want to boost all
titles by the same amount, you'll find it easier to boost at query time.
That way you can experiment with the boost amount without re-building
the index. The values of Field.setBoost() are built into the index and
are harder to change. Thus I recommend using Query.setBoost() instead.
Construct a query for each field to be searched (by hand, or with the
QueryParser), boost each of these field queries separately, then build a
BooleanQuery that combines these into a single Query that you then
execute. I hope that makes sense.
org.apache.lucene.index
---IndexWriter
一个IndexWriter创建和维护一个index
参数create会建议构造方法,决定是一个新的index被创建,或打开一个已经存在的index。
甚至IndexReader正在使用这个Index,你也可以设置create=true打开该index,旧的IndexReader 对象会继续查询已经使用“时间点”快照的index,不会看到最新的创建的Index直到IndexReader对象被重新打开.
IndexWriter也可以不使用create参数构造对象,如果在提供给该对象路上Index不存在,IndexWriter对象会创建一个新的索引,否则会打开一个存在的Index。
在上述两种情况中,使用addDocument()增加documents,使用deleteDocuments(Term)或deleteDocuments(Query)移除documents,使用updateDocument()更新document(更新仅仅是删除后添加整个document),在完成文档增删改后,应该调用close()文法关闭IndexWriter.
这些变更会缓存在内存中,定期地flush到Directory,从最近一次flush后当删除的document被缓存到一定量时( setMaxBufferedDeleteTerms(int)),或者添加的document缓存到一定的量,flush会被触发调用。添加documents时,用于存放document内存容量(setRAMBufferSizeMB(double))或数目达到设定的量都会触发flush。默认当内存容量达到16MB会触发flush。你可以通过设定一个大的内存容量提高建立索引的速度。注意,刷新缓冲时,只移除在IndexWriter对象内的缓存状态,输出到INDEX。这些改变在执行commit或close前,对IndexReader是不可见的。一次缓存输出可能引发一次或多次segment合并,合并默认运行在后台的一个线程中,因此不会阻断addDocument的调用。
构造方法的可以选择的autoCommit方法控制IndexReader对象感知同一个index的更改,当为false,更改不会被感知,直到调用close()或commit(),更改会做为一个新的
文件被冲刷到directory,但是不会执行(不会有新的segments_N 文件引用新的文件进行写操作,也不会同步进行持久存储),直到close()被调用。在调用close()前,某个原因导致了一个严重的错误,index不会更改(它会保持它开
始时的状态),你也可以调用rollback(),关闭写入流,不会提交任何的更改,同时删除所有flush而没有被使用的index文件。这是很有用的对实行简单的单一写事务性的semantics.你可以做两个阶段的提交,调用commit()前先调用prepareCommit()。当LUCENE与外部资源(比如:数据库)一起工作时,两个必须一起提交或者回退事务。
当autoCommit为true是,IndexWriter对象会周期性的自己提交(Deprecated:3.0,IndexWriter 将不会再接受autoCommit=true,这会引起hardwire失败)。你可以根据需要一直调用commit()。不能保证自动执行会被执行(这个被使用在每次完全合并后。Version2.4)如果强制提交可以调用commit()或者关闭writer。当运行在这种模式下,注意在优化或者进行segment合并时不要刷新reader,这会占用大量的磁盘空间。
无论是不是autoCommit,IndexReader或者IndexSearcher只会看到在Index打开时的那个时间点的内容。任何在reader打开后提交的内容,对其是不可见的,直到reader被重新打开。
如果Index不会添加大量的documents,同时需要最佳的查询性能,这时用于全部优化的optimize()方法或者用于部分优化的optimize(int)方法应该在Index关闭前调用。
IndexWriter对象在使用中,会生成一个对目录的锁文件,试着在同一个目录打开另一个IndexWriter对象将会导致LockObtainFailedException。如果一个IndexReader对象在同一个目录用于从索引中删除documents时,这个异常也会被抛出。
org.apache.lucene.document
----Document
Document 是建索引和查询的功能单位,一个Document是一组field,每一个field有一个名称和一个文本值,一个field可能存放在Document中,它是
在Document中被查询命中后做为返回值,因此每一个document应该代表性包含一个或多个存储型的field用来唯一标识它。
field没有被存储在document中,不允许从索引中取回(比如使用ScoreDoc.doc Searcher.doc(int) IndexReader.document(int))
Document doc = new Document();
doc.add(Fieldable field)添加一个field到document中
String doc.get(String name),返回指定名称field的字符值,如果不存在则返回NULL
Fieldable doc.getField(String name)返回给定名称的field,如果不存在则返回null
doc.removeField(String name)移除给定名称的field
List getFields()返回在Document中的所有field
doc.setBoost()//设置权重因子
java.lang.Object
org.apache.lucene.document.AbstractField
org.apache.lucene.document.Field
----Field
field是document中的一部分,每一个field有两个部分,名称和值,值可以是做为字符串或者Reader流提供,它们可能是一个原子性的关键字,
它不用进一步处理,这些关键字可能是日期,链接等,字段被选择性地存储在索引中,以使它们可能做为document中的命中点被返回。
Field.Index:详细指明这个field是否要建索引,或者以什么样的方式建索引
Field.Index.NO:不对这个字段建索引,因此这个字段是不会被查询到的,如文档的路径、属性可能我们不想让它被检索到
Field.Index.NO_NORMS:建立索引,但是不对它进行分词索引,存储的规范不起作用,索引时,将不考虑权重因子和Field长度标准,优点就
是它占较小的内存,所在的字段开始索引时都是这种状态。(Index the field's value without an Analyzer, and disable the storing of
norms. No norms means that index-time boosting and field length normalization will be disabled. The benefit is less memory usage as
norms take up one byte per indexed field for every document in the index. Note that once you index a given field <i>with</i> norms
enabled, disabling norms will have no effect. In other words, for NO_NORMS to have the above described effect on a field, all
instances of that field must be indexed with NO_NORMS from the beginning.)
Field.Index.TOKENIZED:分词建索引
Field.Index.UN_TOKENIZED:不使用分词器对其值建索引,如日期,作者等
Field.Store:详细指明这个field是否存储,或都以什么样的方式存储。
Field.TermVector:条件向量
Field(String name, Reader reader) :分词索引,但是不存储,相当于Field(String name,String
value,Field.Store.NO,Field.Index.TOKENIZED)
Field(String name, String value, Field.Store store, Field.Index index) :指定存储情况、索引情况。
Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector) :
org.apache.lucene.search.Searcher
org.apache.lucene.search.IndexSearcher
应用常常只需要通过Searcher.search(Query) 或者Searcher.search(Query,Filter)的方法继承得到。由于执行效率的原因,推荐在使用所有的查询
时,只打开同一个IndexSearcher。
注意:你只能在IndexSearcher没有关闭的期间,可以访问命中点集合,或则会抛IOException
org.apache.lucene.queryParser.QueryParser
这个类是,由JAVACC产生,最重要的方法就是parse(String),查询语法如下:
加号(+)或是减号(-),分别标志一个必须的条件或者一个禁用的条件,
由一个冒号(:)开头的条件,表示查询的field,这能够构造一个查询多个field的查询。
查询的句子可能是其中一种:
一个条件,表示所有的document符合这个条件,或者是一个嵌套的查询,由括号包裹,用+\-做为前缀使用于一组条件
查询语法如下:
Query ::=(Clause)*
Clause ::= ["+","-"][<TERM>":"](<TREM>|"("QUERY")")
评分机制
如果你想对每一个查询都设置权重因子在(评分时加大权重)标题field,使用你的查询句子用Query.setBoost()设置权重因子在你的标题field是最容
易的。
Field.setBoost()应该只使用在不同的Document中给不同的field设置权重因子时才使用,所以在查询时给所有的标题加相同的数值的权重因子是比较
容易的。这种方式可以试验权重数值,而不用重建索引,Field.setBoost()设置的值被建立在索引中,因而很难被改变。因此我推荐使用
Query.setBoost()来代替,为每一个查询的field构造成一个Query(自己编码,或者使用QueryParser),单独为每一个查询的field设置权重因子,然
后在执行时,联合这些查询建立一个逻辑查询(BooleanQuery)。我想这是有意义的。
If you wish to boost the title field for every query then it would be
easiest to boost the title clause of your query, with Query.setBoost().
Field.setBoost() should only be used when you want to give a field
different boosts in different documents, but since you want to boost all
titles by the same amount, you'll find it easier to boost at query time.
That way you can experiment with the boost amount without re-building
the index. The values of Field.setBoost() are built into the index and
are harder to change. Thus I recommend using Query.setBoost() instead.
Construct a query for each field to be searched (by hand, or with the
QueryParser), boost each of these field queries separately, then build a
BooleanQuery that combines these into a single Query that you then
execute. I hope that makes sense.
package learn;
/**
* 建立索引
* @author Administrator
*
*/
import java.io.*;
import org.apache.lucene.index.*;
import org.apache.lucene.store.*;
import org.apache.lucene.analysis.standard.*;
import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.cn.*;
import org.apache.lucene.document.*;
public class BuildIndex {
private final static String INDEX_DIR_PATH="e:\\index";
private final static String test_dir="e:\\txt";
/**
* @param args
*/
/**
* 获得IndexWriter对象
*/
public IndexWriter getIndexWriter(Directory indexDir,Analyzer analyzer) throws IOException{
return new IndexWriter(indexDir,false,analyzer,true);
}
/**
* 建立索引
* @throws IOException
*/
public void build() throws IOException {
Directory indexDir = FSDirectory.getDirectory(INDEX_DIR_PATH);
Analyzer analyzer = new ChineseAnalyzer();
IndexWriter writer = getIndexWriter(indexDir,analyzer);
indexFile(writer,new File(test_dir));
writer.flush();
writer.optimize();
writer.close();
}
/**
* 索引所有文件
* @param writer
* @param f
* @throws IOException
*/
public void indexFile(IndexWriter writer,File f) throws IOException{
if(!f.exists()){
throw new IOException("文件不存在");
}
if(f.isDirectory()){
File[] fArr = f.listFiles();
for(int i=0;i<fArr.length;i++){
indexFile(writer,fArr[i]);//递归
}
}else{
FileReader fr = new FileReader(f);
Document doc = new Document();
doc.add(new Field("content",fr));//建立索引,不存储
doc.add(new Field("path",f.getAbsolutePath(),Field.Store.YES,Field.Index.NO));//存储不建索引
writer.addDocument(doc);//添加记录
}
}
public static void main(String[] args) throws IOException{
BuildIndex build = new BuildIndex();
build.build();
}
}
package learn;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.cn.ChineseAnalyzer;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.queryParser.*;
import org.apache.lucene.document.*;
import java.io.*;
public class TestSearch {
private static IndexSearcher searcher = null;
private final static String INDEX_DIR_PATH="e:\\index";
public IndexSearcher getInstance() throws IOException{
if(searcher==null){
Directory indexDir = FSDirectory.getDirectory(INDEX_DIR_PATH);
searcher = new IndexSearcher(indexDir);
}
return searcher;
}
/**
* 查询
* @param searchStr
* @throws IOException
* @throws ParseException
*/
public void search(String searchStr) throws IOException,ParseException{
Analyzer analyzer = new ChineseAnalyzer();
Query q =new QueryParser("content",analyzer).parse(searchStr);
Hits hits = getInstance().search(q);
HitIterator it = (HitIterator)hits.iterator();
while(it.hasNext()){
Document doc = ((Hit)it.next()).getDocument();
System.out.println("path:"+doc.getField("path")+"");
}
}
public static void main(String[] args) throws IOException,ParseException{
TestSearch test = new TestSearch();
test.search("网页 OR 服务器");
}
}