Index: lucene/core/src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- lucene/core/src/java/org/apache/lucene/index/IndexWriter.java (revision 1504822) +++ lucene/core/src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -2429,6 +2429,10 @@ * close the writer. See above for details. * *

+ * NOTE: empty segments are dropped by this method and not added to this + * index. + * + *

* NOTE: this method merges all given {@link IndexReader}s in one * merge. If you intend to merge a large number of readers, it may be better * to call this method multiple times, each time with a small set of readers. @@ -2462,11 +2466,20 @@ String mergedName = newSegmentName(); final List mergeReaders = new ArrayList(); for (IndexReader indexReader : readers) { - numDocs += indexReader.numDocs(); - for (AtomicReaderContext ctx : indexReader.leaves()) { - mergeReaders.add(ctx.reader()); + if (indexReader.numDocs() > 0) { + numDocs += indexReader.numDocs(); + for (AtomicReaderContext ctx : indexReader.leaves()) { + if (ctx.reader().numDocs() > 0) { // drop empty (or all deleted) segments + mergeReaders.add(ctx.reader()); + } + } } } + + if (mergeReaders.isEmpty()) { // no segments with documents to add + return; + } + final IOContext context = new IOContext(new MergeInfo(numDocs, -1, true, -1)); // TODO: somehow we should fix this merge so it's Index: lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java =================================================================== --- lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java (revision 1504822) +++ lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java (working copy) @@ -43,6 +43,7 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.store.MockDirectoryWrapper; import org.apache.lucene.store.RAMDirectory; +import org.apache.lucene.util.Bits; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util._TestUtil; @@ -1205,4 +1206,53 @@ r3.close(); d3.close(); } + + public void testAddEmpty() throws Exception { + Directory d1 = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), d1); + MultiReader empty = new MultiReader(); + w.addIndexes(empty); + w.close(); + DirectoryReader dr = DirectoryReader.open(d1); + for (AtomicReaderContext ctx : dr.leaves()) { + assertTrue("empty segments should be dropped by addIndexes", ctx.reader().maxDoc() > 0); + } + dr.close(); + d1.close(); + } + + // Currently it's impossible to end up with a segment with all documents + // deleted, as such segments are dropped. Still, to validate that addIndexes + // works with such segments, or readers that end up in such state, we fake an + // all deleted segment. + public void testFakeAllDeleted() throws Exception { + Directory src = newDirectory(), dest = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), src); + w.addDocument(new Document()); + IndexReader allDeletedReader = new FilterAtomicReader(w.getReader().leaves().get(0).reader()) { + @Override + public Bits getLiveDocs() { + return new Bits() { + @Override public int length() { return 1; } + @Override public boolean get(int index) { return false; } + }; + } + @Override public boolean hasDeletions() { return true; } + @Override public int numDocs() { return 0; } + }; + w.close(); + + w = new RandomIndexWriter(random(), dest); + w.addIndexes(allDeletedReader); + w.close(); + DirectoryReader dr = DirectoryReader.open(src); + for (AtomicReaderContext ctx : dr.leaves()) { + assertTrue("empty segments should be dropped by addIndexes", ctx.reader().maxDoc() > 0); + } + dr.close(); + allDeletedReader.close(); + src.close(); + dest.close(); + } + }