1 package org.apache.lucene.index;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
22 import org.apache.lucene.analysis.MockAnalyzer;
23 import org.apache.lucene.document.Document;
24 import org.apache.lucene.document.Field;
25 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
26 import org.apache.lucene.store.Directory;
27 import org.apache.lucene.store.MockDirectoryWrapper;
28 import org.apache.lucene.util.LuceneTestCase;
30 import static org.apache.lucene.index.TestIndexReader.addDoc;
31 import static org.apache.lucene.index.TestIndexReader.addDocumentWithFields;
32 import static org.apache.lucene.index.TestIndexReader.assertTermDocsCount;
33 import static org.apache.lucene.index.TestIndexReader.createDocument;
35 public class TestIndexReaderDelete extends LuceneTestCase {
36 private void deleteReaderReaderConflict(boolean doFullMerge) throws IOException {
37 Directory dir = newDirectory();
39 Term searchTerm1 = new Term("content", "aaa");
40 Term searchTerm2 = new Term("content", "bbb");
41 Term searchTerm3 = new Term("content", "ccc");
43 // add 100 documents with term : aaa
44 // add 100 documents with term : bbb
45 // add 100 documents with term : ccc
46 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.CREATE));
47 for (int i = 0; i < 100; i++) {
48 addDoc(writer, searchTerm1.text());
49 addDoc(writer, searchTerm2.text());
50 addDoc(writer, searchTerm3.text());
58 // Both readers get segment info as exists at this time
59 IndexReader reader1 = IndexReader.open(dir, false);
60 assertEquals("first opened", 100, reader1.docFreq(searchTerm1));
61 assertEquals("first opened", 100, reader1.docFreq(searchTerm2));
62 assertEquals("first opened", 100, reader1.docFreq(searchTerm3));
63 assertTermDocsCount("first opened", reader1, searchTerm1, 100);
64 assertTermDocsCount("first opened", reader1, searchTerm2, 100);
65 assertTermDocsCount("first opened", reader1, searchTerm3, 100);
67 IndexReader reader2 = IndexReader.open(dir, false);
68 assertEquals("first opened", 100, reader2.docFreq(searchTerm1));
69 assertEquals("first opened", 100, reader2.docFreq(searchTerm2));
70 assertEquals("first opened", 100, reader2.docFreq(searchTerm3));
71 assertTermDocsCount("first opened", reader2, searchTerm1, 100);
72 assertTermDocsCount("first opened", reader2, searchTerm2, 100);
73 assertTermDocsCount("first opened", reader2, searchTerm3, 100);
75 // DELETE DOCS FROM READER 2 and CLOSE IT
76 // delete documents containing term: aaa
77 // when the reader is closed, the segment info is updated and
78 // the first reader is now stale
79 reader2.deleteDocuments(searchTerm1);
80 assertEquals("after delete 1", 100, reader2.docFreq(searchTerm1));
81 assertEquals("after delete 1", 100, reader2.docFreq(searchTerm2));
82 assertEquals("after delete 1", 100, reader2.docFreq(searchTerm3));
83 assertTermDocsCount("after delete 1", reader2, searchTerm1, 0);
84 assertTermDocsCount("after delete 1", reader2, searchTerm2, 100);
85 assertTermDocsCount("after delete 1", reader2, searchTerm3, 100);
88 // Make sure reader 1 is unchanged since it was open earlier
89 assertEquals("after delete 1", 100, reader1.docFreq(searchTerm1));
90 assertEquals("after delete 1", 100, reader1.docFreq(searchTerm2));
91 assertEquals("after delete 1", 100, reader1.docFreq(searchTerm3));
92 assertTermDocsCount("after delete 1", reader1, searchTerm1, 100);
93 assertTermDocsCount("after delete 1", reader1, searchTerm2, 100);
94 assertTermDocsCount("after delete 1", reader1, searchTerm3, 100);
97 // ATTEMPT TO DELETE FROM STALE READER
98 // delete documents containing term: bbb
100 reader1.deleteDocuments(searchTerm2);
101 fail("Delete allowed from a stale index reader");
102 } catch (IOException e) {
106 // RECREATE READER AND TRY AGAIN
108 reader1 = IndexReader.open(dir, false);
109 assertEquals("reopened", 100, reader1.docFreq(searchTerm1));
110 assertEquals("reopened", 100, reader1.docFreq(searchTerm2));
111 assertEquals("reopened", 100, reader1.docFreq(searchTerm3));
112 assertTermDocsCount("reopened", reader1, searchTerm1, 0);
113 assertTermDocsCount("reopened", reader1, searchTerm2, 100);
114 assertTermDocsCount("reopened", reader1, searchTerm3, 100);
116 reader1.deleteDocuments(searchTerm2);
117 assertEquals("deleted 2", 100, reader1.docFreq(searchTerm1));
118 assertEquals("deleted 2", 100, reader1.docFreq(searchTerm2));
119 assertEquals("deleted 2", 100, reader1.docFreq(searchTerm3));
120 assertTermDocsCount("deleted 2", reader1, searchTerm1, 0);
121 assertTermDocsCount("deleted 2", reader1, searchTerm2, 0);
122 assertTermDocsCount("deleted 2", reader1, searchTerm3, 100);
125 // Open another reader to confirm that everything is deleted
126 reader2 = IndexReader.open(dir, false);
127 assertTermDocsCount("reopened 2", reader2, searchTerm1, 0);
128 assertTermDocsCount("reopened 2", reader2, searchTerm2, 0);
129 assertTermDocsCount("reopened 2", reader2, searchTerm3, 100);
135 private void deleteReaderWriterConflict(boolean doFullMerge) throws IOException {
136 //Directory dir = new RAMDirectory();
137 Directory dir = newDirectory();
139 Term searchTerm = new Term("content", "aaa");
140 Term searchTerm2 = new Term("content", "bbb");
142 // add 100 documents with term : aaa
143 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.CREATE));
144 for (int i = 0; i < 100; i++) {
145 addDoc(writer, searchTerm.text());
149 // OPEN READER AT THIS POINT - this should fix the view of the
150 // index at the point of having 100 "aaa" documents and 0 "bbb"
151 IndexReader reader = IndexReader.open(dir, false);
152 assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
153 assertEquals("first docFreq", 0, reader.docFreq(searchTerm2));
154 assertTermDocsCount("first reader", reader, searchTerm, 100);
155 assertTermDocsCount("first reader", reader, searchTerm2, 0);
157 // add 100 documents with term : bbb
158 writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(OpenMode.APPEND));
159 for (int i = 0; i < 100; i++) {
160 addDoc(writer, searchTerm2.text());
163 // REQUEST full merge
164 // This causes a new segment to become current for all subsequent
165 // searchers. Because of this, deletions made via a previously open
166 // reader, which would be applied to that reader's segment, are lost
167 // for subsequent searchers/readers
169 writer.forceMerge(1);
173 // The reader should not see the new data
174 assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
175 assertEquals("first docFreq", 0, reader.docFreq(searchTerm2));
176 assertTermDocsCount("first reader", reader, searchTerm, 100);
177 assertTermDocsCount("first reader", reader, searchTerm2, 0);
180 // DELETE DOCUMENTS CONTAINING TERM: aaa
181 // NOTE: the reader was created when only "aaa" documents were in
184 deleted = reader.deleteDocuments(searchTerm);
185 fail("Delete allowed on an index reader with stale segment information");
186 } catch (StaleReaderException e) {
190 // Re-open index reader and try again. This time it should see
193 reader = IndexReader.open(dir, false);
194 assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
195 assertEquals("first docFreq", 100, reader.docFreq(searchTerm2));
196 assertTermDocsCount("first reader", reader, searchTerm, 100);
197 assertTermDocsCount("first reader", reader, searchTerm2, 100);
199 deleted = reader.deleteDocuments(searchTerm);
200 assertEquals("deleted count", 100, deleted);
201 assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm));
202 assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm2));
203 assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
204 assertTermDocsCount("deleted termDocs", reader, searchTerm2, 100);
207 // CREATE A NEW READER and re-test
208 reader = IndexReader.open(dir, false);
209 assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm2));
210 assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
211 assertTermDocsCount("deleted termDocs", reader, searchTerm2, 100);
216 public void testBasicDelete() throws IOException {
217 Directory dir = newDirectory();
219 IndexWriter writer = null;
220 IndexReader reader = null;
221 Term searchTerm = new Term("content", "aaa");
223 // add 100 documents with term : aaa
224 writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
225 writer.setInfoStream(VERBOSE ? System.out : null);
226 for (int i = 0; i < 100; i++) {
227 addDoc(writer, searchTerm.text());
231 // OPEN READER AT THIS POINT - this should fix the view of the
232 // index at the point of having 100 "aaa" documents and 0 "bbb"
233 reader = IndexReader.open(dir, false);
234 assertEquals("first docFreq", 100, reader.docFreq(searchTerm));
235 assertTermDocsCount("first reader", reader, searchTerm, 100);
238 // DELETE DOCUMENTS CONTAINING TERM: aaa
240 reader = IndexReader.open(dir, false);
241 deleted = reader.deleteDocuments(searchTerm);
242 assertEquals("deleted count", 100, deleted);
243 assertEquals("deleted docFreq", 100, reader.docFreq(searchTerm));
244 assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
246 // open a 2nd reader to make sure first reader can
247 // commit its changes (.del) while second reader
249 IndexReader reader2 = IndexReader.open(dir, false);
252 // CREATE A NEW READER and re-test
253 reader = IndexReader.open(dir, false);
254 assertEquals("deleted docFreq", 0, reader.docFreq(searchTerm));
255 assertTermDocsCount("deleted termDocs", reader, searchTerm, 0);
261 public void testDeleteReaderReaderConflictNoFullMerge() throws IOException {
262 deleteReaderReaderConflict(false);
265 public void testDeleteReaderReaderConflictFullMerge() throws IOException {
266 deleteReaderReaderConflict(true);
269 public void testDeleteReaderWriterConflictNoFullMerge() throws IOException {
270 deleteReaderWriterConflict(false);
273 public void testDeleteReaderWriterConflictFullMerge() throws IOException {
274 deleteReaderWriterConflict(true);
277 public void testMultiReaderDeletes() throws Exception {
278 Directory dir = newDirectory();
279 RandomIndexWriter w= new RandomIndexWriter(random, dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
280 Document doc = new Document();
281 doc.add(newField("f", "doctor", Field.Store.NO, Field.Index.NOT_ANALYZED));
283 doc = new Document();
285 doc.add(newField("f", "who", Field.Store.NO, Field.Index.NOT_ANALYZED));
287 IndexReader r = new SlowMultiReaderWrapper(w.getReader());
290 assertFalse(r.hasDeletions());
293 r = new SlowMultiReaderWrapper(IndexReader.open(dir, false));
295 assertFalse(r.hasDeletions());
296 assertEquals(1, r.deleteDocuments(new Term("f", "doctor")));
297 assertTrue(r.hasDeletions());
298 assertTrue(r.isDeleted(0));
299 assertEquals(1, r.deleteDocuments(new Term("f", "who")));
300 assertTrue(r.isDeleted(1));
305 public void testUndeleteAll() throws IOException {
306 Directory dir = newDirectory();
307 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
308 addDocumentWithFields(writer);
309 addDocumentWithFields(writer);
311 IndexReader reader = IndexReader.open(dir, false);
312 reader.deleteDocument(0);
313 reader.deleteDocument(1);
314 reader.undeleteAll();
316 reader = IndexReader.open(dir, false);
317 assertEquals(2, reader.numDocs()); // nothing has really been deleted thanks to undeleteAll()
322 public void testUndeleteAllAfterClose() throws IOException {
323 Directory dir = newDirectory();
324 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
325 addDocumentWithFields(writer);
326 addDocumentWithFields(writer);
328 IndexReader reader = IndexReader.open(dir, false);
329 reader.deleteDocument(0);
331 reader = IndexReader.open(dir, false);
332 reader.undeleteAll();
333 assertEquals(2, reader.numDocs()); // nothing has really been deleted thanks to undeleteAll()
338 public void testUndeleteAllAfterCloseThenReopen() throws IOException {
339 Directory dir = newDirectory();
340 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
341 addDocumentWithFields(writer);
342 addDocumentWithFields(writer);
344 IndexReader reader = IndexReader.open(dir, false);
345 reader.deleteDocument(0);
347 reader = IndexReader.open(dir, false);
348 reader.undeleteAll();
350 reader = IndexReader.open(dir, false);
351 assertEquals(2, reader.numDocs()); // nothing has really been deleted thanks to undeleteAll()
357 public void testIndexReaderUnDeleteAll() throws Exception {
358 MockDirectoryWrapper dir = newDirectory();
359 dir.setPreventDoubleWrite(false);
360 IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
361 TEST_VERSION_CURRENT, new MockAnalyzer(random)));
362 writer.addDocument(createDocument("a"));
363 writer.addDocument(createDocument("b"));
364 writer.addDocument(createDocument("c"));
366 IndexReader reader = IndexReader.open(dir, false);
367 reader.deleteDocuments(new Term("id", "a"));
369 reader.deleteDocuments(new Term("id", "b"));
370 reader.undeleteAll();
371 reader.deleteDocuments(new Term("id", "b"));
373 IndexReader.open(dir,true).close();