Index: lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/IndexWriter.java	(revision 1409801)
+++ lucene/core/src/java/org/apache/lucene/index/IndexWriter.java	(working copy)
@@ -2381,7 +2381,8 @@
                                          false, codec, null, null);
 
       SegmentMerger merger = new SegmentMerger(info, infoStream, trackingDir, config.getTermIndexInterval(),
-                                               MergeState.CheckAbort.NONE, globalFieldNumberMap, context);
+                                               MergeState.CheckAbort.NONE, globalFieldNumberMap, context, 
+                                               config.getMergedSegmentFilter());
 
       for (IndexReader reader : readers) {    // add new indexes
         merger.add(reader);
@@ -3551,7 +3552,7 @@
     final TrackingDirectoryWrapper dirWrapper = new TrackingDirectoryWrapper(directory);
 
     SegmentMerger merger = new SegmentMerger(merge.info.info, infoStream, dirWrapper, config.getTermIndexInterval(), checkAbort,
-                                             globalFieldNumberMap, context);
+                                             globalFieldNumberMap, context, config.getMergedSegmentFilter());
 
     if (infoStream.isEnabled("IW")) {
       infoStream.message("IW", "merging " + segString(merge.segments));
@@ -4063,6 +4064,25 @@
     public abstract void warm(AtomicReader reader) throws IOException;
   }
 
