diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/index/FieldsReader.java LuceneTrunk/src/java/org/apache/lucene/index/FieldsReader.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/index/FieldsReader.java	2005-12-07 21:29:59.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/index/FieldsReader.java	2006-04-27 00:07:33.000000000 -1000
@@ -58,83 +58,114 @@
   }
 
   final Document doc(int n) throws IOException {
+    return doc(n, null);
+  }
+  
+  final Document doc(int n, String[] fields) throws IOException {
+      
+    if (fields!=null)
+      for (int i=0; i<fields.length; i++)
+        fields[i] = fields[i].intern();
+      
     indexStream.seek(n * 8L);
     long position = indexStream.readLong();
     fieldsStream.seek(position);
 
     Document doc = new Document();
     int numFields = fieldsStream.readVInt();
+    boolean load = true;
+    
     for (int i = 0; i < numFields; i++) {
       int fieldNumber = fieldsStream.readVInt();
       FieldInfo fi = fieldInfos.fieldInfo(fieldNumber);
-
-      byte bits = fieldsStream.readByte();
+    
+      if (fields!=null) {
+        load = false;
+        for (int j=0; j<fields.length; j++)
+          if (fi.name==fields[j]) {
+            load = true;
+            break;
+          }
+      }
       
+      byte bits = fieldsStream.readByte();
+      boolean binary = (bits & FieldsWriter.FIELD_IS_BINARY) != 0;
       boolean compressed = (bits & FieldsWriter.FIELD_IS_COMPRESSED) != 0;
-      boolean tokenize = (bits & FieldsWriter.FIELD_IS_TOKENIZED) != 0;
-      
-      if ((bits & FieldsWriter.FIELD_IS_BINARY) != 0) {
-        final byte[] b = new byte[fieldsStream.readVInt()];
-        fieldsStream.readBytes(b, 0, b.length);
-        if (compressed)
-          doc.add(new Field(fi.name, uncompress(b), Field.Store.COMPRESS));
-        else
-          doc.add(new Field(fi.name, b, Field.Store.YES));
-      }
-      else {
-        Field.Index index;
-        Field.Store store = Field.Store.YES;
-        
-        if (fi.isIndexed && tokenize)
-          index = Field.Index.TOKENIZED;
-        else if (fi.isIndexed && !tokenize)
-          index = Field.Index.UN_TOKENIZED;
-        else
-          index = Field.Index.NO;
-        
-        Field.TermVector termVector = null;
-        if (fi.storeTermVector) {
-          if (fi.storeOffsetWithTermVector) {
-            if (fi.storePositionWithTermVector) {
-              termVector = Field.TermVector.WITH_POSITIONS_OFFSETS;
+
+      if (load) {
+        boolean tokenize = (bits & FieldsWriter.FIELD_IS_TOKENIZED) != 0;
+
+        if (binary) {
+          final byte[] b = new byte[fieldsStream.readVInt()];
+          fieldsStream.readBytes(b, 0, b.length);
+          if (compressed)
+            doc.add(new Field(fi.name, uncompress(b), Field.Store.COMPRESS));
+          else
+            doc.add(new Field(fi.name, b, Field.Store.YES));
+        }
+        else {
+          Field.Index index;
+          Field.Store store = Field.Store.YES;
+
+          if (fi.isIndexed && tokenize)
+            index = Field.Index.TOKENIZED;
+          else if (fi.isIndexed && !tokenize)
+            index = Field.Index.UN_TOKENIZED;
+          else
+            index = Field.Index.NO;
+
+          Field.TermVector termVector = null;
+          if (fi.storeTermVector) {
+            if (fi.storeOffsetWithTermVector) {
+              if (fi.storePositionWithTermVector) {
+                termVector = Field.TermVector.WITH_POSITIONS_OFFSETS;
+              }
+              else {
+                termVector = Field.TermVector.WITH_OFFSETS;
+              }
+            }
+            else if (fi.storePositionWithTermVector) {
+              termVector = Field.TermVector.WITH_POSITIONS;
             }
             else {
-              termVector = Field.TermVector.WITH_OFFSETS;
+              termVector = Field.TermVector.YES;
             }
           }
-          else if (fi.storePositionWithTermVector) {
-            termVector = Field.TermVector.WITH_POSITIONS;
-          }
           else {
-            termVector = Field.TermVector.YES;
+            termVector = Field.TermVector.NO;
           }
-        }
-        else {
-          termVector = Field.TermVector.NO;
-        }
-        
-        if (compressed) {
-          store = Field.Store.COMPRESS;
-          final byte[] b = new byte[fieldsStream.readVInt()];
-          fieldsStream.readBytes(b, 0, b.length);
-          Field f = new Field(fi.name,      // field name
-              new String(uncompress(b), "UTF-8"), // uncompress the value and add as string
-              store,
-              index,
-              termVector);
-          f.setOmitNorms(fi.omitNorms);
-          doc.add(f);
-        }
-        else {
-          Field f = new Field(fi.name,     // name
-                fieldsStream.readString(), // read value
+
+          if (compressed) {
+            store = Field.Store.COMPRESS;
+            final byte[] b = new byte[fieldsStream.readVInt()];
+            fieldsStream.readBytes(b, 0, b.length);
+            Field f = new Field(fi.name,      // field name
+                new String(uncompress(b), "UTF-8"), // uncompress the value and add as string
                 store,
                 index,
                 termVector);
-          f.setOmitNorms(fi.omitNorms);
-          doc.add(f);
+            f.setOmitNorms(fi.omitNorms);
+            doc.add(f);
+          }
+          else {
+            Field f = new Field(fi.name,     // name
+                  fieldsStream.readString(), // read value
+                  store,
+                  index,
+                  termVector);
+            f.setOmitNorms(fi.omitNorms);
+            doc.add(f);
+          }
+        }
+      } else {
+        int length = fieldsStream.readVInt();
+        if (binary  || compressed) {
+          long pos = fieldsStream.getFilePointer();
+          fieldsStream.seek(pos+length);              // Skip the field value
         }
+        else 
+          fieldsStream.skipChars(length);
       }
     }
 
diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/index/FilterIndexReader.java LuceneTrunk/src/java/org/apache/lucene/index/FilterIndexReader.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/index/FilterIndexReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/index/FilterIndexReader.java	2006-04-26 19:05:15.000000000 -1000
@@ -102,6 +102,8 @@
 
   public Document document(int n) throws IOException { return in.document(n); }
 
+  public Document document(int n, String[] fields) throws IOException { return in.document(n, fields); }
+  
   public boolean isDeleted(int n) { return in.isDeleted(n); }
   public boolean hasDeletions() { return in.hasDeletions(); }
   protected void doUndeleteAll() throws IOException { in.undeleteAll(); }
diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/index/IndexReader.java LuceneTrunk/src/java/org/apache/lucene/index/IndexReader.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/index/IndexReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/index/IndexReader.java	2006-04-26 19:03:16.000000000 -1000
@@ -356,6 +356,13 @@
   /** Returns the stored fields of the <code>n</code><sup>th</sup>
    <code>Document</code> in this index. */
   public abstract Document document(int n) throws IOException;
+  
+  /** Like document(int n) but only returns selected fields.  More efficient if you only need these fields.
+   * @param n the doc-id of the desired document
+   * @param fields the names of the desired fields
+   * @return a document n with just all fields named in fields, but no others
+   */
+  public abstract Document document(int n, String[] fields) throws IOException;
 
   /** Returns true if document <i>n</i> has been deleted */
   public abstract boolean isDeleted(int n);
diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/index/MultiReader.java LuceneTrunk/src/java/org/apache/lucene/index/MultiReader.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/index/MultiReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/index/MultiReader.java	2006-04-26 19:04:22.000000000 -1000
@@ -103,7 +103,12 @@
     int i = readerIndex(n);                          // find segment num
     return subReaders[i].document(n - starts[i]);    // dispatch to segment reader
   }
-
+  
+  public Document document(int n, String[] fields) throws IOException {
+    int i = readerIndex(n);                                  // find segment num
+    return subReaders[i].document(n - starts[i], fields);    // dispatch to segment reader
+  }
+  
   public boolean isDeleted(int n) {
     int i = readerIndex(n);                           // find segment num
     return subReaders[i].isDeleted(n - starts[i]);    // dispatch to segment reader
diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/index/ParallelReader.java LuceneTrunk/src/java/org/apache/lucene/index/ParallelReader.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/index/ParallelReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/index/ParallelReader.java	2006-04-26 20:31:16.000000000 -1000
@@ -143,7 +143,32 @@
     }
     return result;
   }
-
+  
+  // append selected fields from storedFieldReaders
+  public Document document(int n, String[] selectedFields) throws IOException {
+    IndexReader[] readers = new IndexReader[selectedFields.length];
+    int numReaders = 0;
+    
+    outer:
+    for (int i=0; i<selectedFields.length; i++) {
+      IndexReader reader = (IndexReader)fieldToReader.get(selectedFields[i]);
+      for (int j=0; j<numReaders; j++)
+        if (readers[j]==reader)
+          continue outer;
+      readers[numReaders++] = reader;
+    }
+      
+    Document result = new Document();
+    for (int i = 0; i<numReaders; i++) {
+      IndexReader reader = (IndexReader)readers[i];
+      Enumeration fields = reader.document(n, selectedFields).fields();
+      while (fields.hasMoreElements()) {
+        result.add((Field)fields.nextElement());
+      }
+    }
+    return result;
+  }
+  
   // get all vectors
   public TermFreqVector[] getTermFreqVectors(int n) throws IOException {
     ArrayList results = new ArrayList();
diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/index/SegmentReader.java LuceneTrunk/src/java/org/apache/lucene/index/SegmentReader.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/index/SegmentReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/index/SegmentReader.java	2006-04-26 19:05:59.000000000 -1000
@@ -284,6 +284,13 @@
     return fieldsReader.doc(n);
   }
 
+  public synchronized Document document(int n, String[] fields) throws IOException {
+    if (isDeleted(n))
+      throw new IllegalArgumentException
+              ("attempt to access a deleted document");
+    return fieldsReader.doc(n, fields);
+  }
+  
   public synchronized boolean isDeleted(int n) {
     return (deletedDocs != null && deletedDocs.get(n));
   }
diff -urX exclude.files LuceneTrunkFreshPull/src/java/org/apache/lucene/store/IndexInput.java LuceneTrunk/src/java/org/apache/lucene/store/IndexInput.java
--- LuceneTrunkFreshPull/src/java/org/apache/lucene/store/IndexInput.java	2005-12-07 21:30:00.000000000 -1000
+++ LuceneTrunk/src/java/org/apache/lucene/store/IndexInput.java	2006-04-27 00:06:21.000000000 -1000
@@ -116,6 +116,21 @@
     }
   }
 
+  /** Skip UTF-8 encoded characters.
+   * @param length the number of characters to skip
+   * @throws IOException if there is a problem reading the characters
+   */
+  public void skipChars(int length) throws IOException {
+    for (int j=0; j<length; j++) {
+      byte b = readByte();
+      if ((b&0200)!=0) {
+        readByte();
+        if ((b&0140)==0140)
+          readByte();
+      }
+    }
+  }
+  
   /** Closes the stream to futher operations. */
   public abstract void close() throws IOException;
 
diff -urX exclude.files LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestFieldsReader.java LuceneTrunk/src/test/org/apache/lucene/index/TestFieldsReader.java
--- LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestFieldsReader.java	2005-12-07 21:29:48.000000000 -1000
+++ LuceneTrunk/src/test/org/apache/lucene/index/TestFieldsReader.java	2006-04-26 20:42:24.000000000 -1000
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import java.util.Enumeration;
 import junit.framework.TestCase;
 import org.apache.lucene.store.RAMDirectory;
 import org.apache.lucene.document.Document;
@@ -68,7 +69,27 @@
     assertTrue(field.isStoreOffsetWithTermVector() == false);
     assertTrue(field.isStorePositionWithTermVector() == false);
     assertTrue(field.getOmitNorms() == true);
-
+    
+    doc = reader.doc(0, new String[]{"textField2", "textField3"});
+    Enumeration fields = doc.fields();
+    int numFields = 0;
+    while (fields.hasMoreElements()) {
+      numFields++;
+      field = (Field) fields.nextElement();
+      if (field.name().equals("textField2")) {
+        assertTrue(field.isTermVectorStored() == true);
+        assertTrue(field.isStoreOffsetWithTermVector() == true);
+        assertTrue(field.isStorePositionWithTermVector() == true);
+        assertTrue(field.getOmitNorms() == false);
+      } else {
+        assertEquals("textField3", field.name());
+        assertTrue(field.isTermVectorStored() == false);
+        assertTrue(field.isStoreOffsetWithTermVector() == false);
+        assertTrue(field.isStorePositionWithTermVector() == false);
+        assertTrue(field.getOmitNorms() == true);
+      }
+    }
+    assertEquals(2, numFields);
 
     reader.close();
   }
diff -urX exclude.files LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestMultiReader.java LuceneTrunk/src/test/org/apache/lucene/index/TestMultiReader.java
--- LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestMultiReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/test/org/apache/lucene/index/TestMultiReader.java	2006-04-26 20:53:12.000000000 -1000
@@ -16,8 +16,10 @@
  * limitations under the License.
  */
 
+import java.util.Enumeration;
 import junit.framework.TestCase;
 import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.RAMDirectory;
 
@@ -68,6 +70,20 @@
     TermFreqVector vector = reader.getTermFreqVector(0, DocHelper.TEXT_FIELD_2_KEY);
     assertTrue(vector != null);
     TestSegmentReader.checkNorms(reader);
+    
+    Document result = reader.document(0, new String[]{"keyField", "omitNorms", "textField1"});
+    assertTrue(result != null);
+    //There are 2 unstored fields on the document that are not preserved across writing
+    assertTrue(DocHelper.numFields(result) == 3);
+    
+    Enumeration fields = result.fields();
+    while (fields.hasMoreElements()) {
+      Field field = (Field) fields.nextElement();
+      assertTrue(field != null);
+      assertTrue(field.name().equals("keyField") || field.name().equals("omitNorms") || field.name().equals("textField1"));
+      assertTrue(DocHelper.nameValues.containsKey(field.name()));
+    }
+    
   }
 
   public void testUndeleteAll() throws IOException {
diff -urX exclude.files LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestParallelReader.java LuceneTrunk/src/test/org/apache/lucene/index/TestParallelReader.java
--- LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestParallelReader.java	2005-12-07 21:29:48.000000000 -1000
+++ LuceneTrunk/src/test/org/apache/lucene/index/TestParallelReader.java	2006-04-26 21:00:54.000000000 -1000
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Enumeration;
 
 import junit.framework.TestCase;
 
@@ -92,6 +93,24 @@
       // expected exception
     }
   }
