索引 基本索引操作

本文详细介绍了使用Lucene进行的基本索引操作,包括添加、删除、恢复及更新索引中的文档。通过示例代码展示了如何实现这些操作,并解释了关键方法的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要内容为读Lucene in action 所得

基本的索引操作包括:

  1. 向索引添加文档
  2. 删除索引中的文档
  3. 恢复被删除的文档
  4. 更新索引中的文档

下面就这个四个方面分别详述(以测试类来进行示例)

1.向索引添加文档

这个抽象类作为基础,此后2,3,4中都会实现此抽象类,其中setUp函数为JUnit测试时候首先调用的函数,在此函数中做了初始化工作

 

注意 Field的构造函数的不同用法,我根据自己的理解加了注释

 

import java.io.IOException;

import junit.framework.TestCase;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;


/**
 * @author xusulong
 *
 */
public abstract class BaseIndexingTestCase extends TestCase {
	protected String[] keywords = {"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"};
	protected Directory dir;
	
	protected void setUp() throws IOException {
		String indexDir = 
			System.getProperty("java.io.tmpdir", "tmp") + 
			System.getProperty("file.separator") + "index-dir";
		dir = FSDirectory.getDirectory(indexDir);
		addDocument(dir);
	}
	
	protected void addDocument(Directory dir) throws IOException{
		IndexWriter writer = new IndexWriter(dir, getAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
		writer.setUseCompoundFile(isCompound());
		
		for(int i = 0; i < keywords.length; i ++) {
			Document doc = new Document();
			//不分析,索引,存储
			doc.add(new Field("id", keywords[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.optimize();
		writer.close();
	}
	/**
	 * 默认分析器
	 * @return
	 */
	protected Analyzer getAnalyzer() {
		return new SimpleAnalyzer();
	}
	
	protected boolean isCompound() {
		return true;
	}
}
 

 

2.删除索引中的文档

文档的删除是通过IndexReader类实现的(从某种程度上说,这个名字显得不太合适,因为没有完全表达出该类真实的功能)。这个类并没有立即从索引中删除文档,而只是在这些文档上加一个删除标记,直到IndexReader调用close()后才真正将它们删除

这个类继承自1.中的BaseIndexTestCase

import java.io.IOException;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;


/**
 * @author xusulong
 *
 */
public class DocumentDeleteTest extends BaseIndexingTestCase {

	
	public void testDeleteBeforeIndexMerge() throws IOException {
		IndexReader reader = IndexReader.open(dir);
		assertEquals(2, reader.maxDoc());
		assertEquals(2, reader.numDocs());
		reader.deleteDocument(1);
		
		//isDelete(int)用于检查一个特定变化的文档的状态
		assertTrue(reader.isDeleted(1));
		//hasDeletions()用于检查一个所有是否包含了带有删除标记的文档
		assertTrue(reader.hasDeletions());
		assertEquals(2, reader.maxDoc());
		assertEquals(1, reader.numDocs());
		
		reader.close();
		
		reader = IndexReader.open(dir);
		assertEquals(2, reader.maxDoc());
		assertEquals(1, reader.numDeletedDocs());
		
		reader.close();
	}
	
	public void testDeleteAfterIndexMerge() throws IOException {
		IndexReader reader = IndexReader.open(dir);
		assertEquals(2, reader.maxDoc());
		assertEquals(2, reader.numDocs());
		reader.deleteDocument(1);
		reader.close();
		
		IndexWriter writer = new IndexWriter(dir, getAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);
		writer.optimize();
		writer.close();
		
		reader = IndexReader.open(dir);
		//删除标记reader close后没有了,所有false
		assertTrue(reader.isDeleted(1));
		assertTrue(reader.hasDeletions());
		assertEquals(1, reader.maxDoc());
		assertEquals(1, reader.numDocs());
		
		reader.close();
		
	}
}

 IndexReader中两个易混淆的方法maxDoc() 和 numDocs()的区别:前者返回下一个 可得到的文档的内部编号,后者返回索引中的文档数量。因为索引仅仅包含两个文档,所以numDocs()返回 2;而文档编号是从0开始的,所以maxDoc()也返回2。

注:每一个Lucene文档都有一个惟一的内部编号。这些编号的分配不是永久的,因为当索引中的段合并时,Lucene会重新分配其编号。因此,你不能认为某一特定的文档会一直拥有某一个固定的内部编号。

 

3.恢复被删除的文档

由于文档的删除是推迟到IndexReader实例关闭的时候才进行的,所以Lucene允许在文档被标记删除但还没有执行最后的删除操作之前,恢复被标 记为删除的文档。可以通过调用undelete()方法移除索引目录中的.del文件来恢复被删除的文件。随后,关闭IndexReader实例,这样就 保留了索引中所有标记为删除的文档。如果用IndexReader实例标记了删除文档,那么只有调用同一个IndexReader实例的 undeleteAll()方法,才能在最初的位置恢复各个被标记为删除的文档(也就是说,IndexReadel实例只能处理自身标记的删除文档,无法 恢复其他实例的文档)。

4.更新索引中的文档

Lucene不提供update(Document)方法。为了达到更新的目的,必须首先从一个索引中删除待更新的文档,然后将修改过的文档重新添加到索引中

import java.io.IOException;

import org.apache.lucene.analysis.Analyzer;
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.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;


/**
 * @author xusulong
 *
 */
public class DocumentUpdateTest extends BaseIndexingTestCase {
	public void testUpdate() throws IOException {
		assertEquals(1, getHitCount("city", "Amsterdam"));
		
		IndexReader reader = IndexReader.open(dir);
		//删除在city域中包含Amsterdam的文档
		reader.deleteDocuments(new Term("city", "Amsterdam"));
		reader.close();
		//核实该文档是否被删除
		assertEquals(0, getHitCount("city", "Amsterdam"));
		
		IndexWriter writer = new IndexWriter(dir, getAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);
		
		Document doc = new Document();
		//重新添加city域的值为"Haag"的文档
		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));
		writer.addDocument(doc);
		writer.optimize();
		writer.close();
		//确认文档已经更新
		assertEquals(1, getHitCount("city", "Haag"));
	}
	
	protected Analyzer getAnalyzer() {
		return new WhitespaceAnalyzer();
	}
	
	private int getHitCount(String filedName, String searchString) throws IOException{
		IndexSearcher searcher = new IndexSearcher(dir);
		Term t = new Term(filedName, searchString);
		Query query = new TermQuery(t);
		
		TopDocs topDocs = searcher.search(query, 100);
		ScoreDoc[] hits = topDocs.scoreDocs;
		
		int hitCount = hits.length;
		searcher.close();
		return hitCount;
	}
}
 
通过批量删除的方式进行更新操作

我们的例子删除和重新添加的只是一个文档对象。如果你需要删除和添加多个文档对象,最好批量地进行这些操作。步骤如下:

1.打开IndexReader对象。

2.删除所有需要删除的Document对象。

3.关闭IndexReader对象。

4.打开IndexWriter对象。

5.添加所有需要添加的Document对象。

6.关闭IndexWriter对象。

要记住:对文档对象的批量删除和索引操作,总要比交替地删除和添加操作速度快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值