+  /**
+   * Interface for filtering index data during merging.
+   */
+  public static abstract class MergedSegmentFilter {
+
+    /** Sole constructor. (For invocation by subclass 
+     *  constructors, typically implicit.) */
+    protected MergedSegmentFilter() {
+      super();
+    }
+
+    /**
+     * Called to wrap <code>reader</code> for merging.
+     */
+    public abstract AtomicReader filter(AtomicReader reader);
+
+  }
+
+
   private void handleOOM(OutOfMemoryError oom, String location) {
     if (infoStream.isEnabled("IW")) {
       infoStream.message("IW", "hit OutOfMemoryError inside " + location);
Index: lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java	(revision 1409801)
+++ lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java	(working copy)
@@ -23,6 +23,7 @@
 import org.apache.lucene.codecs.Codec;
 import org.apache.lucene.index.DocumentsWriterPerThread.IndexingChain;
 import org.apache.lucene.index.IndexWriter.IndexReaderWarmer;
+import org.apache.lucene.index.IndexWriter.MergedSegmentFilter;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.util.InfoStream;
@@ -450,6 +451,11 @@
   public IndexReaderWarmer getMergedSegmentWarmer() {
     return super.getMergedSegmentWarmer();
   }
+
+  @Override
+  public MergedSegmentFilter getMergedSegmentFilter() {
+    return super.getMergedSegmentFilter();
+  }
   
   @Override
   public double getRAMBufferSizeMB() {
@@ -498,6 +504,12 @@
   public IndexWriterConfig setMergedSegmentWarmer(IndexReaderWarmer mergeSegmentWarmer) {
     return (IndexWriterConfig) super.setMergedSegmentWarmer(mergeSegmentWarmer);
   }
+
+  @Override
+  public IndexWriterConfig setMergedSegmentFilter(MergedSegmentFilter mergeSegmentFilter) {
+    return (IndexWriterConfig) super.setMergedSegmentFilter(mergeSegmentFilter);
+  }
+
   
   @Override
   public IndexWriterConfig setRAMBufferSizeMB(double ramBufferSizeMB) {
Index: lucene/core/src/java/org/apache/lucene/index/LiveIndexWriterConfig.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/LiveIndexWriterConfig.java	(revision 1409801)
+++ lucene/core/src/java/org/apache/lucene/index/LiveIndexWriterConfig.java	(working copy)
@@ -22,6 +22,7 @@
 import org.apache.lucene.codecs.lucene41.Lucene41PostingsFormat; // javadocs
 import org.apache.lucene.index.DocumentsWriterPerThread.IndexingChain;
 import org.apache.lucene.index.IndexWriter.IndexReaderWarmer;
+import org.apache.lucene.index.IndexWriter.MergedSegmentFilter;
 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.similarities.Similarity;
@@ -43,6 +44,8 @@
   private volatile int maxBufferedDeleteTerms;
   private volatile int readerTermsIndexDivisor;
   private volatile IndexReaderWarmer mergedSegmentWarmer;
+  private volatile MergedSegmentFilter mergedSegmentFilter;
+
   private volatile int termIndexInterval; // TODO: this should be private to the codec, not settable here
 
   // modified by IndexWriterConfig
@@ -98,6 +101,7 @@
   /** {@link Version} that {@link IndexWriter} should emulate. */
   protected final Version matchVersion;
 
+
   // used by IndexWriterConfig
   LiveIndexWriterConfig(Analyzer analyzer, Version matchVersion) {
     this.analyzer = analyzer;
@@ -107,6 +111,7 @@
     maxBufferedDeleteTerms = IndexWriterConfig.DEFAULT_MAX_BUFFERED_DELETE_TERMS;
     readerTermsIndexDivisor = IndexWriterConfig.DEFAULT_READER_TERMS_INDEX_DIVISOR;
     mergedSegmentWarmer = null;
+    mergedSegmentFilter = null;
     termIndexInterval = IndexWriterConfig.DEFAULT_TERM_INDEX_INTERVAL; // TODO: this should be private to the codec, not settable here
     delPolicy = new KeepOnlyLastCommitDeletionPolicy();
     commit = null;
@@ -132,6 +137,7 @@
     maxBufferedDeleteTerms = config.getMaxBufferedDeleteTerms();
     maxBufferedDocs = config.getMaxBufferedDocs();
     mergedSegmentWarmer = config.getMergedSegmentWarmer();
+    mergedSegmentFilter = config.getMergedSegmentFilter();
     ramBufferSizeMB = config.getRAMBufferSizeMB();
     readerTermsIndexDivisor = config.getReaderTermsIndexDivisor();
     termIndexInterval = config.getTermIndexInterval();
@@ -384,6 +390,22 @@
   }
 
   /**
+   * Get the merged segment warmer. See {@link MergedSegmentFilter}.
+   * 
+   * <p>
+   * Takes effect on the next merge.
+   */
+  public LiveIndexWriterConfig setMergedSegmentFilter(MergedSegmentFilter mergeSegmentFilter) {
+    this.mergedSegmentFilter = mergeSegmentFilter;
+    return this;
+  }
+
+  /** Returns the current merged segment filter. See {@link MergedSegmentFilter}. */
+  public MergedSegmentFilter getMergedSegmentFilter() {
+    return mergedSegmentFilter;
+  }
+
+  /**
    * Sets the termsIndexDivisor passed to any readers that IndexWriter opens,
    * for example when applying deletes or creating a near-real-time reader in
    * {@link DirectoryReader#open(IndexWriter, boolean)}. If you pass -1, the
@@ -548,6 +570,7 @@
     sb.append("maxBufferedDocs=").append(getMaxBufferedDocs()).append("\n");
     sb.append("maxBufferedDeleteTerms=").append(getMaxBufferedDeleteTerms()).append("\n");
     sb.append("mergedSegmentWarmer=").append(getMergedSegmentWarmer()).append("\n");
+    sb.append("mergedSegmentFilter=").append(getMergedSegmentFilter()).append("\n");
     sb.append("readerTermsIndexDivisor=").append(getReaderTermsIndexDivisor()).append("\n");
     sb.append("termIndexInterval=").append(getTermIndexInterval()).append("\n"); // TODO: this should be private to the codec, not settable here
     sb.append("delPolicy=").append(getIndexDeletionPolicy().getClass().getName()).append("\n");
Index: lucene/core/src/java/org/apache/lucene/index/SegmentMerger.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/SegmentMerger.java	(revision 1409801)
+++ lucene/core/src/java/org/apache/lucene/index/SegmentMerger.java	(working copy)
@@ -54,9 +54,17 @@
   private final MergeState mergeState = new MergeState();
   private final FieldInfos.Builder fieldInfosBuilder;
 
+  private final IndexWriter.MergedSegmentFilter filter;
+
+  SegmentMerger(SegmentInfo segmentInfo, InfoStream infoStream, Directory dir, int termIndexInterval,
+                MergeState.CheckAbort checkAbort, FieldInfos.FieldNumbers fieldNumbers, IOContext context) {
+    this(segmentInfo, infoStream, dir, termIndexInterval, checkAbort, fieldNumbers, context, null);
+  }
+
   // note, just like in codec apis Directory 'dir' is NOT the same as segmentInfo.dir!!
   SegmentMerger(SegmentInfo segmentInfo, InfoStream infoStream, Directory dir, int termIndexInterval,
-                MergeState.CheckAbort checkAbort, FieldInfos.FieldNumbers fieldNumbers, IOContext context) {
+                MergeState.CheckAbort checkAbort, FieldInfos.FieldNumbers fieldNumbers, IOContext context,
+                IndexWriter.MergedSegmentFilter filter) {
     mergeState.segmentInfo = segmentInfo;
     mergeState.infoStream = infoStream;
     mergeState.readers = new ArrayList<AtomicReader>();
@@ -65,6 +73,7 @@
     this.termIndexInterval = termIndexInterval;
     this.codec = segmentInfo.getCodec();
     this.context = context;
+    this.filter  = filter;
     this.fieldInfosBuilder = new FieldInfos.Builder(fieldNumbers);
   }
 
@@ -73,13 +82,19 @@
    */
   final void add(IndexReader reader) {
     for (final AtomicReaderContext ctx : reader.leaves()) {
-      final AtomicReader r = ctx.reader();
-      mergeState.readers.add(r);
+      add(ctx.reader());
     }
   }
 
-  final void add(SegmentReader reader) {
-    mergeState.readers.add(reader);
+  final void add(AtomicReader reader) {
+    if (filter != null) {
+      final AtomicReader filteredReader = filter.filter(reader);
+      if (filteredReader != null) {
+        mergeState.readers.add(filteredReader);
+      }
+    } else {
+      mergeState.readers.add(reader);
+    }
   }
 
   /**
Index: lucene/core/src/test/org/apache/lucene/index/TestIndexWriterConfig.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestIndexWriterConfig.java	(revision 1409801)
+++ lucene/core/src/test/org/apache/lucene/index/TestIndexWriterConfig.java	(working copy)
@@ -71,6 +71,7 @@
     assertEquals(IndexWriterConfig.DEFAULT_READER_POOLING, conf.getReaderPooling());
     assertTrue(DocumentsWriterPerThread.defaultIndexingChain == conf.getIndexingChain());
     assertNull(conf.getMergedSegmentWarmer());
+    assertNull(conf.getMergedSegmentFilter());
     assertEquals(IndexWriterConfig.DEFAULT_READER_TERMS_INDEX_DIVISOR, conf.getReaderTermsIndexDivisor());
     assertEquals(TieredMergePolicy.class, conf.getMergePolicy().getClass());
     assertEquals(ThreadAffinityDocumentsWriterThreadPool.class, conf.getIndexerThreadPool().getClass());
@@ -95,6 +96,7 @@
     getters.add("getMaxBufferedDocs");
     getters.add("getIndexingChain");
     getters.add("getMergedSegmentWarmer");
+    getters.add("getMergedSegmentFilter");
     getters.add("getMergePolicy");
     getters.add("getMaxThreadStates");
     getters.add("getReaderPooling");
Index: lucene/core/src/test/org/apache/lucene/index/TestMergedSegmentFilter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestMergedSegmentFilter.java	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/index/TestMergedSegmentFilter.java	(working copy)
@@ -0,0 +1,194 @@
+package org.apache.lucene.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.Assert;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.lucene.analysis.*;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Version;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.Bits;
+
+
+public class TestMergedSegmentFilter extends LuceneTestCase {
+
+
+  public void addDocument(IndexWriter writer) throws IOException {
+    final FieldType type = new FieldType();
+    type.setIndexed(true);
+    type.setStored(true);
+    type.setTokenized(true);
+    type.setIndexOptions(FieldInfo.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
+
+    final Document doc = new Document();
+    doc.add( new Field("a", "A Value", type) );
+    doc.add( new Field("b", "B Value", type) );
+    doc.add( new Field("c", "C Value", type) );
+    writer.addDocument(doc);
+  }
+
+
+  /**
+   * Test removing a field during segment merging.
+   */
+  public void testRemoveField() throws IOException {
+    final IndexWriterConfig config = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()));
+    config.setMergedSegmentFilter(new RemoveField());
+
+    final Directory dir = newDirectory();
+    try {
+      final IndexWriter writer = new IndexWriter(dir, config);
+      addDocument(writer);
+      writer.commit();
+      addDocument(writer);
+      writer.commit();
+      writer.forceMerge(1, true);
+      writer.close();
+
+      final IndexReader reader = DirectoryReader.open(dir);
+      try {
+        Assert.assertEquals("Optimized", 1, reader.leaves().size());
+
+        for (AtomicReaderContext segment : reader.leaves()) {
+          Assert.assertNotNull(segment.reader().terms("a"));
+          Assert.assertNull(segment.reader().terms("b"));
+          Assert.assertNotNull(segment.reader().terms("c"));
+        }
+      } finally {
+        reader.close();
+      }
+    } finally {
+      dir.close();
+    }
+  }
+
+
+  private static final class RemoveField extends IndexWriter.MergedSegmentFilter {
+    
+    @Override
+    public AtomicReader filter(AtomicReader reader) {
+      return new RemoveFieldReader(reader);
+    }
+  }
+
+  private static final class RemoveFieldReader extends AtomicReader {
+    private final AtomicReader reader;
+
+    public RemoveFieldReader(AtomicReader reader) {
+      this.reader = reader;
+    }
+
+    @Override
+    public Fields fields() throws IOException {
+      return new RemoveFieldFields(reader.fields());
+    }
+
+    @Override
+    public DocValues docValues(String field) throws IOException {
+      return reader.docValues(field);
+    }
+  
+    @Override
+    public DocValues normValues(String field) throws IOException {
+      return reader.normValues(field);
+    }
+
+    @Override
+    public FieldInfos getFieldInfos() {
+      return reader.getFieldInfos();
+    }
+  
+    @Override
+    public Bits getLiveDocs() {
+      return reader.getLiveDocs();
+    }
+
+    @Override
+    public void document(int doc, StoredFieldVisitor visitor) throws IOException {
+      reader.document(doc, visitor);
+    }
+    
+    @Override
+    public Fields getTermVectors(int doc) throws IOException {
+      return reader.getTermVectors(doc);
+    }
+
+    @Override
+    public int numDocs() {
+      return reader.numDocs();
+    }
+
+    @Override
+    public int maxDoc() {
+      return reader.maxDoc();
+    }
+
+    @Override
+    public boolean hasDeletions() {
+      return reader.hasDeletions();
+    }
+
+    @Override
+    public void doClose() throws IOException {
+      reader.doClose();
+    }
+
+  }
+
+  private static final class RemoveFieldFields extends Fields {
+    public final Fields fields;
+    private final List<String> fieldNames = new ArrayList<String>();
+
+    public RemoveFieldFields(Fields fields) {
+      this.fields = fields;
+      for (String name : fields) {
+        if (!"b".equals(name)) {
+          fieldNames.add(name);
+        }
+      }
+    }
+
+    @Override
+    public Iterator<String> iterator() {
+      return fieldNames.iterator();
+    }
+
+    @Override
+    public Terms terms(String field) throws IOException {
+      if ("b".equals(field)) {
+        return null;
+      } else {
+        return fields.terms(field);
+      }
+    }
+
+    @Override
+    public int size() {
+      return fieldNames.size();
+    }
+  }
+}

Property changes on: lucene/core/src/test/org/apache/lucene/index/TestMergedSegmentFilter.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