+  
+  public void testDocument() throws IOException {
+    Directory dir1 = getDir1();
+    Directory dir2 = getDir2();
+    ParallelReader pr = new ParallelReader();
+    pr.add(IndexReader.open(dir1));
+    pr.add(IndexReader.open(dir2));
+    
+    Document doc = pr.document(0, new String[]{"f1","f3"});
+    assertTrue(doc!=null);
+    assertTrue(DocHelper.numFields(doc)==2);
+    Enumeration fields = doc.fields();
+    while (fields.hasMoreElements()) {
+      Field field = (Field) fields.nextElement();
+      assertEquals("v1", field.stringValue());
+      assertTrue(field.name().equals("f1") || field.name().equals("f3"));
+    }
+  }
 
   private void queryTest(Query query) throws IOException {
     Hits parallelHits = parallel.search(query);
diff -urX exclude.files LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestSegmentReader.java LuceneTrunk/src/test/org/apache/lucene/index/TestSegmentReader.java
--- LuceneTrunkFreshPull/src/test/org/apache/lucene/index/TestSegmentReader.java	2006-04-21 13:48:58.000000000 -1000
+++ LuceneTrunk/src/test/org/apache/lucene/index/TestSegmentReader.java	2006-04-26 20:47:56.000000000 -1000
@@ -68,6 +68,20 @@
       assertTrue(field != null);
       assertTrue(DocHelper.nameValues.containsKey(field.name()));
     }
+    
+    result = reader.document(0, new String[]{"keyField", "omitNorms", "textField1"});
+    assertTrue(result != null);
+    //There are 2 unstored fields on the document that are not preserved across writing
+    assertTrue(DocHelper.numFields(result) == 3);
+    
+    fields = result.fields();
+    while (fields.hasMoreElements()) {
+      Field field = (Field) fields.nextElement();
+      assertTrue(field != null);
+      assertTrue(field.name().equals("keyField") || field.name().equals("omitNorms") || field.name().equals("textField1"));
+      assertTrue(DocHelper.nameValues.containsKey(field.name()));
+    }
+    
   }
   
   public void testDelete() throws IOException {
