Index: org/apache/lucene/document/AbstractField.java
===================================================================
--- org/apache/lucene/document/AbstractField.java	(revision 653899)
+++ org/apache/lucene/document/AbstractField.java	(working copy)
@@ -32,6 +32,7 @@
   protected boolean isTokenized = true;
   protected boolean isBinary = false;
   protected boolean isCompressed = false;
+  protected boolean isStoreTermDocs = false;
   protected boolean lazy = false;
   protected float boost = 1.0f;
   // the one and only data object for all different kind of field values
@@ -42,7 +43,7 @@
     
   }
 
-  protected AbstractField(String name, Field.Store store, Field.Index index, Field.TermVector termVector) {
+  protected AbstractField(String name, Field.Store store, Field.Index index, Field.TermVector termVector, Field.TermDocs termDocs) {
     if (name == null)
       throw new NullPointerException("name cannot be null");
     this.name = name.intern();        // field names are interned
@@ -78,7 +79,15 @@
     } else {
       throw new IllegalArgumentException("unknown index parameter " + index);
     }
-
+    
+    if (termDocs == Field.TermDocs.STORE) {
+    	isStoreTermDocs = true;
+    } else if (termDocs == Field.TermDocs.NO) {
+    	isStoreTermDocs = false;
+    } else {
+      throw new IllegalArgumentException("unknown index parameter " + index);
+    }
+    
     this.isBinary = false;
 
     setStoreTermVector(termVector);
@@ -159,7 +168,9 @@
     with search hits.  It is an error for this to be true if a field is
     Reader-valued. */
   public final boolean  isStored()  { return isStored; }
-
+  
+  public final boolean isStoreTermDocs() { return isStoreTermDocs; }
+  
   /** True iff the value of the field is to be indexed, so that it may be
     searched on. */
   public final boolean  isIndexed()   { return isIndexed; }
Index: org/apache/lucene/document/Field.java
===================================================================
--- org/apache/lucene/document/Field.java	(revision 653899)
+++ org/apache/lucene/document/Field.java	(working copy)
@@ -56,14 +56,31 @@
     /** Do not store the field value in the index. */
     public static final Store NO = new Store("NO");
   }
-
+  
   /** Specifies whether and how a field should be indexed. */
