--- /dev/null
+package org.apache.lucene.index;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.MockDirectoryWrapper;
+import org.apache.lucene.util.LuceneTestCase;
+
+import static org.apache.lucene.index.TestIndexReader.addDoc;
+import static org.apache.lucene.index.TestIndexReader.addDocumentWithFields;
+import static org.apache.lucene.index.TestIndexReader.assertTermDocsCount;
+import static org.apache.lucene.index.TestIndexReader.createDocument;
+
+public class TestIndexReaderDelete extends LuceneTestCase {
+ private void deleteReaderReaderConflict(boolean optimize) throws IOException {
+ Directory dir = newDirectory();
+
+ Term searchTerm1 = new Term("content", "aaa");
+ Term searchTerm2 = new Term("content", "bbb");
+ Term searchTerm3 = new Term("content", "ccc");
+
+ // add 100 documents with term : aaa
+ // add 100 documents with term : bbb
+ // add 100 documents with term : ccc
+ IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.CREATE));
+ for (int i = 0; i < 100; i++) {
+ addDoc(writer, searchTerm1.text());
+ addDoc(writer, searchTerm2.text());
+ addDoc(writer, searchTerm3.text());
+ }
+ if(optimize)
+ writer.optimize();
+ writer.close();
+
+ // OPEN TWO READERS
+ // Both readers get segment info as exists at this time
+ IndexReader reader1 = IndexReader.open(dir, false);
+ assertEquals("first opened", 100, reader1.docFreq(searchTerm1));
+ assertEquals("first opened", 100, reader1.docFreq(searchTerm2));
+ assertEquals("first opened", 100, reader1.docFreq(searchTerm3));
+ assertTermDocsCount("first opened", reader1, searchTerm1, 100);
+ assertTermDocsCount("first opened", reader1, searchTerm2, 100);
+ assertTermDocsCount("first opened", reader1, searchTerm3, 100);
+
+ IndexReader reader2 = IndexReader.open(dir, false);
+ assertEquals("first opened", 100, reader2.docFreq(searchTerm1));
+ assertEquals("first opened", 100, reader2.docFreq(searchTerm2));
+ assertEquals("first opened", 100, reader2.docFreq(searchTerm3));
+ assertTermDocsCount("first opened", reader2, searchTerm1, 100);
+ assertTermDocsCount("first opened", reader2, searchTerm2, 100);
+ assertTermDocsCount("first opened", reader2, searchTerm3, 100);
+
+ // DELETE DOCS FROM READER 2 and CLOSE IT
+ // delete documents containing term: aaa
+ // when the reader is closed, the segment info is updated and
+ // the first reader is now stale
+ reader2.deleteDocuments(searchTerm1);
+ assertEquals("after delete 1", 100, reader2.docFreq(searchTerm1));
+ assertEquals("after delete 1", 100, reader2.docFreq(searchTerm2));
+ assertEquals("after delete 1", 100, reader2.docFreq(searchTerm3));
+ assertTermDocsCount("after delete 1", reader2, searchTerm1, 0);
+ assertTermDocsCount("after delete 1", reader2, searchTerm2, 100);
+ assertTermDocsCount("after delete 1", reader2, searchTerm3, 100);
+ reader2.close();
+
+ // Make sure reader 1 is unchanged since it was open earlier
+ assertEquals("after delete 1", 100, reader1.docFreq(searchTerm1));
+ assertEquals("after delete 1", 100, reader1.docFreq(searchTerm2));
+ assertEquals("after delete 1", 100, reader1.docFreq(searchTerm3));
+ assertTermDocsCount("after delete 1", reader1, searchTerm1, 100);
+ assertTermDocsCount("after delete 1", reader1, searchTerm2, 100);
+ assertTermDocsCount("after delete 1", reader1, searchTerm3, 100);
+
+
+ // ATTEMPT TO DELETE FROM STALE READER
+ // delete documents containing term: bbb
+ try {
+ reader1.deleteDocuments(searchTerm2);
+ fail("Delete allowed from a stale index reader");
+ } catch (IOException e) {
+ /* success */
+ }
+
+ // RECREATE READER AND TRY AGAIN
+ reader1.close();
+ reader1 = IndexReader.open(dir, false);
+ assertEquals("reopened", 100, reader1.docFreq(searchTerm1));
+ assertEquals("reopened", 100, reader1.docFreq(searchTerm2));
+ assertEquals("reopened", 100, reader1.docFreq(searchTerm3));
+ assertTermDocsCount("reopened", reader1, searchTerm1, 0);
+ assertTermDocsCount("reopened", reader1, searchTerm2, 100);
+ assertTermDocsCount("reopened", reader1, searchTerm3, 100);
+
+ reader1.deleteDocuments(searchTerm2);
+ assertEquals("deleted 2", 100, reader1.docFreq(searchTerm1));
+ assertEquals("deleted 2", 100, reader1.docFreq(searchTerm2));
+ assertEquals("deleted 2", 100, reader1.docFreq(searchTerm3));
+ assertTermDocsCount("deleted 2", reader1, searchTerm1, 0);
+ assertTermDocsCount("deleted 2", reader1, searchTerm2, 0);
+ assertTermDocsCount("deleted 2", reader1, searchTerm3, 100);
+ reader1.close();
+
+ // Open another reader to confirm that everything is deleted
+ reader2 = IndexReader.open(dir, false);
+ assertTermDocsCount("reopened 2", reader2, searchTerm1, 0);
+ assertTermDocsCount("reopened 2", reader2, searchTerm2, 0);
+ assertTermDocsCount("reopened 2", reader2, searchTerm3, 100);
+ reader2.close();
+
+ dir.close();
+ }
+
+ private void deleteReaderWriterConflict(boolean optimize) throws IOException {
+ //Directory dir = new RAMDirectory();
+ Directory dir = newDirectory();
+
+ Term searchTerm = new Term("content", "aaa");
+ Term searchTerm2 = new Term("content", "bbb");
+
+ // add 100 documents with term : aaa
+ IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.CREATE));
+ for (int i = 0; i < 100; i++) {
+ addDoc(writer, searchTerm.text());
+ }
+ writer.close();
+
+ // OPEN READER AT THIS POINT - this should fix the view of the
+ // index at the point of having 100 "aaa" documents and 0 "bbb"
+ IndexReader reader = IndexReader.open(dir, false);
+ assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
+ assertEquals("first docFreq", 0, reader.docFreq(searchTerm2));
+ assertTermDocsCount("first reader", reader, searchTerm, 100);
+ assertTermDocsCount("first reader", reader, searchTerm2, 0);
+
+ // add 100 documents with term : bbb
+ writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
+ for (int i = 0; i < 100; i++) {
+ addDoc(writer, searchTerm2.text());
+ }
+
+ // REQUEST OPTIMIZATION
+ // This causes a new segment to become current for all subsequent
+ // searchers. Because of this, deletions made via a previously open
+ // reader, which would be applied to that reader's segment, are lost
+ // for subsequent searchers/readers
+ if(optimize)
+ writer.optimize();
+ writer.close();
+
+ // The reader should not see the new data
+ assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
+ assertEquals("first docFreq", 0, reader.docFreq(searchTerm2));
+ assertTermDocsCount("first reader", reader, searchTerm, 100);
+ assertTermDocsCount("first reader", reader, searchTerm2, 0);
+
+
+ // DELETE DOCUMENTS CONTAINING TERM: aaa
+ // NOTE: the reader was created when only "aaa" documents were in
+ int deleted = 0;
+ try {
+ deleted = reader.deleteDocuments(searchTerm);
+ fail("Delete allowed on an index reader with stale segment information");
+ } catch (StaleReaderException e) {
+ /* success */
+ }
+
+ // Re-open index reader and try again. This time it should see
+ // the new data.
+ reader.close();
+ reader = IndexReader.open(dir, false);
+ assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
+ assertEquals("first docFreq", 100, reader.docFreq(searchTerm2));
+ assertTermDocsCount("first reader", reader, searchTerm, 100);
+ assertTermDocsCount("first reader", reader, searchTerm2, 100);
+
+ deleted = reader.deleteDocuments(searchTerm);
+ assertEquals("deleted count", 100, deleted);
+ assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm));
+ assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm2));
+ assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
+ assertTermDocsCount("deleted termDocs", reader, searchTerm2, 100);
+ reader.close();
+
+ // CREATE A NEW READER and re-test
+ reader = IndexReader.open(dir, false);
+ assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm2));
+ assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
+ assertTermDocsCount("deleted termDocs", reader, searchTerm2, 100);
+ reader.close();
+ dir.close();
+ }
+
+ public void testBasicDelete() throws IOException {
+ Directory dir = newDirectory();
+
+ IndexWriter writer = null;
+ IndexReader reader = null;
+ Term searchTerm = new Term("content", "aaa");
+
+ // add 100 documents with term : aaa
+ writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+ writer.setInfoStream(VERBOSE ? System.out : null);
+ for (int i = 0; i < 100; i++) {
+ addDoc(writer, searchTerm.text());
+ }
+ writer.close();
+
+ // OPEN READER AT THIS POINT - this should fix the view of the
+ // index at the point of having 100 "aaa" documents and 0 "bbb"
+ reader = IndexReader.open(dir, false);
+ assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
+ assertTermDocsCount("first reader", reader, searchTerm, 100);
+ reader.close();
+
+ // DELETE DOCUMENTS CONTAINING TERM: aaa
+ int deleted = 0;
+ reader = IndexReader.open(dir, false);
+ deleted = reader.deleteDocuments(searchTerm);
+ assertEquals("deleted count", 100, deleted);
+ assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm));
+ assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
+
+ // open a 2nd reader to make sure first reader can
+ // commit its changes (.del) while second reader
+ // is open:
+ IndexReader reader2 = IndexReader.open(dir, false);
+ reader.close();
+
+ // CREATE A NEW READER and re-test
+ reader = IndexReader.open(dir, false);
+ assertEquals("deleted docFreq", 0, reader.docFreq(searchTerm));
+ assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
+ reader.close();
+ reader2.close();
+ dir.close();
+ }
+
+ public void testDeleteReaderReaderConflictUnoptimized() throws IOException {
+ deleteReaderReaderConflict(false);
+ }
+
+ public void testDeleteReaderReaderConflictOptimized() throws IOException {
+ deleteReaderReaderConflict(true);
+ }
+
+ public void testDeleteReaderWriterConflictUnoptimized() throws IOException {
+ deleteReaderWriterConflict(false);
+ }
+
+ public void testDeleteReaderWriterConflictOptimized() throws IOException {
+ deleteReaderWriterConflict(true);
+ }
+
+ public void testMultiReaderDeletes() throws Exception {
+ Directory dir = newDirectory();
+ RandomIndexWriter w= new RandomIndexWriter(random, dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
+ Document doc = new Document();
+ doc.add(newField("f", "doctor", Field.Store.NO, Field.Index.NOT_ANALYZED));
+ w.addDocument(doc);
+ doc = new Document();
+ w.commit();
+ doc.add(newField("f", "who", Field.Store.NO, Field.Index.NOT_ANALYZED));
+ w.addDocument(doc);
+ IndexReader r = new SlowMultiReaderWrapper(w.getReader());
+ w.close();
+
+ assertFalse(r.hasDeletions());
+ r.close();
+
+ r = new SlowMultiReaderWrapper(IndexReader.open(dir, false));
+
+ assertFalse(r.hasDeletions());
+ assertEquals(1, r.deleteDocuments(new Term("f", "doctor")));
+ assertTrue(r.hasDeletions());
+ assertTrue(r.isDeleted(0));
+ assertEquals(1, r.deleteDocuments(new Term("f", "who")));
+ assertTrue(r.isDeleted(1));
+ r.close();
+ dir.close();
+ }
+
+ public void testUndeleteAll() throws IOException {
+ Directory dir = newDirectory();
+ IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+ addDocumentWithFields(writer);
+ addDocumentWithFields(writer);
+ writer.close();
+ IndexReader reader = IndexReader.open(dir, false);
+ reader.deleteDocument(0);
+ reader.deleteDocument(1);
+ reader.undeleteAll();
+ reader.close();
+ reader = IndexReader.open(dir, false);
+ assertEquals(2, reader.numDocs()); // nothing has really been deleted thanks to undeleteAll()
+ reader.close();
+ dir.close();
+ }
+
+ public void testUndeleteAllAfterClose() throws IOException {
+ Directory dir = newDirectory();
+ IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+ addDocumentWithFields(writer);
+ addDocumentWithFields(writer);
+ writer.close();
+ IndexReader reader = IndexReader.open(dir, false);
+ reader.deleteDocument(0);
+ reader.close();
+ reader = IndexReader.open(dir, false);
+ reader.undeleteAll();
+ assertEquals(2, reader.numDocs()); // nothing has really been deleted thanks to undeleteAll()
+ reader.close();
+ dir.close();
+ }
+
+ public void testUndeleteAllAfterCloseThenReopen() throws IOException {
+ Directory dir = newDirectory();
+ IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+ addDocumentWithFields(writer);
+ addDocumentWithFields(writer);
+ writer.close();
+ IndexReader reader = IndexReader.open(dir, false);
+ reader.deleteDocument(0);
+ reader.close();
+ reader = IndexReader.open(dir, false);
+ reader.undeleteAll();
+ reader.close();
+ reader = IndexReader.open(dir, false);
+ assertEquals(2, reader.numDocs()); // nothing has really been deleted thanks to undeleteAll()
+ reader.close();
+ dir.close();
+ }
+
+ // LUCENE-1647
+ public void testIndexReaderUnDeleteAll() throws Exception {
+ MockDirectoryWrapper dir = newDirectory();
+ dir.setPreventDoubleWrite(false);
+ IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
+ TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+ writer.addDocument(createDocument("a"));
+ writer.addDocument(createDocument("b"));
+ writer.addDocument(createDocument("c"));
+ writer.close();
+ IndexReader reader = IndexReader.open(dir, false);
+ reader.deleteDocuments(new Term("id", "a"));
+ reader.flush();
+ reader.deleteDocuments(new Term("id", "b"));
+ reader.undeleteAll();
+ reader.deleteDocuments(new Term("id", "b"));
+ reader.close();
+ IndexReader.open(dir,true).close();
+ dir.close();
+ }
+}