8) 第二章 索引:基本索引操作

    先上示例代码,原意看的就看,不愿意看的先略过,回头再对照着看也行:

import java.io.IOException;

import junit.framework.TestCase;

import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;

/**
 * 示例代码:
 * Lucene中document的基本操作
 * (新增、删除、更新)
 */
public class IndexingTest extends TestCase {

	protected String[] ids = { "1", "2" };
	protected String[] unindexed = { "Netherlands", "Italy" };
	protected String[] unstored = { 
			"Amsterdam has lots of bridges",
			"Venice has lots of canals" };
	protected String[] text = { "Amsterdam", "Venice" };

	private Directory directory;

	//---------------------------------------------------------------【新增】
	protected void setUp() throws Exception {
		// A setUp()方法会在每一个测试之前运行,它先创建了一个RAMDirectory用来存储索引信息
		directory = new RAMDirectory();
		// B 参见 D#
		IndexWriter writer = getWriter(); 
		
		// C 遍历所有内容,创建Document及Fields并将Document加入索引
		for (int i = 0; i < ids.length; i++) {
			Document doc = new Document();
			doc.add(new Field("id", ids[i], Field.Store.YES,
					Field.Index.NOT_ANALYZED));
			doc.add(new Field("country", unindexed[i], Field.Store.YES,
					Field.Index.NO));
			doc.add(new Field("contents", unstored[i], Field.Store.NO,
					Field.Index.ANALYZED));
			doc.add(new Field("city", text[i], Field.Store.YES,
					Field.Index.ANALYZED));
			writer.addDocument(doc);
		}
		writer.close();
	}

	// D 在RAMDirectory上创建IndexWriter
	private IndexWriter getWriter() throws IOException { 
		return new IndexWriter(directory, new WhitespaceAnalyzer(),
				IndexWriter.MaxFieldLength.UNLIMITED);
	}

	protected int getHitCount(String fieldName, String searchString)
			throws IOException {
		// E 创建Searcher
		IndexSearcher searcher = new IndexSearcher(directory); 
		Term t = new Term(fieldName, searchString);
		// F 创建简单的单一Term的查询
		Query query = new TermQuery(t); 
		// G 获取结果集的数量
		int hitCount = TestUtil.hitCount(searcher, query); 
		searcher.close();
		return hitCount;
	}

	public void testIndexWriter() throws IOException {
		IndexWriter writer = getWriter();
		// H 验证writer中document的数量
		assertEquals(ids.length, writer.numDocs()); 
		writer.close();
	}

	public void testIndexReader() throws IOException {
		IndexReader reader = IndexReader.open(directory);
		// I 验证searcher中document的数量
		assertEquals(ids.length, reader.maxDoc());
		assertEquals(ids.length, reader.numDocs());
		reader.close();
	}
	
	//---------------------------------------------------------------【删除】
	public void testDeleteBeforeIndexMerge() throws IOException {
		IndexWriter writer = getWriter();
		// 1 索引中有2个document
		assertEquals(2, writer.numDocs());
		// 2 删除第一个document
		writer.deleteDocuments(new Term("id", "1"));
		writer.commit();
		// 3 验证索引中包含了删除操作
		assertTrue(writer.hasDeletions()); 
		// 4 一个被索引的document 一个被删除的document
		assertEquals(2, writer.maxDoc());
		// 5 仅有一个被索引的document
		assertEquals(1, writer.numDocs());
		writer.close();
	}

	public void testDeleteAfterIndexMerge() throws IOException {
		IndexWriter writer = getWriter();
		assertEquals(2, writer.numDocs());
		writer.deleteDocuments(new Term("id", "1"));
		// 5 优化压缩索引
		writer.optimize(); 
		writer.commit();
		assertFalse(writer.hasDeletions());
		// 6 合并后仅剩一个被索引的document
		assertEquals(1, writer.maxDoc()); 
		assertEquals(1, writer.numDocs());
		writer.close();
	}
	