+  public static final class TermDocs extends Parameter implements Serializable {
+    private TermDocs(String name) {
+      super(name);
+    }
+    
+    /**
+     * Store docs with the term in the term dictionary
+     */
+    public static final TermDocs STORE = new TermDocs("STORE");
+    
+    /**
+     * Do not store docs with the term in the term dictionary
+     */
+    public static final TermDocs NO = new TermDocs("NO");
+  }
+  
+  /** Specifies whether and how a field should be indexed. */
   public static final class Index extends Parameter implements Serializable {
 
     private Index(String name) {
       super(name);
     }
-
+    
     /** Do not index the field value. This field can thus not be searched,
      * but one can still access its contents provided it is
      * {@link Field.Store stored}. */
@@ -202,9 +219,17 @@
    * @throws IllegalArgumentException if the field is neither stored nor indexed 
    */
   public Field(String name, String value, Store store, Index index) {
-    this(name, value, store, index, TermVector.NO);
+    this(name, value, store, index, TermVector.NO, TermDocs.NO);
   }
   
+  public Field(String name, String value, Store store, Index index, TermDocs termDocs) {
+    this(name, value, store, index, TermVector.NO, termDocs);
+  }
+  
+  public Field(String name, String value, Store store, Index index, TermVector termVector) {
+  	this(name, value, store, index, termVector, TermDocs.NO);
+  }
+  
   /**
    * Create a field by specifying its name, value and how it will
    * be saved in the index.
@@ -222,7 +247,7 @@
    *  <li>the field is not indexed but termVector is <code>TermVector.YES</code></li>
    * </ul> 
    */ 
-  public Field(String name, String value, Store store, Index index, TermVector termVector) {
+  public Field(String name, String value, Store store, Index index, TermVector termVector, TermDocs termDocs) {
     if (name == null)
       throw new NullPointerException("name cannot be null");
     if (value == null)
@@ -271,6 +296,14 @@
       throw new IllegalArgumentException("unknown index parameter " + index);
     }
     
+    if (termDocs == TermDocs.NO) {
+    	this.isStoreTermDocs = false;
+    } else if (termDocs == TermDocs.STORE) {
+    	this.isStoreTermDocs = true;
+    } else {
+      throw new IllegalArgumentException("unknown termdocs parameter " + termDocs);
+    }
+    
     this.isBinary = false;
 
     setStoreTermVector(termVector);
Index: org/apache/lucene/document/Fieldable.java
===================================================================
--- org/apache/lucene/document/Fieldable.java	(revision 653899)
+++ org/apache/lucene/document/Fieldable.java	(working copy)
@@ -108,7 +108,9 @@
    * @see org.apache.lucene.index.IndexReader#getTermFreqVector(int, String)
    */
   boolean isTermVectorStored();
-
+  
+  boolean isStoreTermDocs();
+  
   /**
    * True iff terms are stored as term vector together with their offsets 
    * (start and end positon in source text).
Index: org/apache/lucene/index/DocumentsWriter.java
===================================================================
--- org/apache/lucene/index/DocumentsWriter.java	(revision 653899)
+++ org/apache/lucene/index/DocumentsWriter.java	(working copy)
@@ -17,30 +17,32 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.document.Document;
-import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Similarity;
 import org.apache.lucene.search.Weight;
+import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexInput;
 import org.apache.lucene.store.IndexOutput;
-import org.apache.lucene.store.IndexInput;
-import org.apache.lucene.store.AlreadyClosedException;
+import org.apache.lucene.util.IntArrayList;
+import org.apache.lucene.util.SortedVIntList;
 import org.apache.lucene.util.UnicodeUtil;
 
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.Map.Entry;
-import java.text.NumberFormat;
-import java.util.Collections;
-
 /**
  * This class accepts multiple added documents and directly
  * writes a single segment file.  It does this more
@@ -181,7 +183,10 @@
   private int maxBufferedDocs = IndexWriter.DEFAULT_MAX_BUFFERED_DOCS;
 
   private int flushedDocCount;                      // How many docs already flushed to index
-
+  
+  //Used for append posting term docs
+	private IntArrayList termDocsList = new IntArrayList(50, 1.25);
+  
   synchronized void updateFlushedDocCount(int n) {
     flushedDocCount += n;
   }
@@ -832,17 +837,19 @@
 
       final char[] text = termStates[0].text;
       final int start = termStates[0].textOffset;
-
+      boolean doTermDocs = termStates[0].field.doTermDocs;
+      
       long freqPointer = freqOut.getFilePointer();
       long proxPointer = proxOut.getFilePointer();
 
       skipListWriter.resetSkip();
-
+      
+      termDocsList.clear();
+      
       // Now termStates has numToMerge FieldMergeStates
       // which all share the same term.  Now we must
       // interleave the docID streams.
       while(numToMerge > 0) {
-        
         if ((++df % skipInterval) == 0) {
           skipListWriter.setSkipData(lastDoc, currentFieldStorePayloads, lastPayloadLength);
           skipListWriter.bufferSkip(df);
@@ -855,13 +862,15 @@
 
         final int doc = minState.docID;
         final int termDocFreq = minState.termFreq;
-
+        
         assert doc < numDocsInRAM;
         assert doc > lastDoc || df == 1;
 
         final int newDocCode = (doc-lastDoc)<<1;
         lastDoc = doc;
-
+        
+        if (doTermDocs) termDocsList.add(doc);
+        
         final ByteSliceReader prox = minState.prox;
 
         // Carefully copy over the prox + payload info,
@@ -927,9 +936,17 @@
       // Done merging this term
 
       long skipPointer = skipListWriter.writeSkip(freqOut);
-
+      
+      // create the term docs array if needed
+			int[] docs = null;
+			SortedVIntList vintList = null;
+			if (doTermDocs) {
+				docs = termDocsList.toIntArray();
+				Arrays.sort(docs);
+				vintList = new SortedVIntList(docs);
+			}
       // Write term
-      termInfo.set(df, freqPointer, proxPointer, (int) (skipPointer - freqPointer));
+      termInfo.set(df, freqPointer, proxPointer, (int) (skipPointer - freqPointer), vintList);
 
       // TODO: we could do this incrementally
       UnicodeUtil.UTF16toUTF8(text, start, termsUTF8);
Index: org/apache/lucene/index/DocumentsWriterFieldData.java
===================================================================
--- org/apache/lucene/index/DocumentsWriterFieldData.java	(revision 653899)
+++ org/apache/lucene/index/DocumentsWriterFieldData.java	(working copy)
@@ -50,6 +50,7 @@
   boolean doVectorPositions;
   boolean doVectorOffsets;
   boolean postingsCompacted;
+  boolean doTermDocs;
 
   int numPostings;
       
Index: org/apache/lucene/index/DocumentsWriterThreadState.java
===================================================================
--- org/apache/lucene/index/DocumentsWriterThreadState.java	(revision 653899)
+++ org/apache/lucene/index/DocumentsWriterThreadState.java	(working copy)
@@ -206,7 +206,7 @@
 
       FieldInfo fi = docWriter.fieldInfos.add(field.name(), field.isIndexed(), field.isTermVectorStored(),
                                               field.isStorePositionWithTermVector(), field.isStoreOffsetWithTermVector(),
-                                              field.getOmitNorms(), false);
+                                              field.getOmitNorms(), false, field.isStoreTermDocs());
       if (fi.isIndexed && !fi.omitNorms) {
         // Maybe grow our buffered norms
         if (docWriter.norms.length <= fi.number) {
@@ -270,6 +270,7 @@
         fp.fieldCount = 0;
         fp.doVectors = fp.doVectorPositions = fp.doVectorOffsets = false;
         fp.doNorms = fi.isIndexed && !fi.omitNorms;
+        fp.doTermDocs = fi.storeTermDoc;
 
         if (numFieldData == fieldDataArray.length) {
           int newSize = fieldDataArray.length*2;
Index: org/apache/lucene/index/FieldInfo.java
===================================================================
--- org/apache/lucene/index/FieldInfo.java	(revision 653899)
+++ org/apache/lucene/index/FieldInfo.java	(working copy)
@@ -26,6 +26,7 @@
   boolean storeTermVector;
   boolean storeOffsetWithTermVector;
   boolean storePositionWithTermVector;
+  boolean storeTermDoc;
 
   boolean omitNorms; // omit norms associated with indexed fields
   
@@ -33,7 +34,7 @@
 
   FieldInfo(String na, boolean tk, int nu, boolean storeTermVector, 
             boolean storePositionWithTermVector,  boolean storeOffsetWithTermVector, 
-            boolean omitNorms, boolean storePayloads) {
+            boolean omitNorms, boolean storePayloads, boolean storeTermDoc) {
     name = na;
     isIndexed = tk;
     number = nu;
@@ -42,10 +43,11 @@
     this.storePositionWithTermVector = storePositionWithTermVector;
     this.omitNorms = omitNorms;
     this.storePayloads = storePayloads;
+    this.storeTermDoc = storeTermDoc;
   }
 
   public Object clone() {
     return new FieldInfo(name, isIndexed, number, storeTermVector, storePositionWithTermVector,
-                         storeOffsetWithTermVector, omitNorms, storePayloads);
+                         storeOffsetWithTermVector, omitNorms, storePayloads, storeTermDoc);
   }
 }
Index: org/apache/lucene/index/FieldInfos.java
===================================================================
--- org/apache/lucene/index/FieldInfos.java	(revision 653899)
+++ org/apache/lucene/index/FieldInfos.java	(working copy)
@@ -40,6 +40,7 @@
   static final byte STORE_OFFSET_WITH_TERMVECTOR = 0x8;
   static final byte OMIT_NORMS = 0x10;
   static final byte STORE_PAYLOADS = 0x20;
+  static final byte STORE_TERM_DOCS = 0x40; 
   
   private ArrayList byNumber = new ArrayList();
   private HashMap byName = new HashMap();
@@ -83,7 +84,7 @@
     while (fieldIterator.hasNext()) {
       Fieldable field = (Fieldable) fieldIterator.next();
       add(field.name(), field.isIndexed(), field.isTermVectorStored(), field.isStorePositionWithTermVector(),
-              field.isStoreOffsetWithTermVector(), field.getOmitNorms());
+              field.isStoreOffsetWithTermVector(), field.getOmitNorms(), field.isStoreTermDocs());
     }
   }
   
@@ -96,10 +97,10 @@
    * @param storeOffsetWithTermVector true if offsets should be stored
    */
   public void addIndexed(Collection names, boolean storeTermVectors, boolean storePositionWithTermVector, 
-                         boolean storeOffsetWithTermVector) {
+                         boolean storeOffsetWithTermVector, boolean storeTermDocs) {
     Iterator i = names.iterator();
     while (i.hasNext()) {
-      add((String)i.next(), true, storeTermVectors, storePositionWithTermVector, storeOffsetWithTermVector);
+      add((String)i.next(), true, storeTermVectors, storePositionWithTermVector, storeOffsetWithTermVector, storeTermDocs);
     }
   }
 
@@ -126,7 +127,7 @@
    * @see #add(String, boolean, boolean, boolean, boolean)
    */
   public void add(String name, boolean isIndexed) {
-    add(name, isIndexed, false, false, false, false);
+    add(name, isIndexed, false, false, false, false, false);
   }
 
   /**
@@ -137,7 +138,7 @@
    * @param storeTermVector true if the term vector should be stored
    */
   public void add(String name, boolean isIndexed, boolean storeTermVector){
-    add(name, isIndexed, storeTermVector, false, false, false);
+    add(name, isIndexed, storeTermVector, false, false, false, false);
   }
   
   /** If the field is not yet known, adds it. If it is known, checks to make
@@ -152,9 +153,9 @@
    * @param storeOffsetWithTermVector true if the term vector with offsets should be stored
    */
   public void add(String name, boolean isIndexed, boolean storeTermVector,
-                  boolean storePositionWithTermVector, boolean storeOffsetWithTermVector) {
+                  boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, boolean storeTermDocs) {
 
-    add(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, false);
+    add(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, false, storeTermDocs);
   }
 
     /** If the field is not yet known, adds it. If it is known, checks to make
@@ -170,9 +171,9 @@
    * @param omitNorms true if the norms for the indexed field should be omitted
    */
   public void add(String name, boolean isIndexed, boolean storeTermVector,
-                  boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, boolean omitNorms) {
+                  boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, boolean omitNorms, boolean storeTermDocs) {
     add(name, isIndexed, storeTermVector, storePositionWithTermVector,
-        storeOffsetWithTermVector, omitNorms, false);
+        storeOffsetWithTermVector, omitNorms, false, storeTermDocs);
   }
   
   /** If the field is not yet known, adds it. If it is known, checks to make
@@ -190,10 +191,10 @@
    */
   public FieldInfo add(String name, boolean isIndexed, boolean storeTermVector,
                        boolean storePositionWithTermVector, boolean storeOffsetWithTermVector,
-                       boolean omitNorms, boolean storePayloads) {
+                       boolean omitNorms, boolean storePayloads, boolean storeTermDocs) {
     FieldInfo fi = fieldInfo(name);
     if (fi == null) {
-      return addInternal(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads);
+      return addInternal(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, storeTermDocs);
     } else {
       if (fi.isIndexed != isIndexed) {
         fi.isIndexed = true;                      // once indexed, always index
@@ -213,17 +214,19 @@
       if (fi.storePayloads != storePayloads) {
         fi.storePayloads = true;
       }
-
+      if (fi.storeTermDoc != storeTermDocs) {
+      	fi.storeTermDoc = true;
+      }
     }
     return fi;
   }
 
   private FieldInfo addInternal(String name, boolean isIndexed,
                                 boolean storeTermVector, boolean storePositionWithTermVector, 
-                                boolean storeOffsetWithTermVector, boolean omitNorms, boolean storePayloads) {
+                                boolean storeOffsetWithTermVector, boolean omitNorms, boolean storePayloads, boolean storeTermDocs) {
     FieldInfo fi =
       new FieldInfo(name, isIndexed, byNumber.size(), storeTermVector, storePositionWithTermVector,
-              storeOffsetWithTermVector, omitNorms, storePayloads);
+              storeOffsetWithTermVector, omitNorms, storePayloads, storeTermDocs);
     byNumber.add(fi);
     byName.put(name, fi);
     return fi;
@@ -295,6 +298,7 @@
       if (fi.storeOffsetWithTermVector) bits |= STORE_OFFSET_WITH_TERMVECTOR;
       if (fi.omitNorms) bits |= OMIT_NORMS;
       if (fi.storePayloads) bits |= STORE_PAYLOADS;
+      if (fi.storeTermDoc) bits |= STORE_TERM_DOCS;
       output.writeString(fi.name);
       output.writeByte(bits);
     }
@@ -311,8 +315,8 @@
       boolean storeOffsetWithTermVector = (bits & STORE_OFFSET_WITH_TERMVECTOR) != 0;
       boolean omitNorms = (bits & OMIT_NORMS) != 0;
       boolean storePayloads = (bits & STORE_PAYLOADS) != 0;
-      
-      addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads);
+      boolean storeTermDocs = (bits & STORE_TERM_DOCS) != 0;
+      addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, storeTermDocs);
     }    
   }
 
Index: org/apache/lucene/index/FieldsReader.java
===================================================================
--- org/apache/lucene/index/FieldsReader.java	(revision 653899)
+++ org/apache/lucene/index/FieldsReader.java	(working copy)
@@ -280,6 +280,7 @@
       Field.Store store = Field.Store.YES;
       Field.Index index = getIndexType(fi, tokenize);
       Field.TermVector termVector = getTermVectorType(fi);
+      Field.TermDocs termDocs = getTermDocsType(fi);
 
       Fieldable f;
       if (compressed) {
@@ -298,7 +299,7 @@
           fieldsStream.seek(pointer+length);
         else
           fieldsStream.skipChars(length);
-        f = new LazyField(fi.name, store, index, termVector, length, pointer, binary);
+        f = new LazyField(fi.name, store, index, termVector, termDocs, length, pointer, binary);
         f.setOmitNorms(fi.omitNorms);
       }
       doc.add(f);
@@ -397,7 +398,15 @@
     }
     return termVector;
   }
