add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / backwards / src / test / org / apache / lucene / index / TestIndexWriterReader.java
1 package org.apache.lucene.index;
2
3 /**
4  * Copyright 2004 The Apache Software Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 import java.io.IOException;
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Random;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.lucene.analysis.MockAnalyzer;
28 import org.apache.lucene.analysis.WhitespaceAnalyzer;
29 import org.apache.lucene.document.Document;
30 import org.apache.lucene.document.Field;
31 import org.apache.lucene.document.Field.Index;
32 import org.apache.lucene.document.Field.Store;
33 import org.apache.lucene.document.Field.TermVector;
34 import org.apache.lucene.search.TermQuery;
35 import org.apache.lucene.search.IndexSearcher;
36 import org.apache.lucene.search.Query;
37 import org.apache.lucene.search.TopDocs;
38 import org.apache.lucene.store.Directory;
39 import org.apache.lucene.store.MockDirectoryWrapper;
40 import org.apache.lucene.store.AlreadyClosedException;
41 import org.apache.lucene.store.RAMDirectory;
42 import org.apache.lucene.util.LuceneTestCase;
43 import org.apache.lucene.util._TestUtil;
44 import org.apache.lucene.util.ThreadInterruptedException;
45 import java.util.concurrent.atomic.AtomicInteger;
46
47 public class TestIndexWriterReader extends LuceneTestCase {
48   static PrintStream infoStream = VERBOSE ? System.out : null;
49   
50   public static int count(Term t, IndexReader r) throws IOException {
51     int count = 0;
52     TermDocs td = r.termDocs(t);
53     while (td.next()) {
54       td.doc();
55       count++;
56     }
57     td.close();
58     return count;
59   }
60   
61   public void testAddCloseOpen() throws IOException {
62     Directory dir1 = newDirectory();
63     IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
64     
65     IndexWriter writer = new IndexWriter(dir1, iwc);
66     for (int i = 0; i < 97 ; i++) {
67       IndexReader reader = writer.getReader();
68       if (i == 0) {
69         writer.addDocument(createDocument(i, "x", 1 + random.nextInt(5)));
70       } else {
71         int previous = random.nextInt(i);
72         // a check if the reader is current here could fail since there might be
73         // merges going on.
74         switch (random.nextInt(5)) {
75         case 0:
76         case 1:
77         case 2:
78           writer.addDocument(createDocument(i, "x", 1 + random.nextInt(5)));
79           break;
80         case 3:
81           writer.updateDocument(new Term("id", "" + previous), createDocument(
82               previous, "x", 1 + random.nextInt(5)));
83           break;
84         case 4:
85           writer.deleteDocuments(new Term("id", "" + previous));
86         }
87       }
88       assertFalse(reader.isCurrent());
89       reader.close();
90     }
91     writer.optimize(); // make sure all merging is done etc.
92     IndexReader reader = writer.getReader();
93     writer.commit(); // no changes that are not visible to the reader
94     assertTrue(reader.isCurrent());
95     writer.close();
96     assertTrue(reader.isCurrent()); // all changes are visible to the reader
97     iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
98     writer = new IndexWriter(dir1, iwc);
99     assertTrue(reader.isCurrent());
100     writer.addDocument(createDocument(1, "x", 1+random.nextInt(5)));
101     assertTrue(reader.isCurrent()); // segments in ram but IW is different to the readers one
102     writer.close();
103     assertFalse(reader.isCurrent()); // segments written
104     reader.close();
105     dir1.close();
106   }
107   
108   public void testUpdateDocument() throws Exception {
109     boolean optimize = true;
110
111     Directory dir1 = newDirectory();
112     IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
113     if (iwc.getMaxBufferedDocs() < 20) {
114       iwc.setMaxBufferedDocs(20);
115     }
116     // no merging
117     if (random.nextBoolean()) {
118       iwc.setMergePolicy(NoMergePolicy.NO_COMPOUND_FILES);
119     } else {
120       iwc.setMergePolicy(NoMergePolicy.COMPOUND_FILES);
121     }
122     IndexWriter writer = new IndexWriter(dir1, iwc);
123
124     // create the index
125     createIndexNoClose(!optimize, "index1", writer);
126
127     // writer.flush(false, true, true);
128
129     // get a reader
130     IndexReader r1 = writer.getReader();
131     assertTrue(r1.isCurrent());
132
133     String id10 = r1.document(10).getField("id").stringValue();
134     
135     Document newDoc = r1.document(10);
136     newDoc.removeField("id");
137     newDoc.add(newField("id", Integer.toString(8000), Store.YES, Index.NOT_ANALYZED));
138     writer.updateDocument(new Term("id", id10), newDoc);
139     assertFalse(r1.isCurrent());
140
141     IndexReader r2 = writer.getReader();
142     assertTrue(r2.isCurrent());
143     assertEquals(0, count(new Term("id", id10), r2));
144     assertEquals(1, count(new Term("id", Integer.toString(8000)), r2));
145     
146     r1.close();
147     writer.close();
148     assertTrue(r2.isCurrent());
149     
150     IndexReader r3 = IndexReader.open(dir1, true);
151     assertTrue(r3.isCurrent());
152     assertTrue(r2.isCurrent());
153     assertEquals(0, count(new Term("id", id10), r3));
154     assertEquals(1, count(new Term("id", Integer.toString(8000)), r3));
155
156     writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
157     Document doc = new Document();
158     doc.add(newField("field", "a b c", Field.Store.NO, Field.Index.ANALYZED));
159     writer.addDocument(doc);
160     assertTrue(r2.isCurrent());
161     assertTrue(r3.isCurrent());
162
163     writer.close();
164
165     assertFalse(r2.isCurrent());
166     assertTrue(!r3.isCurrent());
167
168     r2.close();
169     r3.close();
170     
171     dir1.close();
172   }
173   
174   public void testIsCurrent() throws IOException {
175     Directory dir = newDirectory();
176     IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
177     
178     IndexWriter writer = new IndexWriter(dir, iwc);
179     Document doc = new Document();
180     doc.add(newField("field", "a b c", Field.Store.NO, Field.Index.ANALYZED));
181     writer.addDocument(doc);
182     writer.close();
183     
184     iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
185     writer = new IndexWriter(dir, iwc);
186     doc = new Document();
187     doc.add(newField("field", "a b c", Field.Store.NO, Field.Index.ANALYZED));
188     IndexReader nrtReader = writer.getReader();
189     assertTrue(nrtReader.isCurrent());
190     writer.addDocument(doc);
191     assertFalse(nrtReader.isCurrent()); // should see the changes
192     writer.optimize(); // make sure we don't have a merge going on
193     assertFalse(nrtReader.isCurrent());
194     nrtReader.close();
195     
196     IndexReader dirReader = IndexReader.open(dir);
197     nrtReader = writer.getReader();
198     
199     assertTrue(dirReader.isCurrent());
200     assertTrue(nrtReader.isCurrent()); // nothing was committed yet so we are still current
201     assertEquals(2, nrtReader.maxDoc()); // sees the actual document added
202     assertEquals(1, dirReader.maxDoc());
203     writer.close(); // close is actually a commit both should see the changes
204     assertTrue(nrtReader.isCurrent()); 
205     assertFalse(dirReader.isCurrent()); // this reader has been opened before the writer was closed / committed
206     
207     dirReader.close();
208     nrtReader.close();
209     dir.close();
210   }
211   
212   /**
213    * Test using IW.addIndexes
214    * 
215    * @throws Exception
216    */
217   public void testAddIndexes() throws Exception {
218     boolean optimize = false;
219
220     Directory dir1 = newDirectory();
221     IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random));
222     if (iwc.getMaxBufferedDocs() < 20) {
223       iwc.setMaxBufferedDocs(20);
224     }
225     // no merging
226     if (random.nextBoolean()) {
227       iwc.setMergePolicy(NoMergePolicy.NO_COMPOUND_FILES);
228     } else {
229       iwc.setMergePolicy(NoMergePolicy.COMPOUND_FILES);
230     }
231     IndexWriter writer = new IndexWriter(dir1, iwc);
232
233     writer.setInfoStream(infoStream);
234     // create the index
235     createIndexNoClose(!optimize, "index1", writer);
236     writer.flush(false, true);
237
238     // create a 2nd index
239     Directory dir2 = newDirectory();
240     IndexWriter writer2 = new IndexWriter(dir2, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
241     writer2.setInfoStream(infoStream);
242     createIndexNoClose(!optimize, "index2", writer2);
243     writer2.close();
244
245     IndexReader r0 = writer.getReader();
246     assertTrue(r0.isCurrent());
247     writer.addIndexes(new Directory[] { dir2 });
248     assertFalse(r0.isCurrent());
249     r0.close();
250
251     IndexReader r1 = writer.getReader();
252     assertTrue(r1.isCurrent());
253
254     writer.commit();
255     assertTrue(r1.isCurrent()); // we have seen all changes - no change after opening the NRT reader
256
257     assertEquals(200, r1.maxDoc());
258
259     int index2df = r1.docFreq(new Term("indexname", "index2"));
260
261     assertEquals(100, index2df);
262
263     // verify the docs are from different indexes
264     Document doc5 = r1.document(5);
265     assertEquals("index1", doc5.get("indexname"));
266     Document doc150 = r1.document(150);
267     assertEquals("index2", doc150.get("indexname"));
268     r1.close();
269     writer.close();
270     dir1.close();
271     dir2.close();
272   }
273   
274   public void testAddIndexes2() throws Exception {
275     boolean optimize = false;
276
277     Directory dir1 = newDirectory();
278     IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
279     writer.setInfoStream(infoStream);
280
281     // create a 2nd index
282     Directory dir2 = newDirectory();
283     IndexWriter writer2 = new IndexWriter(dir2, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
284     writer2.setInfoStream(infoStream);
285     createIndexNoClose(!optimize, "index2", writer2);
286     writer2.close();
287
288     writer.addIndexes(new Directory[] { dir2 });
289     writer.addIndexes(new Directory[] { dir2 });
290     writer.addIndexes(new Directory[] { dir2 });
291     writer.addIndexes(new Directory[] { dir2 });
292     writer.addIndexes(new Directory[] { dir2 });
293
294     IndexReader r1 = writer.getReader();
295     assertEquals(500, r1.maxDoc());
296     
297     r1.close();
298     writer.close();
299     dir1.close();
300     dir2.close();
301   }
302
303   /**
304    * Deletes using IW.deleteDocuments
305    * 
306    * @throws Exception
307    */
308   public void testDeleteFromIndexWriter() throws Exception {
309     boolean optimize = true;
310
311     Directory dir1 = newDirectory();
312     IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).setReaderTermsIndexDivisor(2));
313     writer.setInfoStream(infoStream);
314     // create the index
315     createIndexNoClose(!optimize, "index1", writer);
316     writer.flush(false, true);
317     // get a reader
318     IndexReader r1 = writer.getReader();
319
320     String id10 = r1.document(10).getField("id").stringValue();
321
322     // deleted IW docs should not show up in the next getReader
323     writer.deleteDocuments(new Term("id", id10));
324     IndexReader r2 = writer.getReader();
325     assertEquals(1, count(new Term("id", id10), r1));
326     assertEquals(0, count(new Term("id", id10), r2));
327     
328     String id50 = r1.document(50).getField("id").stringValue();
329     assertEquals(1, count(new Term("id", id50), r1));
330     
331     writer.deleteDocuments(new Term("id", id50));
332     
333     IndexReader r3 = writer.getReader();
334     assertEquals(0, count(new Term("id", id10), r3));
335     assertEquals(0, count(new Term("id", id50), r3));
336     
337     String id75 = r1.document(75).getField("id").stringValue();
338     writer.deleteDocuments(new TermQuery(new Term("id", id75)));
339     IndexReader r4 = writer.getReader();
340     assertEquals(1, count(new Term("id", id75), r3));
341     assertEquals(0, count(new Term("id", id75), r4));
342     
343     r1.close();
344     r2.close();
345     r3.close();
346     r4.close();
347     writer.close();
348         
349     // reopen the writer to verify the delete made it to the directory
350     writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
351     writer.setInfoStream(infoStream);
352     IndexReader w2r1 = writer.getReader();
353     assertEquals(0, count(new Term("id", id10), w2r1));
354     w2r1.close();
355     writer.close();
356     dir1.close();
357   }
358
359   public void testAddIndexesAndDoDeletesThreads() throws Throwable {
360     final int numIter = 2;
361     int numDirs = 3;
362     
363     Directory mainDir = newDirectory();
364     IndexWriter mainWriter = new IndexWriter(mainDir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
365     _TestUtil.reduceOpenFiles(mainWriter);
366
367     mainWriter.setInfoStream(infoStream);
368     AddDirectoriesThreads addDirThreads = new AddDirectoriesThreads(numIter, mainWriter);
369     addDirThreads.launchThreads(numDirs);
370     addDirThreads.joinThreads();
371     
372     //assertEquals(100 + numDirs * (3 * numIter / 4) * addDirThreads.NUM_THREADS
373     //    * addDirThreads.NUM_INIT_DOCS, addDirThreads.mainWriter.numDocs());
374     assertEquals(addDirThreads.count.intValue(), addDirThreads.mainWriter.numDocs());
375
376     addDirThreads.close(true);
377     
378     assertTrue(addDirThreads.failures.size() == 0);
379
380     _TestUtil.checkIndex(mainDir);
381
382     IndexReader reader = IndexReader.open(mainDir, true);
383     assertEquals(addDirThreads.count.intValue(), reader.numDocs());
384     //assertEquals(100 + numDirs * (3 * numIter / 4) * addDirThreads.NUM_THREADS
385     //    * addDirThreads.NUM_INIT_DOCS, reader.numDocs());
386     reader.close();
387
388     addDirThreads.closeDir();
389     mainDir.close();
390   }
391   
392   private class AddDirectoriesThreads {
393     Directory addDir;
394     final static int NUM_THREADS = 5;
395     final static int NUM_INIT_DOCS = 100;
396     int numDirs;
397     final Thread[] threads = new Thread[NUM_THREADS];
398     IndexWriter mainWriter;
399     final List<Throwable> failures = new ArrayList<Throwable>();
400     IndexReader[] readers;
401     boolean didClose = false;
402     AtomicInteger count = new AtomicInteger(0);
403     AtomicInteger numaddIndexes = new AtomicInteger(0);
404     
405     public AddDirectoriesThreads(int numDirs, IndexWriter mainWriter) throws Throwable {
406       this.numDirs = numDirs;
407       this.mainWriter = mainWriter;
408       addDir = newDirectory();
409       IndexWriter writer = new IndexWriter(addDir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMaxBufferedDocs(2));
410       for (int i = 0; i < NUM_INIT_DOCS; i++) {
411         Document doc = createDocument(i, "addindex", 4);
412         writer.addDocument(doc);
413       }
414         
415       writer.close();
416       
417       readers = new IndexReader[numDirs];
418       for (int i = 0; i < numDirs; i++)
419         readers[i] = IndexReader.open(addDir, false);
420     }
421     
422     void joinThreads() {
423       for (int i = 0; i < NUM_THREADS; i++)
424         try {
425           threads[i].join();
426         } catch (InterruptedException ie) {
427           throw new ThreadInterruptedException(ie);
428         }
429     }
430
431     void close(boolean doWait) throws Throwable {
432       didClose = true;
433       if (doWait) {
434         mainWriter.waitForMerges();
435       }
436       mainWriter.close(doWait);
437     }
438
439     void closeDir() throws Throwable {
440       for (int i = 0; i < numDirs; i++)
441         readers[i].close();
442       addDir.close();
443     }
444     
445     void handle(Throwable t) {
446       t.printStackTrace(System.out);
447       synchronized (failures) {
448         failures.add(t);
449       }
450     }
451     
452     void launchThreads(final int numIter) {
453       for (int i = 0; i < NUM_THREADS; i++) {
454         threads[i] = new Thread() {
455           @Override
456           public void run() {
457             try {
458               final Directory[] dirs = new Directory[numDirs];
459               for (int k = 0; k < numDirs; k++)
460                 dirs[k] = new MockDirectoryWrapper(random, new RAMDirectory(addDir));
461               //int j = 0;
462               //while (true) {
463                 // System.out.println(Thread.currentThread().getName() + ": iter
464                 // j=" + j);
465                 for (int x=0; x < numIter; x++) {
466                   // only do addIndexes
467                   doBody(x, dirs);
468                 }
469                 //if (numIter > 0 && j == numIter)
470                 //  break;
471                 //doBody(j++, dirs);
472                 //doBody(5, dirs);
473               //}
474             } catch (Throwable t) {
475               handle(t);
476             }
477           }
478         };
479       }
480       for (int i = 0; i < NUM_THREADS; i++)
481         threads[i].start();
482     }
483     
484     void doBody(int j, Directory[] dirs) throws Throwable {
485       switch (j % 4) {
486         case 0:
487           mainWriter.addIndexes(dirs);
488           mainWriter.optimize();
489           break;
490         case 1:
491           mainWriter.addIndexes(dirs);
492           numaddIndexes.incrementAndGet();
493           break;
494         case 2:
495           mainWriter.addIndexes(readers);
496           break;
497         case 3:
498           mainWriter.commit();
499       }
500       count.addAndGet(dirs.length*NUM_INIT_DOCS);
501     }
502   }
503
504   public void testIndexWriterReopenSegmentOptimize() throws Exception {
505     doTestIndexWriterReopenSegment(true);
506   }
507
508   public void testIndexWriterReopenSegment() throws Exception {
509     doTestIndexWriterReopenSegment(false);
510   }
511
512   /**
513    * Tests creating a segment, then check to insure the segment can be seen via
514    * IW.getReader
515    */
516   public void doTestIndexWriterReopenSegment(boolean optimize) throws Exception {
517     Directory dir1 = newDirectory();
518     IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
519     writer.setInfoStream(infoStream);
520     IndexReader r1 = writer.getReader();
521     assertEquals(0, r1.maxDoc());
522     createIndexNoClose(false, "index1", writer);
523     writer.flush(!optimize, true);
524
525     IndexReader iwr1 = writer.getReader();
526     assertEquals(100, iwr1.maxDoc());
527
528     IndexReader r2 = writer.getReader();
529     assertEquals(r2.maxDoc(), 100);
530     // add 100 documents
531     for (int x = 10000; x < 10000 + 100; x++) {
532       Document d = createDocument(x, "index1", 5);
533       writer.addDocument(d);
534     }
535     writer.flush(false, true);
536     // verify the reader was reopened internally
537     IndexReader iwr2 = writer.getReader();
538     assertTrue(iwr2 != r1);
539     assertEquals(200, iwr2.maxDoc());
540     // should have flushed out a segment
541     IndexReader r3 = writer.getReader();
542     assertTrue(r2 != r3);
543     assertEquals(200, r3.maxDoc());
544
545     // dec ref the readers rather than close them because
546     // closing flushes changes to the writer
547     r1.close();
548     iwr1.close();
549     r2.close();
550     r3.close();
551     iwr2.close();
552     writer.close();
553
554     // test whether the changes made it to the directory
555     writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
556     IndexReader w2r1 = writer.getReader();
557     // insure the deletes were actually flushed to the directory
558     assertEquals(200, w2r1.maxDoc());
559     w2r1.close();
560     writer.close();
561
562     dir1.close();
563   }
564
565   
566   public static Document createDocument(int n, String indexName, int numFields) {
567     StringBuilder sb = new StringBuilder();
568     Document doc = new Document();
569     doc.add(new Field("id", Integer.toString(n), Store.YES, Index.NOT_ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
570     doc.add(new Field("indexname", indexName, Store.YES, Index.NOT_ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
571     sb.append("a");
572     sb.append(n);
573     doc.add(new Field("field1", sb.toString(), Store.YES, Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
574     sb.append(" b");
575     sb.append(n);
576     for (int i = 1; i < numFields; i++) {
577       doc.add(new Field("field" + (i + 1), sb.toString(), Store.YES,
578                         Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
579     }
580     return doc;
581   }
582
583   /*
584    * Delete a document by term and return the doc id
585    * 
586    * public static int deleteDocument(Term term, IndexWriter writer) throws
587    * IOException { IndexReader reader = writer.getReader(); TermDocs td =
588    * reader.termDocs(term); int doc = -1; //if (td.next()) { // doc = td.doc();
589    * //} //writer.deleteDocuments(term); td.close(); return doc; }
590    */
591   
592   public static void createIndex(Random random, Directory dir1, String indexName,
593       boolean multiSegment) throws IOException {
594     IndexWriter w = new IndexWriter(dir1, LuceneTestCase.newIndexWriterConfig(random,
595         TEST_VERSION_CURRENT, new MockAnalyzer(random))
596         .setMergePolicy(new LogDocMergePolicy()));
597     for (int i = 0; i < 100; i++) {
598       w.addDocument(createDocument(i, indexName, 4));
599       if (multiSegment && (i % 10) == 0) {
600       }
601     }
602     if (!multiSegment) {
603       w.optimize();
604     }
605     w.close();
606   }
607
608   public static void createIndexNoClose(boolean multiSegment, String indexName,
609       IndexWriter w) throws IOException {
610     for (int i = 0; i < 100; i++) {
611       w.addDocument(createDocument(i, indexName, 4));
612     }
613     if (!multiSegment) {
614       w.optimize();
615     }
616   }
617
618   private static class MyWarmer extends IndexWriter.IndexReaderWarmer {
619     int warmCount;
620     @Override
621     public void warm(IndexReader reader) throws IOException {
622       warmCount++;
623     }
624   }
625
626   public void testMergeWarmer() throws Exception {
627
628     Directory dir1 = newDirectory();
629     // Enroll warmer
630     MyWarmer warmer = new MyWarmer();
631     IndexWriter writer = new IndexWriter(
632         dir1,
633         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
634             setMaxBufferedDocs(2).
635             setMergedSegmentWarmer(warmer).
636             setMergeScheduler(new ConcurrentMergeScheduler()).
637             setMergePolicy(newLogMergePolicy())
638     );
639     writer.setInfoStream(infoStream);
640
641     // create the index
642     createIndexNoClose(false, "test", writer);
643
644     // get a reader to put writer into near real-time mode
645     IndexReader r1 = writer.getReader();
646     
647     ((LogMergePolicy) writer.getConfig().getMergePolicy()).setMergeFactor(2);
648
649     int num = atLeast(100);
650     for (int i = 0; i < num; i++) {
651       writer.addDocument(createDocument(i, "test", 4));
652     }
653     ((ConcurrentMergeScheduler) writer.getConfig().getMergeScheduler()).sync();
654
655     assertTrue(warmer.warmCount > 0);
656     final int count = warmer.warmCount;
657
658     writer.addDocument(createDocument(17, "test", 4));
659     writer.optimize();
660     assertTrue(warmer.warmCount > count);
661     
662     writer.close();
663     r1.close();
664     dir1.close();
665   }
666
667   public void testAfterCommit() throws Exception {
668     Directory dir1 = newDirectory();
669     IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergeScheduler(new ConcurrentMergeScheduler()));
670     writer.commit();
671     writer.setInfoStream(infoStream);
672
673     // create the index
674     createIndexNoClose(false, "test", writer);
675
676     // get a reader to put writer into near real-time mode
677     IndexReader r1 = writer.getReader();
678     _TestUtil.checkIndex(dir1);
679     writer.commit();
680     _TestUtil.checkIndex(dir1);
681     assertEquals(100, r1.numDocs());
682
683     for (int i = 0; i < 10; i++) {
684       writer.addDocument(createDocument(i, "test", 4));
685     }
686     ((ConcurrentMergeScheduler) writer.getConfig().getMergeScheduler()).sync();
687
688     IndexReader r2 = r1.reopen();
689     if (r2 != r1) {
690       r1.close();
691       r1 = r2;
692     }
693     assertEquals(110, r1.numDocs());
694     writer.close();
695     r1.close();
696     dir1.close();
697   }
698
699   // Make sure reader remains usable even if IndexWriter closes
700   public void testAfterClose() throws Exception {
701     Directory dir1 = newDirectory();
702     IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
703     writer.setInfoStream(infoStream);
704
705     // create the index
706     createIndexNoClose(false, "test", writer);
707
708     IndexReader r = writer.getReader();
709     writer.close();
710
711     _TestUtil.checkIndex(dir1);
712
713     // reader should remain usable even after IndexWriter is closed:
714     assertEquals(100, r.numDocs());
715     Query q = new TermQuery(new Term("indexname", "test"));
716     IndexSearcher searcher = newSearcher(r);
717     assertEquals(100, searcher.search(q, 10).totalHits);
718     searcher.close();
719     try {
720       r.reopen();
721       fail("failed to hit AlreadyClosedException");
722     } catch (AlreadyClosedException ace) {
723       // expected
724     }
725     r.close();
726     dir1.close();
727   }
728
729   // Stress test reopen during addIndexes
730   public void testDuringAddIndexes() throws Exception {
731     MockDirectoryWrapper dir1 = newDirectory();
732     final IndexWriter writer = new IndexWriter(
733         dir1,
734         newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).
735             setMergePolicy(newLogMergePolicy(2))
736     );
737     writer.setInfoStream(infoStream);
738     ((LogMergePolicy) writer.getConfig().getMergePolicy()).setMergeFactor(2);
739
740     // create the index
741     createIndexNoClose(false, "test", writer);
742     writer.commit();
743
744     final Directory[] dirs = new Directory[10];
745     for (int i=0;i<10;i++) {
746       dirs[i] = new MockDirectoryWrapper(random, new RAMDirectory(dir1));
747     }
748
749     IndexReader r = writer.getReader();
750
751     final int NUM_THREAD = 5;
752     final float SECONDS = 0.5f;
753
754     final long endTime = (long) (System.currentTimeMillis() + 1000.*SECONDS);
755     final List<Throwable> excs = Collections.synchronizedList(new ArrayList<Throwable>());
756
757     final Thread[] threads = new Thread[NUM_THREAD];
758     for(int i=0;i<NUM_THREAD;i++) {
759       threads[i] = new Thread() {
760           @Override
761           public void run() {
762             do {
763               try {
764                 writer.addIndexes(dirs);
765                 writer.maybeMerge();
766               } catch (Throwable t) {
767                 excs.add(t);
768                 throw new RuntimeException(t);
769               }
770             } while(System.currentTimeMillis() < endTime);
771           }
772         };
773       threads[i].setDaemon(true);
774       threads[i].start();
775     }
776
777     int lastCount = 0;
778     while(System.currentTimeMillis() < endTime) {
779       IndexReader r2 = r.reopen();
780       if (r2 != r) {
781         r.close();
782         r = r2;
783       }
784       Query q = new TermQuery(new Term("indexname", "test"));
785       IndexSearcher searcher = newSearcher(r);
786       final int count = searcher.search(q, 10).totalHits;
787       searcher.close();
788       assertTrue(count >= lastCount);
789       lastCount = count;
790     }
791
792     for(int i=0;i<NUM_THREAD;i++) {
793       threads[i].join();
794     }
795     // final check
796     IndexReader r2 = r.reopen();
797     if (r2 != r) {
798       r.close();
799       r = r2;
800     }
801     Query q = new TermQuery(new Term("indexname", "test"));
802     IndexSearcher searcher = newSearcher(r);
803     final int count = searcher.search(q, 10).totalHits;
804     searcher.close();
805     assertTrue(count >= lastCount);
806
807     assertEquals(0, excs.size());
808     r.close();
809     assertEquals(0, dir1.getOpenDeletedFiles().size());
810
811     writer.close();
812
813     dir1.close();
814   }
815
816   // Stress test reopen during add/delete
817   public void testDuringAddDelete() throws Exception {
818     Directory dir1 = newDirectory();
819     final IndexWriter writer = new IndexWriter(
820         dir1,
821         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
822             setMergePolicy(newLogMergePolicy(2))
823     );
824     writer.setInfoStream(infoStream);
825     ((LogMergePolicy) writer.getConfig().getMergePolicy()).setMergeFactor(2);
826
827     // create the index
828     createIndexNoClose(false, "test", writer);
829     writer.commit();
830
831     IndexReader r = writer.getReader();
832
833     final int NUM_THREAD = 5;
834     final float SECONDS = 0.5f;
835
836     final long endTime = (long) (System.currentTimeMillis() + 1000.*SECONDS);
837     final List<Throwable> excs = Collections.synchronizedList(new ArrayList<Throwable>());
838
839     final Thread[] threads = new Thread[NUM_THREAD];
840     for(int i=0;i<NUM_THREAD;i++) {
841       threads[i] = new Thread() {
842           final Random r = new Random(random.nextLong());
843
844           @Override
845           public void run() {
846             int count = 0;
847             do {
848               try {
849                 for(int docUpto=0;docUpto<10;docUpto++) {
850                   writer.addDocument(createDocument(10*count+docUpto, "test", 4));
851                 }
852                 count++;
853                 final int limit = count*10;
854                 for(int delUpto=0;delUpto<5;delUpto++) {
855                   int x = r.nextInt(limit);
856                   writer.deleteDocuments(new Term("field3", "b"+x));
857                 }
858               } catch (Throwable t) {
859                 excs.add(t);
860                 throw new RuntimeException(t);
861               }
862             } while(System.currentTimeMillis() < endTime);
863           }
864         };
865       threads[i].setDaemon(true);
866       threads[i].start();
867     }
868
869     int sum = 0;
870     while(System.currentTimeMillis() < endTime) {
871       IndexReader r2 = r.reopen();
872       if (r2 != r) {
873         r.close();
874         r = r2;
875       }
876       Query q = new TermQuery(new Term("indexname", "test"));
877       IndexSearcher searcher = newSearcher(r);
878       sum += searcher.search(q, 10).totalHits;
879       searcher.close();
880     }
881
882     for(int i=0;i<NUM_THREAD;i++) {
883       threads[i].join();
884     }
885     // at least search once
886     IndexReader r2 = r.reopen();
887     if (r2 != r) {
888       r.close();
889       r = r2;
890     }
891     Query q = new TermQuery(new Term("indexname", "test"));
892     IndexSearcher searcher = newSearcher(r);
893     sum += searcher.search(q, 10).totalHits;
894     searcher.close();
895     assertTrue("no documents found at all", sum > 0);
896
897     assertEquals(0, excs.size());
898     writer.close();
899
900     r.close();
901     dir1.close();
902   }
903
904   public void testExpungeDeletes() throws Throwable {
905     Directory dir = newDirectory();
906     final IndexWriter w = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
907     Document doc = new Document();
908     doc.add(newField("field", "a b c", Field.Store.NO, Field.Index.ANALYZED));
909     Field id = newField("id", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
910     doc.add(id);
911     id.setValue("0");
912     w.addDocument(doc);
913     id.setValue("1");
914     w.addDocument(doc);
915     w.deleteDocuments(new Term("id", "0"));
916
917     IndexReader r = w.getReader();
918     w.expungeDeletes();
919     w.close();
920     r.close();
921     r = IndexReader.open(dir, true);
922     assertEquals(1, r.numDocs());
923     assertFalse(r.hasDeletions());
924     r.close();
925     dir.close();
926   }
927
928   public void testDeletesNumDocs() throws Throwable {
929     Directory dir = newDirectory();
930     final IndexWriter w = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
931     Document doc = new Document();
932     doc.add(newField("field", "a b c", Field.Store.NO, Field.Index.ANALYZED));
933     Field id = newField("id", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
934     doc.add(id);
935     id.setValue("0");
936     w.addDocument(doc);
937     id.setValue("1");
938     w.addDocument(doc);
939     IndexReader r = w.getReader();
940     assertEquals(2, r.numDocs());
941     r.close();
942
943     w.deleteDocuments(new Term("id", "0"));
944     r = w.getReader();
945     assertEquals(1, r.numDocs());
946     r.close();
947
948     w.deleteDocuments(new Term("id", "1"));
949     r = w.getReader();
950     assertEquals(0, r.numDocs());
951     r.close();
952
953     w.close();
954     dir.close();
955   }
956   
957   public void testEmptyIndex() throws Exception {
958     // Ensures that getReader works on an empty index, which hasn't been committed yet.
959     Directory dir = newDirectory();
960     IndexWriter w = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
961     IndexReader r = w.getReader();
962     assertEquals(0, r.numDocs());
963     r.close();
964     w.close();
965     dir.close();
966   }
967
968   public void testSegmentWarmer() throws Exception {
969     Directory dir = newDirectory();
970     final AtomicBoolean didWarm = new AtomicBoolean();
971     IndexWriter w = new IndexWriter(
972         dir,
973         newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)).
974             setMaxBufferedDocs(2).
975             setReaderPooling(true).
976             setMergedSegmentWarmer(new IndexWriter.IndexReaderWarmer() {
977               @Override
978               public void warm(IndexReader r) throws IOException {
979                 IndexSearcher s = newSearcher(r);
980                 TopDocs hits = s.search(new TermQuery(new Term("foo", "bar")), 10);
981                 assertEquals(20, hits.totalHits);
982                 didWarm.set(true);
983                 s.close();
984               }
985             }).
986             setMergePolicy(newLogMergePolicy(10))
987     );
988
989     Document doc = new Document();
990     doc.add(newField("foo", "bar", Field.Store.YES, Field.Index.NOT_ANALYZED));
991     for(int i=0;i<20;i++) {
992       w.addDocument(doc);
993     }
994     w.waitForMerges();
995     w.close();
996     dir.close();
997     assertTrue(didWarm.get());
998   }
999   
1000   public void testNoTermsIndex() throws Exception {
1001     Directory dir = newDirectory();
1002     IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(
1003         TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT))
1004         .setReaderTermsIndexDivisor(-1));
1005     Document doc = new Document();
1006     doc.add(new Field("f", "val", Store.NO, Index.ANALYZED));
1007     w.addDocument(doc);
1008     IndexReader r = IndexReader.open(w, true);
1009     try {
1010       r.termDocs(new Term("f", "val"));
1011       fail("should have failed to seek since terms index was not loaded");
1012     } catch (IllegalStateException e) {
1013       // expected - we didn't load the term index
1014     } finally {
1015       r.close();
1016       w.close();
1017       dir.close();
1018     }
1019   }
1020   
1021 }