	//---------------------------------------------------------------【更新】
	public void testUpdate() throws IOException {
		assertEquals(1, getHitCount("city", "Amsterdam"));
		IndexWriter writer = getWriter();
		
		// 1 创建新的document用来更新, 其city域为Haag
		Document doc = new Document();
		doc.add(new Field("id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED));
		doc.add(new Field("country", "Netherlands", Field.Store.YES, Field.Index.NO));
		doc.add(new Field("contents", "Amsterdam has lots of bridges", Field.Store.NO, Field.Index.ANALYZED));
		doc.add(new Field("city", "Haag", Field.Store.YES, Field.Index.ANALYZED));
		
		// 2 用新创建的document替换之前的
		writer.updateDocument(new Term("id", "1"), doc);
		writer.close();
		// 3 验证原来的document不存在了
		assertEquals(0, getHitCount("city", "Amsterdam"));
		// 4 验证新的document被加入索引中
		assertEquals(1, getHitCount("city", "Haag")); 
	}

}

 还需要一个辅助类上面的代码才能够正确运行:

import java.io.IOException;

import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;

public class TestUtil {

	public static int hitCount(IndexSearcher searcher, Query query)	throws IOException {
		return searcher.search(query, 1).totalHits;
	}
	
}

1. 新增Document

    新增方法有两个:

addDocument(Document) - 采用默认的分析器(analyzer)添加文档
addDocument(Document, Analyzer) - 采用指定的分析器添加文档

    需要注意的是,搜索阶段所使用的分析器必须与索引阶段使用的一致,才能正确的搜索到相应结果。

    为了简化问题,示例代码中,待索引的内容为String型,而在实际应用中,它们可能来自PDF等,需要先解析哦!

    在我们的例子中,构造IndexWriter共传入了三个参数:第一个参数Directory表示索引存储的位置(这里是内存);第二个参数是索引过程中所使用的分析器;第三个参数 MaxFieldLength.UNLIMITED 示意IndexWriter要索引文档中所有的词元。如果IndexWriter检测到指定的Directory中并没有index文件存在,它会先创建一个,否则,IndexWriter会仅为其增加新的document.

    IndexWriter还有其它许多构造方法,其中一些接受String或File参数以创建FSDirectory, 还有一些允许你指定是想强制性地创建新索引还是覆盖之前的。更高级的构造器,则允许你设计自己的索引删除策略或是提交策略等。

2. 删除Document

    删除方法有四个:      

deleteDocuments(Term);
deleteDocuments(Term[]);
deleteDocuments(Query);
deleteDocuments(Query[]);

    从它们的参数中你也许已经猜出个大概。你可以删掉包含了特定Term的文档,或是符合Query查询条件的。通常,为你的document设计一个名为"id"的Field是个不错的办法,这样就你可以为每个document设置唯一的id,然后通过如下方式删除它:

      writer.deleteDocument(new Term("id", documentID));

    同新增方法一样,删除操作不会被立即写入磁盘中,你需要手动调用commit()以提交所有变化或者调用close()关闭writer.

    示例代码中调用的两个方法 maxDoc() 和 numDocs(), 它们的区别在于:

      maxDoc()   会返回索引中的文档(document)总数,包括删除的和未删除的

      numDocs()  仅返回索引中未被删除的文档数

3. 更新Document

    更新方法有两个:

updateDocument(Term, Document);
updateDocument(Term, Document, Analyzer);

    很明显它们的区别在于是否指定特定分析器。

    需要指出的是,更新操作实质上仅仅是删除和新增的加合。该操作会先依据指定Term删除相应的document,然后依据第二个参数为索引增加新的document. 遗憾的是,即便你仅修改了document中的某个field,你也需要删除整个document,再为其增加新的document,也就是说,此操作的最小粒度是document而非field.

猜你喜欢

转载自bun-ny.iteye.com/blog/1071641
今日推荐