| /* |
| * 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. |
| */ |
| package org.apache.lucene.index; |
| |
| import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean; |
| import static com.carrotsearch.randomizedtesting.RandomizedTest.randomLongBetween; |
| import static java.util.stream.Collectors.toList; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Random; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import org.apache.lucene.analysis.MockAnalyzer; |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.document.LongPoint; |
| import org.apache.lucene.search.DocIdSetIterator; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.TermQuery; |
| import org.apache.lucene.store.AlreadyClosedException; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.store.MockDirectoryWrapper.FakeIOException; |
| import org.apache.lucene.store.MockDirectoryWrapper; |
| import org.apache.lucene.store.RAMDirectory; |
| import org.apache.lucene.util.Bits; |
| import org.apache.lucene.util.BytesRef; |
| import org.apache.lucene.util.InfoStream; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.apache.lucene.util.LuceneTestCase.SuppressCodecs; |
| import org.apache.lucene.util.TestUtil; |
| import org.apache.lucene.util.ThreadInterruptedException; |
| import org.junit.Test; |
| |
| @SuppressCodecs("SimpleText") // too slow here |
| public class TestIndexWriterReader extends LuceneTestCase { |
| |
| private final int numThreads = TEST_NIGHTLY ? 5 : 2; |
| |
| public static int count(Term t, IndexReader r) throws IOException { |
| int count = 0; |
| PostingsEnum td = TestUtil.docs(random(), r, |
| t.field(), new BytesRef(t.text()), |
| null, |
| 0); |
| |
| if (td != null) { |
| final Bits liveDocs = MultiBits.getLiveDocs(r); |
| while (td.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) { |
| td.docID(); |
| if (liveDocs == null || liveDocs.get(td.docID())) { |
| count++; |
| } |
| } |
| } |
| return count; |
| } |
| |
| public void testAddCloseOpen() throws IOException { |
| // Can't use assertNoDeletes: this test pulls a non-NRT |
| // reader in the end: |
| Directory dir1 = newDirectory(); |
| IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random())); |
| |
| IndexWriter writer = new IndexWriter(dir1, iwc); |
| for (int i = 0; i < 97 ; i++) { |
| DirectoryReader reader = writer.getReader(); |
| if (i == 0) { |
| writer.addDocument(DocHelper.createDocument(i, "x", 1 + random().nextInt(5))); |
| } else { |
| int previous = random().nextInt(i); |
| // a check if the reader is current here could fail since there might be |
| // merges going on. |
| switch (random().nextInt(5)) { |
| case 0: |
| case 1: |
| case 2: |
| writer.addDocument(DocHelper.createDocument(i, "x", 1 + random().nextInt(5))); |
| break; |
| case 3: |
| writer.updateDocument(new Term("id", "" + previous), DocHelper.createDocument( |
| previous, "x", 1 + random().nextInt(5))); |
| break; |
| case 4: |
| writer.deleteDocuments(new Term("id", "" + previous)); |
| } |
| } |
| assertFalse(reader.isCurrent()); |
| reader.close(); |
| } |
| writer.forceMerge(1); // make sure all merging is done etc. |
| DirectoryReader reader = writer.getReader(); |
| writer.commit(); // no changes that are not visible to the reader |
| |
| // A commit is now seen as a change to an NRT reader: |
| assertFalse(reader.isCurrent()); |
| reader.close(); |
| reader = writer.getReader(); |
| assertTrue(reader.isCurrent()); |
| writer.close(); |
| |
| assertTrue(reader.isCurrent()); |
| iwc = newIndexWriterConfig(new MockAnalyzer(random())); |
| writer = new IndexWriter(dir1, iwc); |
| assertTrue(reader.isCurrent()); |
| writer.addDocument(DocHelper.createDocument(1, "x", 1+random().nextInt(5))); |
| assertTrue(reader.isCurrent()); // segments in ram but IW is different to the readers one |
| writer.close(); |
| assertFalse(reader.isCurrent()); // segments written |
| reader.close(); |
| dir1.close(); |
| } |
| |
| public void testUpdateDocument() throws Exception { |
| boolean doFullMerge = true; |
| |
| Directory dir1 = newDirectory(); |
| IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random())); |
| if (iwc.getMaxBufferedDocs() < 20) { |
| iwc.setMaxBufferedDocs(20); |
| } |
| // no merging |
| iwc.setMergePolicy(NoMergePolicy.INSTANCE); |
| if (VERBOSE) { |
| System.out.println("TEST: make index"); |
| } |
| IndexWriter writer = new IndexWriter(dir1, iwc); |
| |
| // create the index |
| createIndexNoClose(!doFullMerge, "index1", writer); |
| |
| // writer.flush(false, true, true); |
| |
| // get a reader |
| DirectoryReader r1 = writer.getReader(); |
| assertTrue(r1.isCurrent()); |
| |
| String id10 = r1.document(10).getField("id").stringValue(); |
| |
| Document newDoc = r1.document(10); |
| newDoc.removeField("id"); |
| newDoc.add(newStringField("id", Integer.toString(8000), Field.Store.YES)); |
| writer.updateDocument(new Term("id", id10), newDoc); |
| assertFalse(r1.isCurrent()); |
| |
| System.out.println("TEST: now get reader"); |
| DirectoryReader r2 = writer.getReader(); |
| assertTrue(r2.isCurrent()); |
| assertEquals(0, count(new Term("id", id10), r2)); |
| if (VERBOSE) { |
| System.out.println("TEST: verify id"); |
| } |
| assertEquals(1, count(new Term("id", Integer.toString(8000)), r2)); |
| |
| r1.close(); |
| assertTrue(r2.isCurrent()); |
| writer.close(); |
| // writer.close wrote a new commit |
| assertFalse(r2.isCurrent()); |
| |
| DirectoryReader r3 = DirectoryReader.open(dir1); |
| assertTrue(r3.isCurrent()); |
| assertFalse(r2.isCurrent()); |
| assertEquals(0, count(new Term("id", id10), r3)); |
| assertEquals(1, count(new Term("id", Integer.toString(8000)), r3)); |
| |
| writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random()))); |
| Document doc = new Document(); |
| doc.add(newTextField("field", "a b c", Field.Store.NO)); |
| writer.addDocument(doc); |
| assertFalse(r2.isCurrent()); |
| assertTrue(r3.isCurrent()); |
| |
| writer.close(); |
| |
| assertFalse(r2.isCurrent()); |
| assertTrue(!r3.isCurrent()); |
| |
| r2.close(); |
| r3.close(); |
| |
| dir1.close(); |
| } |
| |
| public void testIsCurrent() throws IOException { |
| Directory dir = newDirectory(); |
| IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random())); |
| |
| IndexWriter writer = new IndexWriter(dir, iwc); |
| Document doc = new Document(); |
| doc.add(newTextField("field", "a b c", Field.Store.NO)); |
| writer.addDocument(doc); |
| writer.close(); |
| |
| iwc = newIndexWriterConfig(new MockAnalyzer(random())); |
| writer = new IndexWriter(dir, iwc); |
| doc = new Document(); |
| doc.add(newTextField("field", "a b c", Field.Store.NO)); |
| DirectoryReader nrtReader = writer.getReader(); |
| assertTrue(nrtReader.isCurrent()); |
| writer.addDocument(doc); |
| assertFalse(nrtReader.isCurrent()); // should see the changes |
| writer.forceMerge(1); // make sure we don't have a merge going on |
| assertFalse(nrtReader.isCurrent()); |
| nrtReader.close(); |
| |
| DirectoryReader dirReader = DirectoryReader.open(dir); |
| nrtReader = writer.getReader(); |
| |
| assertTrue(dirReader.isCurrent()); |
| assertTrue(nrtReader.isCurrent()); // nothing was committed yet so we are still current |
| assertEquals(2, nrtReader.maxDoc()); // sees the actual document added |
| assertEquals(1, dirReader.maxDoc()); |
| writer.close(); // close is actually a commit both should see the changes |
| assertFalse(nrtReader.isCurrent()); |
| assertFalse(dirReader.isCurrent()); // this reader has been opened before the writer was closed / committed |
| |
| dirReader.close(); |
| nrtReader.close(); |
| dir.close(); |
| } |
| |
| /** |
| * Test using IW.addIndexes |
| */ |
| public void testAddIndexes() throws Exception { |
| boolean doFullMerge = false; |
| |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0); |
| if (iwc.getMaxBufferedDocs() < 20) { |
| iwc.setMaxBufferedDocs(20); |
| } |
| // no merging |
| iwc.setMergePolicy(NoMergePolicy.INSTANCE); |
| IndexWriter writer = new IndexWriter(dir1, iwc); |
| |
| // create the index |
| createIndexNoClose(!doFullMerge, "index1", writer); |
| writer.flush(false, true); |
| |
| // create a 2nd index |
| Directory dir2 = newDirectory(); |
| IndexWriter writer2 = new IndexWriter(dir2, newIndexWriterConfig(new MockAnalyzer(random()))); |
| createIndexNoClose(!doFullMerge, "index2", writer2); |
| writer2.close(); |
| |
| DirectoryReader r0 = writer.getReader(); |
| assertTrue(r0.isCurrent()); |
| writer.addIndexes(dir2); |
| assertFalse(r0.isCurrent()); |
| r0.close(); |
| |
| DirectoryReader r1 = writer.getReader(); |
| assertTrue(r1.isCurrent()); |
| |
| writer.commit(); |
| |
| // A commit is seen as a change to NRT reader: |
| assertFalse(r1.isCurrent()); |
| |
| assertEquals(200, r1.maxDoc()); |
| |
| int index2df = r1.docFreq(new Term("indexname", "index2")); |
| |
| assertEquals(100, index2df); |
| |
| // verify the docs are from different indexes |
| Document doc5 = r1.document(5); |
| assertEquals("index1", doc5.get("indexname")); |
| Document doc150 = r1.document(150); |
| assertEquals("index2", doc150.get("indexname")); |
| r1.close(); |
| writer.close(); |
| dir1.close(); |
| dir2.close(); |
| } |
| |
| public void testAddIndexes2() throws Exception { |
| boolean doFullMerge = false; |
| |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| |
| // create a 2nd index |
| Directory dir2 = newDirectory(); |
| IndexWriter writer2 = new IndexWriter(dir2, newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| createIndexNoClose(!doFullMerge, "index2", writer2); |
| writer2.close(); |
| |
| writer.addIndexes(dir2); |
| writer.addIndexes(dir2); |
| writer.addIndexes(dir2); |
| writer.addIndexes(dir2); |
| writer.addIndexes(dir2); |
| |
| IndexReader r1 = writer.getReader(); |
| assertEquals(500, r1.maxDoc()); |
| |
| r1.close(); |
| writer.close(); |
| dir1.close(); |
| dir2.close(); |
| } |
| |
| /** |
| * Deletes using IW.deleteDocuments |
| */ |
| public void testDeleteFromIndexWriter() throws Exception { |
| boolean doFullMerge = true; |
| |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| // create the index |
| createIndexNoClose(!doFullMerge, "index1", writer); |
| writer.flush(false, true); |
| // get a reader |
| IndexReader r1 = writer.getReader(); |
| |
| String id10 = r1.document(10).getField("id").stringValue(); |
| |
| // deleted IW docs should not show up in the next getReader |
| writer.deleteDocuments(new Term("id", id10)); |
| IndexReader r2 = writer.getReader(); |
| assertEquals(1, count(new Term("id", id10), r1)); |
| assertEquals(0, count(new Term("id", id10), r2)); |
| |
| String id50 = r1.document(50).getField("id").stringValue(); |
| assertEquals(1, count(new Term("id", id50), r1)); |
| |
| writer.deleteDocuments(new Term("id", id50)); |
| |
| IndexReader r3 = writer.getReader(); |
| assertEquals(0, count(new Term("id", id10), r3)); |
| assertEquals(0, count(new Term("id", id50), r3)); |
| |
| String id75 = r1.document(75).getField("id").stringValue(); |
| writer.deleteDocuments(new TermQuery(new Term("id", id75))); |
| IndexReader r4 = writer.getReader(); |
| assertEquals(1, count(new Term("id", id75), r3)); |
| assertEquals(0, count(new Term("id", id75), r4)); |
| |
| r1.close(); |
| r2.close(); |
| r3.close(); |
| r4.close(); |
| writer.close(); |
| |
| // reopen the writer to verify the delete made it to the directory |
| writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| IndexReader w2r1 = writer.getReader(); |
| assertEquals(0, count(new Term("id", id10), w2r1)); |
| w2r1.close(); |
| writer.close(); |
| dir1.close(); |
| } |
| |
| @Slow |
| public void testAddIndexesAndDoDeletesThreads() throws Throwable { |
| final int numIter = TEST_NIGHTLY ? 2 : 1; |
| int numDirs = TEST_NIGHTLY ? 3 : 2; |
| |
| Directory mainDir = getAssertNoDeletesDirectory(newDirectory()); |
| |
| IndexWriter mainWriter = new IndexWriter(mainDir, newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMergePolicy(newLogMergePolicy()) |
| .setMaxFullFlushMergeWaitMillis(0)); |
| TestUtil.reduceOpenFiles(mainWriter); |
| |
| AddDirectoriesThreads addDirThreads = new AddDirectoriesThreads(numIter, mainWriter); |
| addDirThreads.launchThreads(numDirs); |
| addDirThreads.joinThreads(); |
| |
| //assertEquals(100 + numDirs * (3 * numIter / 4) * addDirThreads.numThreads |
| // * addDirThreads.NUM_INIT_DOCS, addDirThreads.mainwriter.getDocStats().numDocs); |
| assertEquals(addDirThreads.count.intValue(), addDirThreads.mainWriter.getDocStats().numDocs); |
| |
| addDirThreads.close(true); |
| |
| assertTrue(addDirThreads.failures.size() == 0); |
| |
| TestUtil.checkIndex(mainDir); |
| |
| IndexReader reader = DirectoryReader.open(mainDir); |
| assertEquals(addDirThreads.count.intValue(), reader.numDocs()); |
| //assertEquals(100 + numDirs * (3 * numIter / 4) * addDirThreads.numThreads |
| // * addDirThreads.NUM_INIT_DOCS, reader.numDocs()); |
| reader.close(); |
| |
| addDirThreads.closeDir(); |
| mainDir.close(); |
| } |
| |
| private class AddDirectoriesThreads { |
| Directory addDir; |
| final static int NUM_INIT_DOCS = 100; |
| int numDirs; |
| final Thread[] threads = new Thread[numThreads]; |
| IndexWriter mainWriter; |
| final List<Throwable> failures = new ArrayList<>(); |
| DirectoryReader[] readers; |
| boolean didClose = false; |
| AtomicInteger count = new AtomicInteger(0); |
| AtomicInteger numaddIndexes = new AtomicInteger(0); |
| |
| public AddDirectoriesThreads(int numDirs, IndexWriter mainWriter) throws Throwable { |
| this.numDirs = numDirs; |
| this.mainWriter = mainWriter; |
| addDir = newDirectory(); |
| IndexWriter writer = new IndexWriter(addDir, newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMaxFullFlushMergeWaitMillis(0) |
| .setMaxBufferedDocs(2)); |
| TestUtil.reduceOpenFiles(writer); |
| for (int i = 0; i < NUM_INIT_DOCS; i++) { |
| Document doc = DocHelper.createDocument(i, "addindex", 4); |
| writer.addDocument(doc); |
| } |
| |
| writer.close(); |
| |
| readers = new DirectoryReader[numDirs]; |
| for (int i = 0; i < numDirs; i++) |
| readers[i] = DirectoryReader.open(addDir); |
| } |
| |
| void joinThreads() { |
| for (int i = 0; i < numThreads; i++) |
| try { |
| threads[i].join(); |
| } catch (InterruptedException ie) { |
| throw new ThreadInterruptedException(ie); |
| } |
| } |
| |
| void close(boolean doWait) throws Throwable { |
| didClose = true; |
| if (doWait) { |
| mainWriter.close(); |
| } else { |
| mainWriter.rollback(); |
| } |
| } |
| |
| void closeDir() throws Throwable { |
| for (int i = 0; i < numDirs; i++) { |
| readers[i].close(); |
| } |
| addDir.close(); |
| } |
| |
| void handle(Throwable t) { |
| t.printStackTrace(System.out); |
| synchronized (failures) { |
| failures.add(t); |
| } |
| } |
| |
| void launchThreads(final int numIter) { |
| for (int i = 0; i < numThreads; i++) { |
| threads[i] = new Thread() { |
| @Override |
| public void run() { |
| try { |
| final Directory[] dirs = new Directory[numDirs]; |
| for (int k = 0; k < numDirs; k++) |
| dirs[k] = new MockDirectoryWrapper(random(), TestUtil.ramCopyOf(addDir)); |
| //int j = 0; |
| //while (true) { |
| // System.out.println(Thread.currentThread().getName() + ": iter |
| // j=" + j); |
| for (int x=0; x < numIter; x++) { |
| // only do addIndexes |
| doBody(x, dirs); |
| } |
| //if (numIter > 0 && j == numIter) |
| // break; |
| //doBody(j++, dirs); |
| //doBody(5, dirs); |
| //} |
| } catch (Throwable t) { |
| handle(t); |
| } |
| } |
| }; |
| } |
| for (int i = 0; i < numThreads; i++) |
| threads[i].start(); |
| } |
| |
| void doBody(int j, Directory[] dirs) throws Throwable { |
| switch (j % 4) { |
| case 0: |
| mainWriter.addIndexes(dirs); |
| mainWriter.forceMerge(1); |
| break; |
| case 1: |
| mainWriter.addIndexes(dirs); |
| numaddIndexes.incrementAndGet(); |
| break; |
| case 2: |
| TestUtil.addIndexesSlowly(mainWriter, readers); |
| break; |
| case 3: |
| mainWriter.commit(); |
| } |
| count.addAndGet(dirs.length*NUM_INIT_DOCS); |
| } |
| } |
| |
| public void testIndexWriterReopenSegmentFullMerge() throws Exception { |
| doTestIndexWriterReopenSegment(true); |
| } |
| |
| public void testIndexWriterReopenSegment() throws Exception { |
| doTestIndexWriterReopenSegment(false); |
| } |
| |
| /** |
| * Tests creating a segment, then check to insure the segment can be seen via |
| * IW.getReader |
| */ |
| public void doTestIndexWriterReopenSegment(boolean doFullMerge) throws Exception { |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMaxFullFlushMergeWaitMillis(0)); |
| IndexReader r1 = writer.getReader(); |
| assertEquals(0, r1.maxDoc()); |
| createIndexNoClose(false, "index1", writer); |
| writer.flush(!doFullMerge, true); |
| |
| IndexReader iwr1 = writer.getReader(); |
| assertEquals(100, iwr1.maxDoc()); |
| |
| IndexReader r2 = writer.getReader(); |
| assertEquals(r2.maxDoc(), 100); |
| // add 100 documents |
| for (int x = 10000; x < 10000 + 100; x++) { |
| Document d = DocHelper.createDocument(x, "index1", 5); |
| writer.addDocument(d); |
| } |
| writer.flush(false, true); |
| // verify the reader was reopened internally |
| IndexReader iwr2 = writer.getReader(); |
| assertTrue(iwr2 != r1); |
| assertEquals(200, iwr2.maxDoc()); |
| // should have flushed out a segment |
| IndexReader r3 = writer.getReader(); |
| assertTrue(r2 != r3); |
| assertEquals(200, r3.maxDoc()); |
| |
| // dec ref the readers rather than close them because |
| // closing flushes changes to the writer |
| r1.close(); |
| iwr1.close(); |
| r2.close(); |
| r3.close(); |
| iwr2.close(); |
| writer.close(); |
| |
| // test whether the changes made it to the directory |
| writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| IndexReader w2r1 = writer.getReader(); |
| // insure the deletes were actually flushed to the directory |
| assertEquals(200, w2r1.maxDoc()); |
| w2r1.close(); |
| writer.close(); |
| |
| dir1.close(); |
| } |
| |
| /* |
| * Delete a document by term and return the doc id |
| * |
| * public static int deleteDocument(Term term, IndexWriter writer) throws |
| * IOException { IndexReader reader = writer.getReader(); TermDocs td = |
| * reader.termDocs(term); int doc = -1; //if (td.next()) { // doc = td.doc(); |
| * //} //writer.deleteDocuments(term); td.close(); return doc; } |
| */ |
| |
| public static void createIndex(Random random, Directory dir1, String indexName, |
| boolean multiSegment) throws IOException { |
| IndexWriter w = new IndexWriter(dir1, LuceneTestCase.newIndexWriterConfig(random, new MockAnalyzer(random)) |
| .setMergePolicy(new LogDocMergePolicy())); |
| for (int i = 0; i < 100; i++) { |
| w.addDocument(DocHelper.createDocument(i, indexName, 4)); |
| } |
| if (!multiSegment) { |
| w.forceMerge(1); |
| } |
| w.close(); |
| } |
| |
| public static void createIndexNoClose(boolean multiSegment, String indexName, |
| IndexWriter w) throws IOException { |
| for (int i = 0; i < 100; i++) { |
| w.addDocument(DocHelper.createDocument(i, indexName, 4)); |
| } |
| if (!multiSegment) { |
| w.forceMerge(1); |
| } |
| } |
| |
| public void testMergeWarmer() throws Exception { |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| // Enroll warmer |
| AtomicInteger warmCount = new AtomicInteger(); |
| IndexWriter writer = new IndexWriter( |
| dir1, |
| newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMaxBufferedDocs(2) |
| .setMaxFullFlushMergeWaitMillis(0) |
| .setMergedSegmentWarmer((leafReader) -> warmCount.incrementAndGet()) |
| .setMergeScheduler(new ConcurrentMergeScheduler()) |
| .setMergePolicy(newLogMergePolicy()) |
| ); |
| |
| // create the index |
| createIndexNoClose(false, "test", writer); |
| |
| // get a reader to put writer into near real-time mode |
| IndexReader r1 = writer.getReader(); |
| |
| ((LogMergePolicy) writer.getConfig().getMergePolicy()).setMergeFactor(2); |
| |
| int num = TEST_NIGHTLY ? atLeast(100) : atLeast(10); |
| for (int i = 0; i < num; i++) { |
| writer.addDocument(DocHelper.createDocument(i, "test", 4)); |
| } |
| ((ConcurrentMergeScheduler) writer.getConfig().getMergeScheduler()).sync(); |
| |
| assertTrue(warmCount.get() > 0); |
| final int count = warmCount.get(); |
| |
| writer.addDocument(DocHelper.createDocument(17, "test", 4)); |
| writer.forceMerge(1); |
| assertTrue(warmCount.get() > count); |
| |
| writer.close(); |
| r1.close(); |
| dir1.close(); |
| } |
| |
| public void testAfterCommit() throws Exception { |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMergeScheduler(new ConcurrentMergeScheduler()) |
| .setMaxFullFlushMergeWaitMillis(0)); |
| writer.commit(); |
| |
| // create the index |
| createIndexNoClose(false, "test", writer); |
| |
| // get a reader to put writer into near real-time mode |
| DirectoryReader r1 = writer.getReader(); |
| TestUtil.checkIndex(dir1); |
| writer.commit(); |
| TestUtil.checkIndex(dir1); |
| assertEquals(100, r1.numDocs()); |
| |
| for (int i = 0; i < 10; i++) { |
| writer.addDocument(DocHelper.createDocument(i, "test", 4)); |
| } |
| ((ConcurrentMergeScheduler) writer.getConfig().getMergeScheduler()).sync(); |
| |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r1); |
| if (r2 != null) { |
| r1.close(); |
| r1 = r2; |
| } |
| assertEquals(110, r1.numDocs()); |
| writer.close(); |
| r1.close(); |
| dir1.close(); |
| } |
| |
| // Make sure reader remains usable even if IndexWriter closes |
| public void testAfterClose() throws Exception { |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| |
| // create the index |
| createIndexNoClose(false, "test", writer); |
| |
| DirectoryReader r = writer.getReader(); |
| writer.close(); |
| |
| TestUtil.checkIndex(dir1); |
| |
| // reader should remain usable even after IndexWriter is closed: |
| assertEquals(100, r.numDocs()); |
| Query q = new TermQuery(new Term("indexname", "test")); |
| IndexSearcher searcher = newSearcher(r); |
| assertEquals(100, searcher.count(q)); |
| |
| expectThrows(AlreadyClosedException.class, () -> { |
| DirectoryReader.openIfChanged(r); |
| }); |
| |
| r.close(); |
| dir1.close(); |
| } |
| |
| // Stress test reopen during addIndexes |
| @Nightly |
| public void testDuringAddIndexes() throws Exception { |
| Directory dir1 = getAssertNoDeletesDirectory(newDirectory()); |
| final IndexWriter writer = new IndexWriter( |
| dir1, |
| newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0) |
| .setMergePolicy(newLogMergePolicy(2)) |
| ); |
| |
| // create the index |
| createIndexNoClose(false, "test", writer); |
| writer.commit(); |
| |
| final Directory[] dirs = new Directory[10]; |
| for (int i=0;i<10;i++) { |
| dirs[i] = new MockDirectoryWrapper(random(), TestUtil.ramCopyOf(dir1)); |
| } |
| |
| DirectoryReader r = writer.getReader(); |
| |
| final int numIterations = 10; |
| final List<Throwable> excs = Collections.synchronizedList(new ArrayList<Throwable>()); |
| |
| // Only one thread can addIndexes at a time, because |
| // IndexWriter acquires a write lock in each directory: |
| final Thread[] threads = new Thread[1]; |
| final AtomicBoolean threadDone = new AtomicBoolean(false); |
| for(int i=0;i<threads.length;i++) { |
| threads[i] = new Thread() { |
| @Override |
| public void run() { |
| int count = 0; |
| do { |
| count++; |
| try { |
| writer.addIndexes(dirs); |
| writer.maybeMerge(); |
| } catch (Throwable t) { |
| excs.add(t); |
| throw new RuntimeException(t); |
| } |
| } while(count < numIterations); |
| threadDone.set(true); |
| } |
| }; |
| threads[i].setDaemon(true); |
| threads[i].start(); |
| } |
| |
| long lastCount = 0; |
| while(threadDone.get() == false) { |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r); |
| if (r2 != null) { |
| r.close(); |
| r = r2; |
| Query q = new TermQuery(new Term("indexname", "test")); |
| IndexSearcher searcher = newSearcher(r); |
| final long count = searcher.count(q); |
| assertTrue(count >= lastCount); |
| lastCount = count; |
| } |
| } |
| |
| for(int i=0;i<threads.length;i++) { |
| threads[i].join(); |
| } |
| // final check |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r); |
| if (r2 != null) { |
| r.close(); |
| r = r2; |
| } |
| Query q = new TermQuery(new Term("indexname", "test")); |
| IndexSearcher searcher = newSearcher(r); |
| final long count = searcher.count(q); |
| assertTrue(count >= lastCount); |
| |
| assertEquals(0, excs.size()); |
| r.close(); |
| if (dir1 instanceof MockDirectoryWrapper) { |
| final Collection<String> openDeletedFiles = ((MockDirectoryWrapper)dir1).getOpenDeletedFiles(); |
| assertEquals("openDeleted=" + openDeletedFiles, 0, openDeletedFiles.size()); |
| } |
| |
| writer.close(); |
| |
| dir1.close(); |
| } |
| |
| private Directory getAssertNoDeletesDirectory(Directory directory) { |
| if (directory instanceof MockDirectoryWrapper) { |
| ((MockDirectoryWrapper)directory).setAssertNoDeleteOpenFile(true); |
| } |
| return directory; |
| } |
| |
| // Stress test reopen during add/delete |
| public void testDuringAddDelete() throws Exception { |
| Directory dir1 = newDirectory(); |
| IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMergePolicy(newLogMergePolicy(2)); |
| if (TEST_NIGHTLY) { |
| // if we have a ton of iterations we need to make sure we don't do unnecessary |
| // extra flushing otherwise we will timeout on nightly |
| iwc.setRAMBufferSizeMB(IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB); |
| iwc.setMaxBufferedDocs(IndexWriterConfig.DISABLE_AUTO_FLUSH); |
| } |
| final IndexWriter writer = new IndexWriter( |
| dir1,iwc); |
| |
| // create the index |
| createIndexNoClose(false, "test", writer); |
| writer.commit(); |
| |
| DirectoryReader r = writer.getReader(); |
| |
| final int iters = TEST_NIGHTLY ? 1000 : 10; |
| final List<Throwable> excs = Collections.synchronizedList(new ArrayList<>()); |
| |
| final Thread[] threads = new Thread[numThreads]; |
| final AtomicInteger remainingThreads = new AtomicInteger(numThreads); |
| for(int i=0;i<numThreads;i++) { |
| threads[i] = new Thread() { |
| final Random r = new Random(random().nextLong()); |
| |
| @Override |
| public void run() { |
| int count = 0; |
| do { |
| try { |
| for(int docUpto=0;docUpto<10;docUpto++) { |
| writer.addDocument(DocHelper.createDocument(10*count+docUpto, "test", 4)); |
| } |
| count++; |
| final int limit = count*10; |
| for(int delUpto=0;delUpto<5;delUpto++) { |
| int x = r.nextInt(limit); |
| writer.deleteDocuments(new Term("field3", "b"+x)); |
| } |
| } catch (Throwable t) { |
| excs.add(t); |
| throw new RuntimeException(t); |
| } |
| } while(count < iters); |
| remainingThreads.decrementAndGet(); |
| } |
| }; |
| threads[i].setDaemon(true); |
| threads[i].start(); |
| } |
| |
| int sum = 0; |
| while(remainingThreads.get() > 0) { |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r); |
| if (r2 != null) { |
| r.close(); |
| r = r2; |
| Query q = new TermQuery(new Term("indexname", "test")); |
| IndexSearcher searcher = newSearcher(r); |
| sum += searcher.count(q); |
| } |
| } |
| |
| for(int i=0;i<numThreads;i++) { |
| threads[i].join(); |
| } |
| // at least search once |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r); |
| if (r2 != null) { |
| r.close(); |
| r = r2; |
| } |
| Query q = new TermQuery(new Term("indexname", "test")); |
| IndexSearcher searcher = newSearcher(r); |
| sum += searcher.count(q); |
| assertTrue("no documents found at all", sum > 0); |
| |
| assertEquals(0, excs.size()); |
| writer.close(); |
| |
| r.close(); |
| dir1.close(); |
| } |
| |
| public void testForceMergeDeletes() throws Throwable { |
| Directory dir = newDirectory(); |
| final IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMergePolicy(newLogMergePolicy())); |
| Document doc = new Document(); |
| doc.add(newTextField("field", "a b c", Field.Store.NO)); |
| Field id = newStringField("id", "", Field.Store.NO); |
| doc.add(id); |
| id.setStringValue("0"); |
| w.addDocument(doc); |
| id.setStringValue("1"); |
| w.addDocument(doc); |
| w.deleteDocuments(new Term("id", "0")); |
| |
| IndexReader r = w.getReader(); |
| w.forceMergeDeletes(); |
| w.close(); |
| r.close(); |
| r = DirectoryReader.open(dir); |
| assertEquals(1, r.numDocs()); |
| assertFalse(r.hasDeletions()); |
| r.close(); |
| dir.close(); |
| } |
| |
| public void testDeletesNumDocs() throws Throwable { |
| Directory dir = newDirectory(); |
| final IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random()))); |
| Document doc = new Document(); |
| doc.add(newTextField("field", "a b c", Field.Store.NO)); |
| Field id = newStringField("id", "", Field.Store.NO); |
| doc.add(id); |
| id.setStringValue("0"); |
| w.addDocument(doc); |
| id.setStringValue("1"); |
| w.addDocument(doc); |
| IndexReader r = w.getReader(); |
| assertEquals(2, r.numDocs()); |
| r.close(); |
| |
| w.deleteDocuments(new Term("id", "0")); |
| r = w.getReader(); |
| assertEquals(1, r.numDocs()); |
| r.close(); |
| |
| w.deleteDocuments(new Term("id", "1")); |
| r = w.getReader(); |
| assertEquals(0, r.numDocs()); |
| r.close(); |
| |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testEmptyIndex() throws Exception { |
| // Ensures that getReader works on an empty index, which hasn't been committed yet. |
| Directory dir = newDirectory(); |
| IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random()))); |
| IndexReader r = w.getReader(); |
| assertEquals(0, r.numDocs()); |
| r.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testSegmentWarmer() throws Exception { |
| Directory dir = newDirectory(); |
| final AtomicBoolean didWarm = new AtomicBoolean(); |
| IndexWriter w = new IndexWriter( |
| dir, |
| newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMaxBufferedDocs(2) |
| .setReaderPooling(true) |
| .setMergedSegmentWarmer((r) -> { |
| IndexSearcher s = newSearcher(r); |
| int count = s.count(new TermQuery(new Term("foo", "bar"))); |
| assertEquals(20, count); |
| didWarm.set(true); |
| }) |
| .setMergePolicy(newLogMergePolicy(10)) |
| ); |
| |
| Document doc = new Document(); |
| doc.add(newStringField("foo", "bar", Field.Store.NO)); |
| for (int i = 0; i < 20; i++) { |
| w.addDocument(doc); |
| } |
| w.waitForMerges(); |
| w.close(); |
| dir.close(); |
| assertTrue(didWarm.get()); |
| } |
| |
| public void testSimpleMergedSegmentWarmer() throws Exception { |
| Directory dir = newDirectory(); |
| final AtomicBoolean didWarm = new AtomicBoolean(); |
| InfoStream infoStream = new InfoStream() { |
| @Override |
| public void close() throws IOException {} |
| |
| @Override |
| public void message(String component, String message) { |
| if ("SMSW".equals(component)) { |
| didWarm.set(true); |
| } |
| } |
| |
| @Override |
| public boolean isEnabled(String component) { |
| return true; |
| } |
| }; |
| IndexWriter w = new IndexWriter( |
| dir, |
| newIndexWriterConfig(new MockAnalyzer(random())) |
| .setMaxBufferedDocs(2) |
| .setReaderPooling(true) |
| .setInfoStream(infoStream) |
| .setMergedSegmentWarmer(new SimpleMergedSegmentWarmer(infoStream)) |
| .setMergePolicy(newLogMergePolicy(10)) |
| ); |
| |
| Document doc = new Document(); |
| doc.add(newStringField("foo", "bar", Field.Store.NO)); |
| for(int i=0;i<20;i++) { |
| w.addDocument(doc); |
| } |
| w.waitForMerges(); |
| w.close(); |
| dir.close(); |
| assertTrue(didWarm.get()); |
| } |
| |
| public void testReopenAfterNoRealChange() throws Exception { |
| Directory d = getAssertNoDeletesDirectory(newDirectory()); |
| IndexWriter w = new IndexWriter( |
| d, |
| newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0)); |
| |
| DirectoryReader r = w.getReader(); // start pooling readers |
| |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r); |
| assertNull(r2); |
| |
| w.addDocument(new Document()); |
| DirectoryReader r3 = DirectoryReader.openIfChanged(r); |
| assertNotNull(r3); |
| assertTrue(r3.getVersion() != r.getVersion()); |
| assertTrue(r3.isCurrent()); |
| |
| // Deletes nothing in reality...: |
| w.deleteDocuments(new Term("foo", "bar")); |
| |
| // ... but IW marks this as not current: |
| assertFalse(r3.isCurrent()); |
| DirectoryReader r4 = DirectoryReader.openIfChanged(r3); |
| assertNull(r4); |
| |
| // Deletes nothing in reality...: |
| w.deleteDocuments(new Term("foo", "bar")); |
| DirectoryReader r5 = DirectoryReader.openIfChanged(r3, w); |
| assertNull(r5); |
| |
| r3.close(); |
| |
| w.close(); |
| d.close(); |
| } |
| |
| @Test |
| public void testNRTOpenExceptions() throws Exception { |
| // LUCENE-5262: test that several failed attempts to obtain an NRT reader |
| // don't leak file handles. |
| MockDirectoryWrapper dir = (MockDirectoryWrapper) getAssertNoDeletesDirectory(newMockDirectory()); |
| final AtomicBoolean shouldFail = new AtomicBoolean(); |
| dir.failOn(new MockDirectoryWrapper.Failure() { |
| @Override |
| public void eval(MockDirectoryWrapper dir) throws IOException { |
| if (shouldFail.get()) { |
| if (callStackContainsAnyOf("getReadOnlyClone")) { |
| if (VERBOSE) { |
| System.out.println("TEST: now fail; exc:"); |
| new Throwable().printStackTrace(System.out); |
| } |
| shouldFail.set(false); |
| throw new FakeIOException(); |
| } |
| } |
| } |
| }); |
| |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0); |
| conf.setMergePolicy(NoMergePolicy.INSTANCE); // prevent merges from getting in the way |
| IndexWriter writer = new IndexWriter(dir, conf); |
| |
| // create a segment and open an NRT reader |
| writer.addDocument(new Document()); |
| writer.getReader().close(); |
| |
| // add a new document so a new NRT reader is required |
| writer.addDocument(new Document()); |
| |
| // try to obtain an NRT reader twice: first time it fails and closes all the |
| // other NRT readers. second time it fails, but also fails to close the |
| // other NRT reader, since it is already marked closed! |
| for (int i = 0; i < 2; i++) { |
| shouldFail.set(true); |
| expectThrows(FakeIOException.class, () -> { |
| writer.getReader().close(); |
| }); |
| } |
| |
| writer.close(); |
| dir.close(); |
| } |
| |
| /** Make sure if all we do is open NRT reader against |
| * writer, we don't see merge starvation. */ |
| public void testTooManySegments() throws Exception { |
| Directory dir = getAssertNoDeletesDirectory(new RAMDirectory()); |
| // Don't use newIndexWriterConfig, because we need a |
| // "sane" mergePolicy: |
| IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random())).setMaxFullFlushMergeWaitMillis(0); |
| IndexWriter w = new IndexWriter(dir, iwc); |
| // Create 500 segments: |
| for(int i=0;i<500;i++) { |
| Document doc = new Document(); |
| doc.add(newStringField("id", ""+i, Field.Store.NO)); |
| w.addDocument(doc); |
| IndexReader r = DirectoryReader.open(w); |
| // Make sure segment count never exceeds 100: |
| assertTrue(r.leaves().size() < 100); |
| r.close(); |
| } |
| w.close(); |
| dir.close(); |
| } |
| |
| // LUCENE-5912: make sure when you reopen an NRT reader using a commit point, the SegmentReaders are in fact shared: |
| public void testReopenNRTReaderOnCommit() throws Exception { |
| Directory dir = newDirectory(); |
| IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random())); |
| IndexWriter w = new IndexWriter(dir, iwc); |
| w.addDocument(new Document()); |
| |
| // Pull NRT reader; it has 1 segment: |
| DirectoryReader r1 = DirectoryReader.open(w); |
| assertEquals(1, r1.leaves().size()); |
| w.addDocument(new Document()); |
| w.commit(); |
| |
| List<IndexCommit> commits = DirectoryReader.listCommits(dir); |
| assertEquals(1, commits.size()); |
| DirectoryReader r2 = DirectoryReader.openIfChanged(r1, commits.get(0)); |
| assertEquals(2, r2.leaves().size()); |
| |
| // Make sure we shared same instance of SegmentReader w/ first reader: |
| assertTrue(r1.leaves().get(0).reader() == r2.leaves().get(0).reader()); |
| r1.close(); |
| r2.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testIndexReaderWriterWithLeafSorter() throws IOException { |
| final String FIELD_NAME = "field1"; |
| final boolean ASC_SORT = randomBoolean(); |
| final long MISSING_VALUE = |
| ASC_SORT ? Long.MAX_VALUE : Long.MIN_VALUE; // missing values at the end |
| |
| // create a comparator that sort leaf readers according with |
| // the min value (asc sort) or max value (desc sort) of its points |
| Comparator<LeafReader> leafSorter = Comparator.comparingLong( |
| r -> { |
| try { |
| PointValues points = r.getPointValues(FIELD_NAME); |
| if (points != null) { |
| byte[] sortValue = |
| ASC_SORT ? points.getMinPackedValue() : points.getMaxPackedValue(); |
| return LongPoint.decodeDimension(sortValue, 0); |
| } |
| } catch (IOException e) { |
| } |
| return MISSING_VALUE; |
| }); |
| if (ASC_SORT == false) { |
| leafSorter = leafSorter.reversed(); |
| } |
| |
| final int NUM_DOCS = atLeast(30); |
| Directory dir = newDirectory(); |
| IndexWriterConfig iwc = new IndexWriterConfig(); |
| iwc.setLeafSorter(leafSorter); |
| IndexWriter writer = new IndexWriter(dir, iwc); |
| for (int i = 0; i < NUM_DOCS; ++i) { |
| final Document doc = new Document(); |
| doc.add(new LongPoint(FIELD_NAME, randomLongBetween(1, 99))); |
| writer.addDocument(doc); |
| if (i > 0 && i % 10 == 0) writer.flush(); |
| } |
| |
| // Test1: test that leafReaders are sorted according to leafSorter provided in IndexWriterConfig |
| { |
| try (DirectoryReader reader = writer.getReader()) { |
| assertLeavesSorted(reader, leafSorter); |
| |
| // add more documents that should be sorted first |
| final long FIRST_VALUE = ASC_SORT ? 0 : 100; |
| for (int i = 0; i < 10; ++i) { |
| final Document doc = new Document(); |
| doc.add(new LongPoint(FIELD_NAME, FIRST_VALUE)); |
| writer.addDocument(doc); |
| } |
| writer.commit(); |
| |
| // and open again |
| try (DirectoryReader reader2 = DirectoryReader.openIfChanged(reader)) { |
| assertLeavesSorted(reader2, leafSorter); |
| } |
| } |
| } |
| |
| // Test2: test that leafReaders are sorted according to the provided leafSorter when opened from |
| // directory |
| { |
| try (DirectoryReader reader = DirectoryReader.open(dir, leafSorter)) { |
| assertLeavesSorted(reader, leafSorter); |
| |
| // add more documents that should be sorted first |
| final long FIRST_VALUE = ASC_SORT ? 0 : 100; |
| for (int i = 0; i < 10; ++i) { |
| final Document doc = new Document(); |
| doc.add(new LongPoint(FIELD_NAME, FIRST_VALUE)); |
| writer.addDocument(doc); |
| } |
| writer.commit(); |
| |
| // and open again |
| try (DirectoryReader reader2 = DirectoryReader.openIfChanged(reader)) { |
| assertLeavesSorted(reader2, leafSorter); |
| } |
| } |
| } |
| |
| // Test3: test that FilterDirectoryReader sorts leaves according |
| // to leafSorter of its wrapped reader |
| { |
| try (DirectoryReader reader = |
| new AssertingDirectoryReader(DirectoryReader.open(dir, leafSorter))) { |
| assertLeavesSorted(reader, leafSorter); |
| |
| // add more documents that should be sorted first |
| final long FIRST_VALUE = ASC_SORT ? 0 : 100; |
| for (int i = 0; i < 10; ++i) { |
| final Document doc = new Document(); |
| doc.add(new LongPoint(FIELD_NAME, FIRST_VALUE)); |
| writer.addDocument(doc); |
| } |
| writer.commit(); |
| |
| // and open again |
| try (DirectoryReader reader2 = DirectoryReader.openIfChanged(reader)) { |
| assertLeavesSorted(reader2, leafSorter); |
| } |
| } |
| } |
| |
| // Test4: test that leafReaders are sorted according to the provided leafSorter when opened from |
| // commit |
| { |
| List<IndexCommit> commits = DirectoryReader.listCommits(dir); |
| IndexCommit latestCommit = commits.get(commits.size() - 1); |
| try (DirectoryReader reader = |
| DirectoryReader.open(latestCommit, leafSorter)) { |
| assertLeavesSorted(reader, leafSorter); |
| |
| // add more documents that should be sorted first |
| final long FIRST_VALUE = ASC_SORT ? 0 : 100; |
| for (int i = 0; i < 10; ++i) { |
| final Document doc = new Document(); |
| doc.add(new LongPoint(FIELD_NAME, FIRST_VALUE)); |
| writer.addDocument(doc); |
| } |
| writer.commit(); |
| |
| // and open again |
| try (DirectoryReader reader2 = DirectoryReader.openIfChanged(reader)) { |
| assertLeavesSorted(reader2, leafSorter); |
| } |
| } |
| } |
| |
| writer.close(); |
| dir.close(); |
| } |
| |
| // assert that the leaf readers of the provided directory reader are sorted according to the |
| // provided leafSorter |
| private static void assertLeavesSorted( |
| DirectoryReader reader, Comparator<LeafReader> leafSorter) { |
| List<LeafReader> lrs = |
| reader.leaves().stream().map(LeafReaderContext::reader).collect(toList()); |
| List<LeafReader> expectedSortedlrs = |
| reader.leaves().stream() |
| .map(LeafReaderContext::reader) |
| .sorted(leafSorter) |
| .collect(toList()); |
| assertEquals(expectedSortedlrs, lrs); |
| } |
| } |