-
+  
+  private Field.TermDocs getTermDocsType(FieldInfo fi) {
+  	if (fi.storeTermDoc) {
+  		return Field.TermDocs.STORE;
+  	} else {
+  		return Field.TermDocs.NO;
+  	}
+  }
+  
   private Field.Index getIndexType(FieldInfo fi, boolean tokenize) {
     Field.Index index;
     if (fi.isIndexed && tokenize)
@@ -418,15 +427,15 @@
     private long pointer;
 
     public LazyField(String name, Field.Store store, int toRead, long pointer, boolean isBinary) {
-      super(name, store, Field.Index.NO, Field.TermVector.NO);
+      super(name, store, Field.Index.NO, Field.TermVector.NO, Field.TermDocs.NO);
       this.toRead = toRead;
       this.pointer = pointer;
       this.isBinary = isBinary;
       lazy = true;
     }
 
-    public LazyField(String name, Field.Store store, Field.Index index, Field.TermVector termVector, int toRead, long pointer, boolean isBinary) {
-      super(name, store, index, termVector);
+    public LazyField(String name, Field.Store store, Field.Index index, Field.TermVector termVector, Field.TermDocs termDocs, int toRead, long pointer, boolean isBinary) {
+      super(name, store, index, termVector, termDocs);
       this.toRead = toRead;
       this.pointer = pointer;
       this.isBinary = isBinary;
Index: org/apache/lucene/index/FilterIndexReader.java
===================================================================
--- org/apache/lucene/index/FilterIndexReader.java	(revision 653899)
+++ org/apache/lucene/index/FilterIndexReader.java	(working copy)
@@ -17,13 +17,14 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.Collection;
+
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.store.Directory;
 
-import java.io.IOException;
-import java.util.Collection;
-
 /**  A <code>FilterIndexReader</code> contains another IndexReader, which it
  * uses as its basic source of data, possibly transforming the data along the
  * way or providing additional functionality. The class
@@ -87,6 +88,7 @@
     public boolean next() throws IOException { return in.next(); }
     public Term term() { return in.term(); }
     public int docFreq() { return in.docFreq(); }
+    public DocIdSetIterator docs() throws IOException { return in.docs(); }
     public void close() throws IOException { in.close(); }
   }
 
@@ -177,7 +179,17 @@
   protected void doSetNorm(int d, String f, byte b) throws CorruptIndexException, IOException {
     in.setNorm(d, f, b);
   }
+  
+  public TermEnum terms(boolean loadDocs) throws IOException {
+    ensureOpen();
+    return in.terms(loadDocs);
+  }
 
+  public TermEnum terms(Term t, boolean loadDocs) throws IOException {
+    ensureOpen();
+    return in.terms(t, loadDocs);
+  }
+  
   public TermEnum terms() throws IOException {
     ensureOpen();
     return in.terms();
Index: org/apache/lucene/index/IndexReader.java
===================================================================
--- org/apache/lucene/index/IndexReader.java	(revision 653899)
+++ org/apache/lucene/index/IndexReader.java	(working copy)
@@ -89,6 +89,8 @@
     public static final FieldOption TERMVECTOR_WITH_OFFSET = new FieldOption ("TERMVECTOR_WITH_OFFSET");
     /** All fields with termvectors with offset values and position values enabled */
     public static final FieldOption TERMVECTOR_WITH_POSITION_OFFSET = new FieldOption ("TERMVECTOR_WITH_POSITION_OFFSET");
+    
+    public static final FieldOption TERM_DOCS = new FieldOption ("TERMVECTOR_DOCS");
   }
 
   private boolean closed;
@@ -645,6 +647,10 @@
    * @throws IOException if there is a low-level IO error
    */
   public abstract TermEnum terms(Term t) throws IOException;
+  
+  public abstract TermEnum terms(Term t, boolean loadDocs) throws IOException;
+  
+  public abstract TermEnum terms(boolean loadDocs) throws IOException;
 
   /** Returns the number of documents containing the term <code>t</code>.
    * @throws IOException if there is a low-level IO error
Index: org/apache/lucene/index/IndexWriter.java
===================================================================
--- org/apache/lucene/index/IndexWriter.java	(revision 653899)
+++ org/apache/lucene/index/IndexWriter.java	(working copy)
@@ -306,9 +306,6 @@
   private SegmentInfos rollbackSegmentInfos;      // segmentInfos we will fallback to if the commit fails
   private HashMap rollbackSegments;
 
-  volatile SegmentInfos pendingCommit;            // set when a commit is pending (after prepareCommit() & before commit())
-  volatile long pendingCommitChangeCount;
-
   private SegmentInfos localRollbackSegmentInfos;      // segmentInfos we will fallback to if the commit fails
   private boolean localAutoCommit;                // saved autoCommit during local transaction
   private int localFlushedDocCount;               // saved docWriter.getFlushedDocCount during local transaction
@@ -367,13 +364,12 @@
       infoStream.println("IW " + messageID + " [" + Thread.currentThread().getName() + "]: " + message);
   }
 
-  private synchronized void setMessageID(PrintStream infoStream) {
+  private synchronized void setMessageID() {
     if (infoStream != null && messageID == -1) {
       synchronized(MESSAGE_ID_LOCK) {
         messageID = MESSAGE_ID++;
       }
     }
-    this.infoStream = infoStream;
   }
 
   /**
@@ -1086,8 +1082,9 @@
     this.closeDir = closeDir;
     directory = d;
     analyzer = a;
-    setMessageID(defaultInfoStream);
+    this.infoStream = defaultInfoStream;
     this.maxFieldLength = maxFieldLength;
+    setMessageID();
 
     if (create) {
       // Clear the write lock in case it's leftover:
@@ -1499,7 +1496,8 @@
    */
   public void setInfoStream(PrintStream infoStream) {
     ensureOpen();
-    setMessageID(infoStream);
+    this.infoStream = infoStream;
+    setMessageID();
     docWriter.setInfoStream(infoStream);
     deleter.setInfoStream(infoStream);
     if (infoStream != null)
@@ -1674,7 +1672,7 @@
       if (infoStream != null)
         message("now call final commit()");
 
-      commit(0);
+      commit(true, 0);
 
       if (infoStream != null)
         message("at close: " + segString());
@@ -2573,7 +2571,7 @@
     if (autoCommit) {
       boolean success = false;
       try {
-        commit(0);
+        commit(true, 0);
         success = true;
       } finally {
         if (!success) {
@@ -2590,40 +2588,24 @@
   }
 
   /**
-   * @deprecated Please use {@link #rollback} instead.
-   */
-  public void abort() throws IOException {
-    rollback();
-  }
-
-  /**
    * Close the <code>IndexWriter</code> without committing
    * any of the changes that have occurred since it was
    * opened. This removes any temporary files that had been
    * created, after which the state of the index will be the
    * same as it was when this writer was first opened.  This
    * can only be called when this IndexWriter was opened
-   * with <code>autoCommit=false</code>.  This also clears a
-   * previous call to {@link #prepareCommit}.
+   * with <code>autoCommit=false</code>.
    * @throws IllegalStateException if this is called when
    *  the writer was opened with <code>autoCommit=true</code>.
    * @throws IOException if there is a low-level IO error
    */
-  public void rollback() throws IOException {
+  public void abort() throws IOException {
     ensureOpen();
     if (autoCommit)
       throw new IllegalStateException("abort() can only be called when IndexWriter was opened with autoCommit=false");
 
     boolean doClose;
     synchronized(this) {
-
-      if (pendingCommit != null) {
-        pendingCommit.rollbackCommit(directory);
-        deleter.decRef(pendingCommit);
-        pendingCommit = null;
-        notifyAll();
-      }
-
       // Ensure that only one thread actually gets to do the closing:
       if (!closing) {
         doClose = true;
@@ -3131,54 +3113,10 @@
     flush(true, false, true);
   }
 
-  /** <p>Expert: prepare for commit.  This does the first
-   *  phase of 2-phase commit.  You can only call this when
-   *  autoCommit is false.  This method does all steps
-   *  necessary to commit changes since this writer was
-   *  opened: flushes pending added and deleted docs, syncs
-   *  the index files, writes most of next segments_N file.
-   *  After calling this you must call either {@link
-   *  #commit()} to finish the commit, or {@link
-   *  #rollback()} to revert the commit and undo all changes
-   *  done since the writer was opened.</p>
-   *
-   * You can also just call {@link #commit()} directly
-   * without prepareCommit first in which case that method
-   * will internally call prepareCommit.
-   */
-  public final void prepareCommit() throws CorruptIndexException, IOException {
-    prepareCommit(false);
-  }
-
-  private final void prepareCommit(boolean internal) throws CorruptIndexException, IOException {
-
-    if (hitOOM)
-      throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot commit");
-
-    if (autoCommit && !internal)
-      throw new IllegalStateException("this method can only be used when autoCommit is false");
-
-    if (!autoCommit && pendingCommit != null)
-      throw new IllegalStateException("prepareCommit was already called with no corresponding call to commit");
-
-    message("prepareCommit: flush");
-
-    flush(true, true, true);
-
-    startCommit(0);
-  }
-
-  private void commit(long sizeInBytes) throws IOException {
-    startCommit(sizeInBytes);
-    finishCommit();
-  }
-
   /**
-   * <p>Commits all pending updates (added & deleted
-   * documents) to the index, and syncs all referenced index
-   * files, such that a reader will see the changes and the
-   * index updates will survive an OS or machine crash or
-   * power loss (though, see the note below).  Note that
+   * <p>Commits all pending updates (added & deleted documents)
+   * to the index, and syncs all referenced index files,
+   * such that a reader will see the changes.  Note that
    * this does not wait for any running background merges to
    * finish.  This may be a costly operation, so you should
    * test the cost in your application and do it only when
@@ -3197,38 +3135,12 @@
    * consistency on such devices.  </p>
    */
   public final void commit() throws CorruptIndexException, IOException {
-
-    message("commit: start");
-
-    if (autoCommit || pendingCommit == null) {
-      message("commit: now prepare");
-      prepareCommit(true);
-    } else
-      message("commit: already prepared");
-
-    finishCommit();
+    commit(true);
   }
 
-  private synchronized final void finishCommit() throws CorruptIndexException, IOException {
-
-    if (pendingCommit != null) {
-      try {
-        message("commit: pendingCommit != null");
-        pendingCommit.finishCommit(directory);
-        lastCommitChangeCount = pendingCommitChangeCount;
-        segmentInfos.updateGeneration(pendingCommit);
-        setRollbackSegmentInfos();
-        deleter.checkpoint(pendingCommit, true);
-      } finally {
-        deleter.decRef(pendingCommit);
-        pendingCommit = null;
-        notifyAll();
-      }
-
-    } else
-      message("commit: pendingCommit == null; skip");
-
-    message("commit: done");
+  private final void commit(boolean triggerMerges) throws CorruptIndexException, IOException {
+    flush(triggerMerges, true, true);
+    commit(true, 0);
   }
 
   /**
@@ -3264,7 +3176,8 @@
     // when flushing a segment; otherwise deletes may become
     // visible before their corresponding added document
     // from an updateDocument call
-    flushDeletes |= autoCommit;
+    if (autoCommit)
+      flushDeletes = true;
 
     // Returns true if docWriter is currently aborting, in
     // which case we skip flushing this segment
@@ -4022,7 +3935,7 @@
         synchronized(this) {
           size = merge.info.sizeInBytes();
         }
-        commit(size);
+        commit(false, size);
       }
       
       success = false;
@@ -4075,7 +3988,7 @@
       synchronized(this) {
         size = merge.info.sizeInBytes();
       }
-      commit(size);
+      commit(false, size);
     }
 
     return mergedDocCount;
@@ -4238,13 +4151,13 @@
   }
 
   /** Walk through all files referenced by the current
-   *  segmentInfos and ask the Directory to sync each file,
-   *  if it wasn't already.  If that succeeds, then we
-   *  prepare a new segments_N file but do not fully commit
-   *  it. */
-  private void startCommit(long sizeInBytes) throws IOException {
+   *  segmentInfos, minus flushes, and ask the Directory to
+   *  sync each file, if it wasn't already.  If that
+   *  succeeds, then we write a new segments_N file & sync
+   *  that. */
+  private void commit(boolean skipWait, long sizeInBytes) throws IOException {
 
-    assert testPoint("startStartCommit");
+    assert testPoint("startCommit");
 
     if (hitOOM)
       return;
@@ -4252,9 +4165,9 @@
     try {
 
       if (infoStream != null)
-        message("startCommit(): start sizeInBytes=" + sizeInBytes);
+        message("start commit() skipWait=" + skipWait + " sizeInBytes=" + sizeInBytes);
 
-      if (sizeInBytes > 0)
+      if (!skipWait)
         syncPause(sizeInBytes);
 
       SegmentInfos toSync = null;
@@ -4266,7 +4179,7 @@
 
         if (changeCount == lastCommitChangeCount) {
           if (infoStream != null)
-            message("  skip startCommit(): no changes pending");
+            message("  skip commit(): no changes pending");
           return;
         }
 
@@ -4276,17 +4189,15 @@
         // threads can be doing this at once, if say a large
         // merge and a small merge finish at the same time:
 
-        if (infoStream != null)
-          message("startCommit index=" + segString(segmentInfos) + " changeCount=" + changeCount);
-
         toSync = (SegmentInfos) segmentInfos.clone();
         deleter.incRef(toSync, false);
         myChangeCount = changeCount;
       }
 
-      assert testPoint("midStartCommit");
+      if (infoStream != null)
+        message("commit index=" + segString(toSync));
 
-      boolean setPending = false;
+      assert testPoint("midCommit");
 
       try {
 
@@ -4326,72 +4237,54 @@
             break;
         }
 
-        assert testPoint("midStartCommit2");
-
+        assert testPoint("midCommit2");
+      
         synchronized(this) {
           // If someone saved a newer version of segments file
           // since I first started syncing my version, I can
           // safely skip saving myself since I've been
           // superseded:
-          if (myChangeCount > lastCommitChangeCount && (pendingCommit == null || myChangeCount > pendingCommitChangeCount)) {
-
-            // Wait now for any current pending commit to complete:
-            while(pendingCommit != null) {
-              message("wait for existing pendingCommit to finish...");
-              try {
-                wait();
-              } catch (InterruptedException ie) {
-                Thread.currentThread().interrupt();
-              }
-            }
-
+          if (myChangeCount > lastCommitChangeCount) {
+          
             if (segmentInfos.getGeneration() > toSync.getGeneration())
               toSync.updateGeneration(segmentInfos);
 
             boolean success = false;
             try {
-
-              // Exception here means nothing is prepared
-              // (this method unwinds everything it did on
-              // an exception)
-              try {
-                toSync.prepareCommit(directory);
-              } finally {
-                // Have our master segmentInfos record the
-                // generations we just prepared.  We do this
-                // on error or success so we don't
-                // double-write a segments_N file.
-                segmentInfos.updateGeneration(toSync);
-              }
-
-              assert pendingCommit == null;
-              setPending = true;
-              pendingCommit = toSync;
-              pendingCommitChangeCount = myChangeCount;
+              toSync.commit(directory);
               success = true;
             } finally {
+              // Have our master segmentInfos record the
+              // generations we just sync'd
+              segmentInfos.updateGeneration(toSync);
               if (!success)
                 message("hit exception committing segments file");
             }
+
+            message("commit complete");
+
+            lastCommitChangeCount = myChangeCount;
+
+            deleter.checkpoint(toSync, true);
+            setRollbackSegmentInfos();
           } else
             message("sync superseded by newer infos");
         }
 
         message("done all syncs");
 
-        assert testPoint("midStartCommitSuccess");
+        assert testPoint("midCommitSuccess");
 
       } finally {
         synchronized(this) {
-          if (!setPending)
-            deleter.decRef(toSync);
+          deleter.decRef(toSync);
         }
       }
     } catch (OutOfMemoryError oom) {
       hitOOM = true;
       throw oom;
     }
-    assert testPoint("finishStartCommit");
+    assert testPoint("finishCommit");
   }
 
   /**
@@ -4484,11 +4377,11 @@
   // Used only by assert for testing.  Current points:
   //   startDoFlush
   //   startCommitMerge
-  //   startStartCommit
-  //   midStartCommit
-  //   midStartCommit2
-  //   midStartCommitSuccess
-  //   finishStartCommit
+  //   startCommit
+  //   midCommit
+  //   midCommit2
+  //   midCommitSuccess
+  //   finishCommit
   //   startCommitMergeDeletes
   //   startMergeInit
   //   startApplyDeletes
Index: org/apache/lucene/index/MultiReader.java
===================================================================
--- org/apache/lucene/index/MultiReader.java	(revision 653899)
+++ org/apache/lucene/index/MultiReader.java	(working copy)
@@ -295,14 +295,24 @@
 
   public TermEnum terms() throws IOException {
     ensureOpen();
-    return new MultiTermEnum(subReaders, starts, null);
+    return new MultiTermEnum(subReaders, starts, null, false);
   }
 
   public TermEnum terms(Term term) throws IOException {
     ensureOpen();
-    return new MultiTermEnum(subReaders, starts, term);
+    return new MultiTermEnum(subReaders, starts, term, false);
   }
+  
+  public TermEnum terms(boolean loadDocs) throws IOException {
+    ensureOpen();
+    return new MultiTermEnum(subReaders, starts, null, loadDocs);
+  }
 
+  public TermEnum terms(Term term, boolean loadDocs) throws IOException {
+    ensureOpen();
+    return new MultiTermEnum(subReaders, starts, term, loadDocs);
+  }
+
   public int docFreq(Term t) throws IOException {
     ensureOpen();
     int total = 0;          // sum freqs in segments
Index: org/apache/lucene/index/MultiSegmentReader.java
===================================================================
--- org/apache/lucene/index/MultiSegmentReader.java	(revision 653899)
+++ org/apache/lucene/index/MultiSegmentReader.java	(working copy)
@@ -17,20 +17,24 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.store.Directory;
-
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-/** 
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.store.Directory;
+
+/**
  * An IndexReader which reads indexes with multiple segments.
  */
 class MultiSegmentReader extends DirectoryIndexReader {
@@ -358,14 +362,24 @@
 
   public TermEnum terms() throws IOException {
     ensureOpen();
-    return new MultiTermEnum(subReaders, starts, null);
+    return new MultiTermEnum(subReaders, starts, null, false);
   }
 
+  public TermEnum terms(boolean loadDocs) throws IOException {
+    ensureOpen();
+    return new MultiTermEnum(subReaders, starts, null, loadDocs);
+  }
+
   public TermEnum terms(Term term) throws IOException {
     ensureOpen();
-    return new MultiTermEnum(subReaders, starts, term);
+    return new MultiTermEnum(subReaders, starts, term, false);
   }
 
+  public TermEnum terms(Term term, boolean loadDocs) throws IOException {
+    ensureOpen();
+    return new MultiTermEnum(subReaders, starts, term, loadDocs);
+  }
+
   public int docFreq(Term t) throws IOException {
     ensureOpen();
     int total = 0;          // sum freqs in segments
@@ -449,18 +463,22 @@
   
     private Term term;
     private int docFreq;
+    private List matchingDocs = new ArrayList(10);
+    private boolean loadDocs;
+    private MultiDocsIterator multiDocsIterator;
   
-    public MultiTermEnum(IndexReader[] readers, int[] starts, Term t)
+    public MultiTermEnum(IndexReader[] readers, int[] starts, Term t, boolean loadDocs)
       throws IOException {
+      this.loadDocs = loadDocs;
       queue = new SegmentMergeQueue(readers.length);
       for (int i = 0; i < readers.length; i++) {
         IndexReader reader = readers[i];
         TermEnum termEnum;
   
         if (t != null) {
-          termEnum = reader.terms(t);
+          termEnum = reader.terms(t, loadDocs);
         } else
-          termEnum = reader.terms();
+          termEnum = reader.terms(loadDocs);
   
         SegmentMergeInfo smi = new SegmentMergeInfo(starts[i], termEnum, reader);
         if (t == null ? smi.next() : termEnum.term() != null)
@@ -473,7 +491,16 @@
         next();
       }
     }
-  
+    
+    public final DocIdSetIterator docs() throws IOException {
+      if (multiDocsIterator == null) {
+        multiDocsIterator = new MultiDocsIterator(matchingDocs, this);
+      } else {
+        multiDocsIterator.set(matchingDocs);
+      }
+      return multiDocsIterator;
+    }
+    
     public boolean next() throws IOException {
       SegmentMergeInfo top = (SegmentMergeInfo)queue.top();
       if (top == null) {
@@ -483,10 +510,16 @@
   
       term = top.term;
       docFreq = 0;
-  
+      matchingDocs.clear();
       while (top != null && term.compareTo(top.term) == 0) {
         queue.pop();
         docFreq += top.termEnum.docFreq();    // increment freq
+        
+        DocIdSetIterator docsIterator = top.termEnum.docs();
+        if (docsIterator != null) {
+          matchingDocs.add(new DocIdSetHolder(top.base, docsIterator));
+        }
+        
         if (top.next())
           queue.put(top);          // restore queue
         else
@@ -508,7 +541,69 @@
       queue.close();
     }
   }
+  
+  private static class DocIdSetHolder implements Comparable {
+    private int base;
+    private DocIdSetIterator docsIterator;
+    
+    public DocIdSetHolder(int base, DocIdSetIterator docsIterator) {
+      this.base = base;
+      this.docsIterator = docsIterator;
+    }
+    
+    public int compareTo(Object object) {
+      DocIdSetHolder other = (DocIdSetHolder)object;
+      if (base < other.base) return 1;
+      if (base > other.base) return -1;
+      return 0;
+    }
+  }
+  
+  private static class MultiDocsIterator extends DocIdSetIterator {
+    private List iteratorList;
+    private int doc;
+    private MultiTermEnum multiTermEnum;
+    
+    public MultiDocsIterator(List iteratorList, MultiTermEnum multiTermEnum) throws IOException {
+      this.iteratorList = new ArrayList(iteratorList);
+      this.multiTermEnum = multiTermEnum;
+      // keeps docs in ascending order by sorting by segment base
+      Collections.sort(iteratorList);
+    }
+    
+    private void set(List iteratorList) {
+      this.iteratorList.clear();
+      for (int x=0; x < iteratorList.size(); x++) {
+        this.iteratorList.add(iteratorList.get(x));
+      }
+      // keeps docs in ascending order by sorting by segment base
+      Collections.sort(iteratorList);
+    }
+    
+    public int doc() {
+      return doc;
+    }
+    
+    public boolean next() throws IOException {
+      if (iteratorList.size() == 0) {
+        doc = -1;
+        return false;
+      }
+      DocIdSetHolder holder = (DocIdSetHolder)iteratorList.get(0);
+      DocIdSetIterator docsIterator = holder.docsIterator;
+      if (!docsIterator.next()) {
+        iteratorList.remove(0);
+        return next();
+      }
+      doc = docsIterator.doc() + holder.base;
+      return true;
+    }
 
+    public boolean skipTo(int target) throws IOException {
+      throw new UnsupportedOperationException("");
+    }
+  }
+  
   static class MultiTermDocs implements TermDocs {
     protected IndexReader[] readers;
     protected int[] starts;
Index: org/apache/lucene/index/ParallelReader.java
===================================================================
--- org/apache/lucene/index/ParallelReader.java	(revision 653899)
+++ org/apache/lucene/index/ParallelReader.java	(working copy)
@@ -17,15 +17,25 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.FieldSelector;
 import org.apache.lucene.document.FieldSelectorResult;
 import org.apache.lucene.document.Fieldable;
+import org.apache.lucene.search.DocIdSetIterator;
 
-import java.io.IOException;
-import java.util.*;
 
-
 /** An IndexReader which reads multiple, parallel indexes.  Each index added
  * must have the same number of documents, but typically each contains
  * different fields.  Each document contains the union of the fields of all
@@ -350,14 +360,24 @@
 
   public TermEnum terms() throws IOException {
     ensureOpen();
-    return new ParallelTermEnum();
+    return new ParallelTermEnum(false);
   }
 
   public TermEnum terms(Term term) throws IOException {
     ensureOpen();
-    return new ParallelTermEnum(term);
+    return new ParallelTermEnum(term, false);
   }
+  
+  public TermEnum terms(boolean loadDocs) throws IOException {
+    ensureOpen();
+    return new ParallelTermEnum(loadDocs);
+  }
 
+  public TermEnum terms(Term term, boolean loadDocs) throws IOException {
+    ensureOpen();
+    return new ParallelTermEnum(term, loadDocs);
+  }
+
   public int docFreq(Term term) throws IOException {
     ensureOpen();
     IndexReader reader = ((IndexReader)fieldToReader.get(term.field()));
@@ -455,18 +475,21 @@
     private String field;
     private Iterator fieldIterator;
     private TermEnum termEnum;
+    private boolean loadDocs;
 
-    public ParallelTermEnum() throws IOException {
+    public ParallelTermEnum(boolean loadDocs) throws IOException {
+    	this.loadDocs = loadDocs;
       field = (String)fieldToReader.firstKey();
       if (field != null)
-        termEnum = ((IndexReader)fieldToReader.get(field)).terms();
+        termEnum = ((IndexReader)fieldToReader.get(field)).terms(loadDocs);
     }
 
-    public ParallelTermEnum(Term term) throws IOException {
+    public ParallelTermEnum(Term term, boolean loadDocs) throws IOException {
+    	this.loadDocs = loadDocs;
       field = term.field();
       IndexReader reader = ((IndexReader)fieldToReader.get(field));
       if (reader!=null)
-        termEnum = reader.terms(term);
+        termEnum = reader.terms(term, loadDocs);
     }
 
     public boolean next() throws IOException {
@@ -510,7 +533,12 @@
 
       return termEnum.docFreq();
     }
-
+    
+    public DocIdSetIterator docs() throws IOException {
+    	if (termEnum == null) return null;
+    	return termEnum.docs();
+    }
+    
     public void close() throws IOException {
       if (termEnum!=null)
         termEnum.close();
Index: org/apache/lucene/index/SegmentMerger.java
===================================================================
--- org/apache/lucene/index/SegmentMerger.java	(revision 653899)
+++ org/apache/lucene/index/SegmentMerger.java	(working copy)
@@ -17,16 +17,19 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.Vector;
-import java.util.Iterator;
-import java.util.Collection;
-import java.io.IOException;
 
 import org.apache.lucene.document.FieldSelector;
 import org.apache.lucene.document.FieldSelectorResult;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexInput;
 import org.apache.lucene.store.IndexOutput;
-import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.util.IntArrayList;
+import org.apache.lucene.util.SortedVIntList;
 
 /**
  * The SegmentMerger class combines two or more Segments, represented by an IndexReader ({@link #add},
@@ -64,7 +67,9 @@
   /** Maximum number of contiguous documents to bulk-copy
       when merging stored fields */
   private final static int MAX_RAW_MERGE_DOCS = 4192;
-
+  
+  private IntArrayList termDocsList = new IntArrayList(50, 1.25);
+  
   /** This ctor used only by test code.
    * 
    * @param dir The Directory to merge the other segments into
@@ -197,11 +202,11 @@
   }
 
   private void addIndexed(IndexReader reader, FieldInfos fieldInfos, Collection names, boolean storeTermVectors, boolean storePositionWithTermVector,
-                         boolean storeOffsetWithTermVector, boolean storePayloads) throws IOException {
+                         boolean storeOffsetWithTermVector, boolean storePayloads, boolean storeTermDocs) throws IOException {
     Iterator i = names.iterator();
     while (i.hasNext()) {
       String field = (String)i.next();
-      fieldInfos.add(field, true, storeTermVectors, storePositionWithTermVector, storeOffsetWithTermVector, !reader.hasNorms(field), storePayloads);
+      fieldInfos.add(field, true, storeTermVectors, storePositionWithTermVector, storeOffsetWithTermVector, !reader.hasNorms(field), storePayloads, storeTermDocs);
     }
   }
 
@@ -264,15 +269,17 @@
         SegmentReader segmentReader = (SegmentReader) reader;
         for (int j = 0; j < segmentReader.getFieldInfos().size(); j++) {
           FieldInfo fi = segmentReader.getFieldInfos().fieldInfo(j);
-          fieldInfos.add(fi.name, fi.isIndexed, fi.storeTermVector, fi.storePositionWithTermVector, fi.storeOffsetWithTermVector, !reader.hasNorms(fi.name), fi.storePayloads);
+          fieldInfos.add(fi.name, fi.isIndexed, fi.storeTermVector, fi.storePositionWithTermVector, fi.storeOffsetWithTermVector, !reader.hasNorms(fi.name), fi.storePayloads, fi.storeTermDoc);
         }
       } else {
-        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR_WITH_POSITION_OFFSET), true, true, true, false);
-        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR_WITH_POSITION), true, true, false, false);
-        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR_WITH_OFFSET), true, false, true, false);
-        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR), true, false, false, false);
-        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.STORES_PAYLOADS), false, false, false, true);
-        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.INDEXED), false, false, false, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR_WITH_POSITION_OFFSET), true, true, true, false, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR_WITH_POSITION), true, true, false, false, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR_WITH_OFFSET), true, false, true, false, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERMVECTOR), true, false, false, false, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.STORES_PAYLOADS), false, false, false, true, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.INDEXED), false, false, false, false, false);
+        addIndexed(reader, fieldInfos, reader.getFieldNames(IndexReader.FieldOption.TERM_DOCS), false, false, false, false, true);
+        
         fieldInfos.add(reader.getFieldNames(IndexReader.FieldOption.UNINDEXED), false);
       }
     }
@@ -502,7 +509,7 @@
         top = (SegmentMergeInfo) queue.top();
       }
 
-      final int df = mergeTermInfo(match, matchSize);		  // add new TermInfo
+      final int df = mergeTermInfo(match, matchSize, term);		  // add new TermInfo
 
       if (checkAbort != null)
         checkAbort.work(df/3.0);
@@ -528,24 +535,43 @@
    * @throws CorruptIndexException if the index is corrupt
    * @throws IOException if there is a low-level IO error
    */
-  private final int mergeTermInfo(SegmentMergeInfo[] smis, int n)
+  private final int mergeTermInfo(SegmentMergeInfo[] smis, int n, Term term)
           throws CorruptIndexException, IOException {
     long freqPointer = freqOutput.getFilePointer();
     long proxPointer = proxOutput.getFilePointer();
+    
+    Holder docsHolder = new Holder(null);
+    // TODO: can optimize to not generate doc array if no need
+    int df = appendPostings(smis, n, docsHolder);		  // append posting data
 
-    int df = appendPostings(smis, n);		  // append posting data
-
     long skipPointer = skipListWriter.writeSkip(freqOutput);
 
     if (df > 0) {
+    	int[] docs = null;
+    	FieldInfo fieldInfo = fieldInfos.fieldInfo(term.field());
+    	SortedVIntList vintList = null;
+    	if (fieldInfo.storeTermDoc) {
+    	  docs = (int[])docsHolder.object;
+    	  Arrays.sort(docs);
+    	  vintList = new SortedVIntList(docs);
+    	} 
+    	
       // add an entry to the dictionary with pointers to prox and freq files
-      termInfo.set(df, freqPointer, proxPointer, (int) (skipPointer - freqPointer));
+      termInfo.set(df, freqPointer, proxPointer, (int) (skipPointer - freqPointer), vintList);
       termInfosWriter.add(smis[0].term, termInfo);
     }
 
     return df;
   }
   
+  public static class Holder {
+  	public Object object;
+  	
+  	public Holder(Object object) {
+  		this.object = object;
+  	}
+  }
+  
   private byte[] payloadBuffer;
   private int[][] docMaps;
   int[][] getDocMaps() {
@@ -566,13 +592,14 @@
    * @throws CorruptIndexException if the index is corrupt
    * @throws IOException if there is a low-level IO error
    */
-  private final int appendPostings(SegmentMergeInfo[] smis, int n)
+  private final int appendPostings(SegmentMergeInfo[] smis, int n, Holder docsHolder)
           throws CorruptIndexException, IOException {
     int lastDoc = 0;
     int df = 0;					  // number of docs w/ term
     skipListWriter.resetSkip();
     boolean storePayloads = fieldInfos.fieldInfo(smis[0].term.field).storePayloads;
     int lastPayloadLength = -1;   // ensures that we write the first length
+    termDocsList.clear();
     for (int i = 0; i < n; i++) {
       SegmentMergeInfo smi = smis[i];
       TermPositions postings = smi.getPositions();
@@ -589,7 +616,9 @@
         if (doc < 0 || (df > 0 && doc <= lastDoc))
           throw new CorruptIndexException("docs out of order (" + doc +
               " <= " + lastDoc + " )");
-
+        
+        termDocsList.add(doc);
+        
         df++;
 
         if ((df % skipInterval) == 0) {
@@ -638,6 +667,7 @@
         }
       }
     }
+    docsHolder.object = termDocsList.toIntArray();
     return df;
   }
 
Index: org/apache/lucene/index/SegmentReader.java
===================================================================
--- org/apache/lucene/index/SegmentReader.java	(revision 653899)
+++ org/apache/lucene/index/SegmentReader.java	(working copy)
@@ -653,7 +653,17 @@
     ensureOpen();
     return tis.terms(t);
   }
+  
+  public TermEnum terms(boolean loadDocs) {
+    ensureOpen();
+    return tis.terms(loadDocs);
+  }
 
+  public TermEnum terms(Term t, boolean loadDocs) throws IOException {
+    ensureOpen();
+    return tis.terms(t, loadDocs);
+  }
+
   FieldInfos getFieldInfos() {
     return fieldInfos;
   }
@@ -756,6 +766,8 @@
       else if ((fi.storeOffsetWithTermVector && fi.storePositionWithTermVector) &&
                 fieldOption == IndexReader.FieldOption.TERMVECTOR_WITH_POSITION_OFFSET) {
         fieldSet.add(fi.name);
+      } else if (fi.storeTermDoc) {
+        fieldSet.add(fi.name);
       }
     }
     return fieldSet;
Index: org/apache/lucene/index/SegmentTermEnum.java
===================================================================
--- org/apache/lucene/index/SegmentTermEnum.java	(revision 653899)
+++ org/apache/lucene/index/SegmentTermEnum.java	(working copy)
@@ -18,9 +18,14 @@
  */
 
 import java.io.IOException;
+
+import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.util.SortedVIntList;
+import org.apache.lucene.util.VIntUtil;
 
 final class SegmentTermEnum extends TermEnum implements Cloneable {
+  public static final int TERM_DOCS_BYTES_THRESHOLD = 50;
   private IndexInput input;
   FieldInfos fieldInfos;
   long size;
@@ -29,6 +34,7 @@
   private TermBuffer termBuffer = new TermBuffer();
   private TermBuffer prevBuffer = new TermBuffer();
   private TermBuffer scanBuffer = new TermBuffer(); // used for scanning
+  private byte[] termDocsBuffer = new byte[TERM_DOCS_BYTES_THRESHOLD];
 
   private TermInfo termInfo = new TermInfo();
 
@@ -39,14 +45,18 @@
   int skipInterval;
   int maxSkipLevels;
   private int formatM1SkipInterval;
+  private boolean loadDocs = false;
+  long nextFilePointer = -1;
+  IndexInput docsInput;
 
-  SegmentTermEnum(IndexInput i, FieldInfos fis, boolean isi)
-          throws CorruptIndexException, IOException {
+  SegmentTermEnum(IndexInput i, FieldInfos fis, boolean isi, boolean loadDocs)
+      throws CorruptIndexException, IOException {
     input = i;
     fieldInfos = fis;
     isIndex = isi;
-    maxSkipLevels = 1; // use single-level skip lists for formats > -3 
-    
+    this.loadDocs = loadDocs;
+    maxSkipLevels = 1; // use single-level skip lists for formats > -3
+
     int firstInt = input.readInt();
     if (firstInt >= 0) {
       // original-format file, without explicit format version number
@@ -90,19 +100,23 @@
     }
   }
 
+  void setLoadDocs(boolean loadDocs) {
+    this.loadDocs = loadDocs;
+  }
+
   protected Object clone() {
     SegmentTermEnum clone = null;
     try {
       clone = (SegmentTermEnum) super.clone();
-    } catch (CloneNotSupportedException e) {}
-
+    } catch (CloneNotSupportedException e) {
+    }
+    clone.loadDocs = loadDocs;
     clone.input = (IndexInput) input.clone();
     clone.termInfo = new TermInfo(termInfo);
 
     clone.termBuffer = (TermBuffer)termBuffer.clone();
     clone.prevBuffer = (TermBuffer)prevBuffer.clone();
     clone.scanBuffer = new TermBuffer();
-
     return clone;
   }
 
@@ -122,7 +136,7 @@
       termBuffer.reset();
       return false;
     }
-
+    
     prevBuffer.set(termBuffer);
     termBuffer.read(input, fieldInfos);
 
@@ -145,11 +159,80 @@
     }
     
     if (isIndex)
-      indexPointer += input.readVLong();	  // read index pointer
+      indexPointer += input.readVLong(); // read index pointer
 
+    // read docs
+    if (!isIndex) {
+      termInfo.numDocBytes = input.readVInt();
+      termInfo.docsPointer = input.getFilePointer();
+      if (termInfo.numDocBytes > 0) {
+        // lower than threshold, cache in terminfo
+        if (termInfo.numDocBytes <= TERM_DOCS_BYTES_THRESHOLD) {
+          byte[] bytes = new byte[termInfo.numDocBytes];
+          input.readBytes(bytes, 0, termInfo.numDocBytes);
+          termInfo.docs = new SortedVIntList(termInfo.docFreq, bytes);
+        } else {
+          // will use iterator over file
+          input.seek(termInfo.numDocBytes + termInfo.docsPointer);
+        }
+      }
+    }
     return true;
   }
 
+  private static class DocIdSetReader extends DocIdSetIterator {
+    private static final int VB1 = 0x7F;
+    private static final int BIT_SHIFT = 7;
+    private final int MAX_BYTES_PER_INT = (31 / BIT_SHIFT) + 1;
+    private int bytePos = 0;
+    private int lastBytePos;
+    int lastInt = 0;
+    private boolean isValid = true;
+    private IndexInput indexInput;
+    
+    public DocIdSetReader(IndexInput indexInput, int lastBytePos) {
+      this.indexInput = indexInput;
+      this.lastBytePos = lastBytePos;
+    }
+    
+    private void advance() throws IOException {
+      try {
+        byte b = indexInput.readByte();
+        bytePos++;
+        lastInt += b & VB1;
+        for (int s = BIT_SHIFT; (b & ~VB1) != 0; s += BIT_SHIFT) {
+          b = indexInput.readByte();
+          bytePos++;
+          lastInt += (b & VB1) << s;
+        }
+      } catch (IOException ioException) {
+        throw new IOException("file pointer: "+indexInput.getFilePointer()+" bytepos: "+bytePos+" lastBytePos: "+lastBytePos, ioException);
+      }
+    }
+    
+    public int doc() { 
+      if (!isValid) {
+        throw new RuntimeException("invalid");
+      }
+      return lastInt; }
+
+    public boolean next() throws IOException {
+      if (!isValid) {
+        throw new IOException("invalid");
+      }
+      if (bytePos >= lastBytePos) {
+        return false;
+      } else {
+        advance();
+        return true;
+      }
+    }
+
+    public boolean skipTo(int target) throws IOException {
+      throw new UnsupportedOperationException("");
+    }
+  }
+
   /** Optimized scan, without allocating new terms. */
   final void scanTo(Term term) throws IOException {
     scanBuffer.set(term);
@@ -179,20 +262,47 @@
     ti.set(termInfo);
   }
 
-  /** Returns the docFreq from the current TermInfo in the enumeration.
-   Initially invalid, valid after next() called for the first time.*/
+  /**
+   * Returns document ids a term is located in
+   */
+  public final DocIdSetIterator docs() throws IOException {
+    if (termInfo.numDocBytes > 0) {
+      if (termInfo.numDocBytes < TERM_DOCS_BYTES_THRESHOLD) {
+        return termInfo.docs.iterator();
+      } else {
+        // read from file
+        if (docsInput == null) {
+          docsInput = (IndexInput)input.clone();
+        }
+        docsInput.seek(termInfo.docsPointer);
+        DocIdSetReader docIdSetReader = new DocIdSetReader(docsInput, termInfo.numDocBytes);
+        return docIdSetReader;
+      }
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Returns the docFreq from the current TermInfo in the enumeration. Initially
+   * invalid, valid after next() called for the first time.
+   */
   public final int docFreq() {
     return termInfo.docFreq;
   }
 
-  /* Returns the freqPointer from the current TermInfo in the enumeration.
-    Initially invalid, valid after next() called for the first time.*/
+  /*
+   * Returns the freqPointer from the current TermInfo in the enumeration.
+   * Initially invalid, valid after next() called for the first time.
+   */
   final long freqPointer() {
     return termInfo.freqPointer;
   }
 
-  /* Returns the proxPointer from the current TermInfo in the enumeration.
-    Initially invalid, valid after next() called for the first time.*/
+  /*
+   * Returns the proxPointer from the current TermInfo in the enumeration.
+   * Initially invalid, valid after next() called for the first time.
+   */
   final long proxPointer() {
     return termInfo.proxPointer;
   }
@@ -200,5 +310,6 @@
   /** Closes the enumeration to further activity, freeing resources. */
   public final void close() throws IOException {
     input.close();
+    if (docsInput != null) docsInput.close();
   }
 }
Index: org/apache/lucene/index/TermEnum.java
===================================================================
--- org/apache/lucene/index/TermEnum.java	(revision 653899)
+++ org/apache/lucene/index/TermEnum.java	(working copy)
@@ -19,6 +19,8 @@
 
 import java.io.IOException;
 
+import org.apache.lucene.search.DocIdSetIterator;
+
 /** Abstract class for enumerating terms.
 
   <p>Term enumerations are always ordered by Term.compareTo().  Each term in
@@ -37,6 +39,9 @@
   /** Closes the enumeration to further activity, freeing resources. */
   public abstract void close() throws IOException;
   
+  /** Returns an iterator over the doc ids **/
+  public abstract DocIdSetIterator docs() throws IOException;
+  
 // Term Vector support
   
   /** Skips terms to the first beyond the current whose value is
Index: org/apache/lucene/index/TermInfo.java
===================================================================
--- org/apache/lucene/index/TermInfo.java	(revision 653899)
+++ org/apache/lucene/index/TermInfo.java	(working copy)
@@ -1,5 +1,7 @@
 package org.apache.lucene.index;
 
+import org.apache.lucene.util.SortedVIntList;
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -26,6 +28,9 @@
   long freqPointer = 0;
   long proxPointer = 0;
   int skipOffset;
+  long docsPointer = 0;
+  int numDocBytes;
+  SortedVIntList docs;
 
   TermInfo() {}
 
@@ -40,14 +45,18 @@
     freqPointer = ti.freqPointer;
     proxPointer = ti.proxPointer;
     skipOffset = ti.skipOffset;
+    this.docs = ti.docs;
+    this.docsPointer = ti.docsPointer;
+    this.numDocBytes = ti.numDocBytes;
   }
 
   final void set(int docFreq,
-                 long freqPointer, long proxPointer, int skipOffset) {
+                 long freqPointer, long proxPointer, int skipOffset, SortedVIntList docs) {
     this.docFreq = docFreq;
     this.freqPointer = freqPointer;
     this.proxPointer = proxPointer;
     this.skipOffset = skipOffset;
+    this.docs = docs;
   }
 
   final void set(TermInfo ti) {
@@ -55,5 +64,8 @@
     freqPointer = ti.freqPointer;
     proxPointer = ti.proxPointer;
     skipOffset = ti.skipOffset;
+    docs = ti.docs;
+    docsPointer = ti.docsPointer;
+    numDocBytes = ti.numDocBytes;
   }
 }
Index: org/apache/lucene/index/TermInfosReader.java
===================================================================
--- org/apache/lucene/index/TermInfosReader.java	(revision 653899)
+++ org/apache/lucene/index/TermInfosReader.java	(working copy)
@@ -59,12 +59,12 @@
       fieldInfos = fis;
 
       origEnum = new SegmentTermEnum(directory.openInput(segment + "." + IndexFileNames.TERMS_EXTENSION,
-          readBufferSize), fieldInfos, false);
+          readBufferSize), fieldInfos, false, false);
       size = origEnum.size;
       totalIndexInterval = origEnum.indexInterval;
 
       indexEnum = new SegmentTermEnum(directory.openInput(segment + "." + IndexFileNames.TERMS_INDEX_EXTENSION,
-          readBufferSize), fieldInfos, true);
+          readBufferSize), fieldInfos, true, true);
 
       success = true;
     } finally {
@@ -197,12 +197,17 @@
 
   /** Returns the TermInfo for a Term in the set, or null. */
   TermInfo get(Term term) throws IOException {
+		return get(term, false);
+	}
+  
+  TermInfo get(Term term, boolean loadDocs) throws IOException {
     if (size == 0) return null;
 
     ensureIndexIsRead();
 
     // optimize sequential access: first try scanning cached enum w/o seeking
     SegmentTermEnum enumerator = getEnum();
+    enumerator.setLoadDocs(loadDocs);
     if (enumerator.term() != null                 // term is at or past current
 	&& ((enumerator.prev() != null && term.compareTo(enumerator.prev())> 0)
 	    || term.compareTo(enumerator.term()) >= 0)) {
@@ -271,10 +276,25 @@
   public SegmentTermEnum terms() {
     return (SegmentTermEnum)origEnum.clone();
   }
-
+  
+  /** Returns an enumeration of all the Terms and TermInfos in the set. */
+	public SegmentTermEnum terms(boolean loadDocs) {
+		SegmentTermEnum segmentTermEnum = (SegmentTermEnum) origEnum.clone();
+		segmentTermEnum.setLoadDocs(true);
+		return segmentTermEnum;
+	}
+  
   /** Returns an enumeration of terms starting at or after the named term. */
   public SegmentTermEnum terms(Term term) throws IOException {
     get(term);
     return (SegmentTermEnum)getEnum().clone();
   }
+  
+  /** Returns an enumeration of terms starting at or after the named term. */
+	public SegmentTermEnum terms(Term term, boolean loadDocs) throws IOException {
+		get(term, loadDocs);
+		SegmentTermEnum segmentTermEnum = (SegmentTermEnum) getEnum().clone();
+		segmentTermEnum.setLoadDocs(loadDocs);
+		return segmentTermEnum;
+	}
 }
Index: org/apache/lucene/index/TermInfosWriter.java
===================================================================
--- org/apache/lucene/index/TermInfosWriter.java	(revision 653899)
+++ org/apache/lucene/index/TermInfosWriter.java	(working copy)
@@ -19,9 +19,12 @@
 
 
 import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.IndexOutput;
-import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.UnicodeUtil;
+import org.apache.lucene.util.VIntUtil;
 
 /** This stores a monotonically increasing set of <Term, TermInfo> pairs in a
   Directory.  A TermInfos can be written once, in order.  */
@@ -155,7 +158,6 @@
     TermInfo pointers must be positive and greater than all previous.*/
   void add(int fieldNumber, byte[] termBytes, int termBytesLength, TermInfo ti)
     throws IOException {
-
     assert compareToLastTerm(fieldNumber, termBytes, termBytesLength) < 0 ||
       (isIndex && termBytesLength == 0 && lastTermBytesLength == 0) :
       "Terms are out of order: field=" + fieldInfos.fieldName(fieldNumber) + " (number " + fieldNumber + ")" +
@@ -169,7 +171,7 @@
       other.add(lastFieldNumber, lastTermBytes, lastTermBytesLength, lastTi);                      // add an index term
 
     writeTerm(fieldNumber, termBytes, termBytesLength);                        // write term
-
+    
     output.writeVInt(ti.docFreq);                       // write doc freq
     output.writeVLong(ti.freqPointer - lastTi.freqPointer); // write pointers
     output.writeVLong(ti.proxPointer - lastTi.proxPointer);
@@ -182,7 +184,16 @@
       output.writeVLong(other.output.getFilePointer() - lastIndexPointer);
       lastIndexPointer = other.output.getFilePointer(); // write pointer
     }
-
+    
+    if (!isIndex) {
+      if (ti.docs == null) {
+        output.writeVInt(0);
+      } else {
+        byte[] bytes = ti.docs.getBytes();
+        output.writeVInt(bytes.length);
+        output.writeBytes(bytes, bytes.length);
+      }
+    }
     lastFieldNumber = fieldNumber;
     lastTi.set(ti);
     size++;
Index: org/apache/lucene/search/ExtendedFieldCacheImpl.java
===================================================================
--- org/apache/lucene/search/ExtendedFieldCacheImpl.java	(revision 653899)
+++ org/apache/lucene/search/ExtendedFieldCacheImpl.java	(working copy)
@@ -44,21 +44,39 @@
       String field = entry.field;
       LongParser parser = (LongParser) entry.custom;
       final long[] retArray = new long[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term(field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          long termval = parser.parseLong(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            long termval = parser.parseLong(term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term(field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            long termval = parser.parseLong(term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
@@ -84,21 +102,39 @@
       String field = entry.field;
       DoubleParser parser = (DoubleParser) entry.custom;
       final double[] retArray = new double[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          double termval = parser.parseDouble(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            double termval = parser.parseDouble(term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            double termval = parser.parseDouble(term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
Index: org/apache/lucene/search/FieldCacheImpl.java
===================================================================
--- org/apache/lucene/search/FieldCacheImpl.java	(revision 653899)
+++ org/apache/lucene/search/FieldCacheImpl.java	(working copy)
@@ -17,17 +17,20 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.TermDocs;
-import org.apache.lucene.index.TermEnum;
-
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
 /**
  * Expert: The default cache implementation, storing all values in memory.
  * A WeakHashMap is used for storage.
@@ -175,21 +178,39 @@
       String field = entry.field;
       ByteParser parser = (ByteParser) entry.custom;
       final byte[] retArray = new byte[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          byte termval = parser.parseByte(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            byte termval = parser.parseByte(term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            byte termval = parser.parseByte(term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
@@ -214,21 +235,39 @@
       String field = entry.field;
       ShortParser parser = (ShortParser) entry.custom;
       final short[] retArray = new short[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          short termval = parser.parseShort(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            short termval = parser.parseShort(term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            short termval = parser.parseShort(term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
@@ -244,7 +283,7 @@
       throws IOException {
     return (int[]) intsCache.get(reader, new Entry(field, parser));
   }
-
+  
   Cache intsCache = new Cache() {
 
     protected Object createValue(IndexReader reader, Object entryKey)
@@ -253,27 +292,45 @@
       String field = entry.field;
       IntParser parser = (IntParser) entry.custom;
       final int[] retArray = new int[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          int termval = parser.parseInt(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            int termval = parser.parseInt(term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+        return retArray;
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            int termval = parser.parseInt(term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
+        return retArray;
       }
-      return retArray;
     }
   };
 
-
   // inherit javadocs
   public float[] getFloats (IndexReader reader, String field)
     throws IOException {
@@ -294,21 +351,39 @@
       String field = entry.field;
       FloatParser parser = (FloatParser) entry.custom;
       final float[] retArray = new float[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          float termval = parser.parseFloat(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            float termval = parser.parseFloat(term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            float termval = parser.parseFloat(term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
@@ -326,21 +401,39 @@
         throws IOException {
       String field = ((String) fieldKey).intern();
       final String[] retArray = new String[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          String termval = term.text();
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            String termval = term.text();
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            String termval = term.text();
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
@@ -351,7 +444,12 @@
       throws IOException {
     return (StringIndex) stringsIndexCache.get(reader, field);
   }
-
+  
+  public static boolean isTermDocsField(String field, IndexReader reader) {
+    Collection fieldNames = reader.getFieldNames(IndexReader.FieldOption.TERM_DOCS);
+    return fieldNames.contains(field);
+  }
+  
   Cache stringsIndexCache = new Cache() {
 
     protected Object createValue(IndexReader reader, Object fieldKey)
@@ -359,6 +457,52 @@
       String field = ((String) fieldKey).intern();
       final int[] retArray = new int[reader.maxDoc()];
       String[] mterms = new String[reader.maxDoc()+1];
+      
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        
+        int t = 0;  // current term number
+
+        // an entry for documents that have no terms in this field
+        // should a document with no terms be at top or bottom?
+        // this puts them at the top - if it is changed, FieldDocSortedHitQueue
+        // needs to change as well.
+        mterms[t++] = null;
+        
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+
+            // store term text
+            // we expect that there is at most one term per document
+            if (t >= mterms.length) throw new RuntimeException ("there are more terms than " +
+                    "documents in field \"" + field + "\", but it's impossible to sort on " +
+                    "tokenized fields");
+            mterms[t] = term.text();
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[doc] = t;
+            }
+            t++;
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+        
+        if (t == 0) {
+          // if there are no terms, make the term array
+          // have a single null entry
+          mterms = new String[1];
+        } else if (t < mterms.length) {
+          // if there are less terms than documents,
+          // trim off the dead array space
+          String[] terms = new String[t];
+          System.arraycopy (mterms, 0, terms, 0, t);
+          mterms = terms;
+        }
+      } else {
       TermDocs termDocs = reader.termDocs();
       TermEnum termEnum = reader.terms (new Term (field, ""));
       int t = 0;  // current term number
@@ -392,7 +536,7 @@
         termDocs.close();
         termEnum.close();
       }
-
+      
       if (t == 0) {
         // if there are no terms, make the term array
         // have a single null entry
@@ -404,7 +548,7 @@
         System.arraycopy (mterms, 0, terms, 0, t);
         mterms = terms;
       }
-
+      }
       StringIndex value = new StringIndex (retArray, mterms);
       return value;
     }
@@ -487,21 +631,39 @@
       String field = entry.field;
       SortComparator comparator = (SortComparator) entry.custom;
       final Comparable[] retArray = new Comparable[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          Comparable termval = comparator.getComparable (term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
+      if (isTermDocsField(field, reader)) {
+        TermEnum termEnum = reader.terms (new Term (field, ""), true);
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            Comparable termval = comparator.getComparable (term.text());
+            DocIdSetIterator docIterator = termEnum.docs();
+            while (docIterator.next()) {
+              int doc = docIterator.doc();
+              retArray[docIterator.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termEnum.close();
+        }
+      } else {
+        TermDocs termDocs = reader.termDocs();
+        TermEnum termEnum = reader.terms (new Term (field, ""));
+        try {
+          do {
+            Term term = termEnum.term();
+            if (term==null || term.field() != field) break;
+            Comparable termval = comparator.getComparable (term.text());
+            termDocs.seek (termEnum);
+            while (termDocs.next()) {
+              retArray[termDocs.doc()] = termval;
+            }
+          } while (termEnum.next());
+        } finally {
+          termDocs.close();
+          termEnum.close();
+        }
       }
       return retArray;
     }
Index: org/apache/lucene/search/FilteredTermEnum.java
===================================================================
--- org/apache/lucene/search/FilteredTermEnum.java	(revision 653899)
+++ org/apache/lucene/search/FilteredTermEnum.java	(working copy)
@@ -40,6 +40,10 @@
     /** Indicates the end of the enumeration has been reached */
     protected abstract boolean endEnum();
     
+    public DocIdSetIterator docs() throws IOException {
+    	return actualEnum.docs();
+    }
+    
     protected void setEnum(TermEnum actualEnum) throws IOException {
         this.actualEnum = actualEnum;
         // Find the first term that matches
Index: org/apache/lucene/search/RangeFilter.java
===================================================================
--- org/apache/lucene/search/RangeFilter.java	(revision 653899)
+++ org/apache/lucene/search/RangeFilter.java	(working copy)
@@ -159,6 +159,10 @@
      * permitted in search results.
      */
     public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+        if (FieldCacheImpl.isTermDocsField(fieldName, reader)) {
+          return getDocIdSetTermDocs(reader);
+        }
+      
         OpenBitSet bits = new OpenBitSet(reader.maxDoc());
         
         TermEnum enumerator =
@@ -216,6 +220,55 @@
         return bits;
     }
     
+    private DocIdSet getDocIdSetTermDocs(IndexReader reader) throws IOException {
+        OpenBitSet bits = new OpenBitSet(reader.maxDoc());
+        
+        TermEnum enumerator =
+            (null != lowerTerm
+             ? reader.terms(new Term(fieldName, lowerTerm), true)
+             : reader.terms(new Term(fieldName,""), true));
+        
+        try {
+            
+            if (enumerator.term() == null) {
+                return bits;
+            }
+            
+            boolean checkLower = false;
+            if (!includeLower) // make adjustments to set to exclusive
+                checkLower = true;
+                do {
+                    Term term = enumerator.term();
+                    if (term != null && term.field().equals(fieldName)) {
+                        if (!checkLower || null==lowerTerm || term.text().compareTo(lowerTerm) > 0) {
+                            checkLower = false;
+                            if (upperTerm != null) {
+                                int compare = upperTerm.compareTo(term.text());
+                                /* if beyond the upper term, or is exclusive and
+                                 * this is equal to the upper term, break out */
+                                if ((compare < 0) ||
+                                    (!includeUpper && compare==0)) {
+                                    break;
+                                }
+                            }
+                            /* we have a good term, find the docs */
+                            DocIdSetIterator docsIterator = enumerator.docs();
+                            while (docsIterator.next()) {
+                              bits.set(docsIterator.doc());
+                            }
+                        }
+                    } else {
+                        break;
+                    }
+                }
+                while (enumerator.next());
+        } finally {
+            enumerator.close();
+        }
+
+        return bits;
+    }
+    
     public String toString() {
         StringBuffer buffer = new StringBuffer();
         buffer.append(fieldName);
Index: org/apache/lucene/util/SortedVIntList.java
===================================================================
--- org/apache/lucene/util/SortedVIntList.java	(revision 653899)
+++ org/apache/lucene/util/SortedVIntList.java	(working copy)
@@ -39,7 +39,17 @@
   private int size;
   private byte[] bytes;
   private int lastBytePos;
-    
+  
+  public SortedVIntList(int size, byte[] bytes) {
+  	this.size = size;
+    this.bytes = bytes;
+    lastBytePos = bytes.length;
+  }
+  
+  public byte[] getBytes() {
+  	return bytes;
+  }
+  
   /**
    *  Create a SortedVIntList from all elements of an array of integers.
    *
