Index: lucene/src/test/org/apache/lucene/index/TestParallelReader.java =================================================================== --- lucene/src/test/org/apache/lucene/index/TestParallelReader.java (revision 963177) +++ lucene/src/test/org/apache/lucene/index/TestParallelReader.java (working copy) @@ -20,10 +20,14 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; +import org.apache.lucene.document.FieldSelector; +import org.apache.lucene.document.FieldSelectorResult; +import org.apache.lucene.document.LoadFirstFieldSelector; import org.apache.lucene.document.MapFieldSelector; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexSearcher; @@ -99,7 +103,57 @@ assertEquals("v2", doc223.get("f2")); assertEquals("v2", doc223.get("f3")); } - + + /** + * {@link LoadFirstFieldSelector} loads the first field of EACH sub-reader. + * @throws IOException + */ + public void testDocumentLoadFirst() throws IOException { + Directory dir1 = getDir1(); + Directory dir2 = getDir2(); + ParallelReader pr = new ParallelReader(); + pr.add(IndexReader.open(dir1, false)); + pr.add(IndexReader.open(dir2, false)); + + Document doc1 = pr.document(0, new LoadFirstFieldSelector()); + Document doc2 = pr.document(1, new LoadFirstFieldSelector()); + + assertEquals(2, doc1.getFields().size()); + assertEquals(2, doc2.getFields().size()); + + assertEquals("v1", doc1.get("f1")); + assertEquals("v1", doc1.get("f1")); + assertEquals("v2", doc2.get("f3")); + assertEquals("v2", doc2.get("f3")); + } + + public void testDocumentAcceptF2BlockedByBreak() throws IOException { + Directory dir1 = getDir1(); + Directory dir2 = getDir2(); + ParallelReader pr = new ParallelReader(); + pr.add(IndexReader.open(dir1, false)); + pr.add(IndexReader.open(dir2, false)); + final AtomicInteger counter = new AtomicInteger(0); + + // count calls to accept, validating include == false inside the ParallelReader + FieldSelector fs = new FieldSelector() { + private static final long serialVersionUID = 1L; + @Override + public FieldSelectorResult accept(String fieldName) { + counter.incrementAndGet(); + return "f2".equals(fieldName) ? FieldSelectorResult.LOAD : FieldSelectorResult.BREAK; + } + }; + + Document doc1 = pr.document(0, fs); + assertEquals(2, counter.get()); + Document doc2 = pr.document(1, fs); + assertEquals(4, counter.get()); + + assertEquals(0, doc1.getFields().size()); + assertEquals(0, doc2.getFields().size()); + } + public void testIncompatibleIndexes() throws IOException { // two documents: Directory dir1 = getDir1(); Index: lucene/src/test/org/apache/lucene/document/TestFieldSelectorResult.java =================================================================== --- lucene/src/test/org/apache/lucene/document/TestFieldSelectorResult.java (revision 0) +++ lucene/src/test/org/apache/lucene/document/TestFieldSelectorResult.java (revision 0) @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.lucene.document; + +import org.apache.lucene.util.LuceneTestCase; + +/** + * Test code for the FieldSelectorResult class + */ +public class TestFieldSelectorResult extends LuceneTestCase { + + /** + * Test method for {@link org.apache.lucene.document.FieldSelectorResult#loads}. + */ + public void testIsLoad() { + assertTrue(FieldSelectorResult.LOAD.loads); + assertTrue(FieldSelectorResult.LOAD_AND_BREAK.loads); + // lazy load returns true b/c it "could" be loaded + assertTrue(FieldSelectorResult.LAZY_LOAD.loads); + assertTrue(FieldSelectorResult.LATENT.loads); + + assertFalse(FieldSelectorResult.BREAK.loads); + assertFalse(FieldSelectorResult.NO_LOAD.loads); + assertFalse(FieldSelectorResult.SIZE.loads); + assertFalse(FieldSelectorResult.SIZE_AND_BREAK.loads); + } + + /** + * Test method for {@link org.apache.lucene.document.FieldSelectorResult#breaks}. + */ + public void testIsBreak() { + assertTrue(FieldSelectorResult.LOAD_AND_BREAK.breaks); + assertTrue(FieldSelectorResult.BREAK.breaks); + assertTrue(FieldSelectorResult.SIZE_AND_BREAK.breaks); + + assertFalse(FieldSelectorResult.LOAD.breaks); + assertFalse(FieldSelectorResult.LAZY_LOAD.breaks); + assertFalse(FieldSelectorResult.LATENT.breaks); + assertFalse(FieldSelectorResult.NO_LOAD.breaks); + assertFalse(FieldSelectorResult.SIZE.breaks); + } + +} \ No newline at end of file Index: lucene/src/java/org/apache/lucene/index/FieldsReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/FieldsReader.java (revision 963177) +++ lucene/src/java/org/apache/lucene/index/FieldsReader.java (working copy) @@ -211,29 +211,33 @@ boolean binary = (bits & FieldsWriter.FIELD_IS_BINARY) != 0; //TODO: Find an alternative approach here if this list continues to grow beyond the //list of 5 or 6 currently here. See Lucene 762 for discussion - if (acceptField.equals(FieldSelectorResult.LOAD)) { - addField(doc, fi, binary, tokenize); + switch (acceptField) { + case LOAD: + case LOAD_AND_BREAK: + addField(doc, fi, binary, tokenize); + break; + case LAZY_LOAD: + addFieldLazy(doc, fi, binary, tokenize, true); + break; + case LATENT: + addFieldLazy(doc, fi, binary, tokenize, false); + break; + case SIZE: + skipField(addFieldSize(doc, fi, binary)); + break; + case SIZE_AND_BREAK: + addFieldSize(doc, fi, binary); + break; + case BREAK: + break; + case NO_LOAD: + default: + skipField(); } - else if (acceptField.equals(FieldSelectorResult.LOAD_AND_BREAK)){ - addField(doc, fi, binary, tokenize); - break;//Get out of this loop - } - else if (acceptField.equals(FieldSelectorResult.LAZY_LOAD)) { - addFieldLazy(doc, fi, binary, tokenize, true); - } - else if (acceptField.equals(FieldSelectorResult.LATENT)) { - addFieldLazy(doc, fi, binary, tokenize, false); - } - else if (acceptField.equals(FieldSelectorResult.SIZE)){ - skipField(addFieldSize(doc, fi, binary)); - } - else if (acceptField.equals(FieldSelectorResult.SIZE_AND_BREAK)){ - addFieldSize(doc, fi, binary); + if (acceptField.breaks) { + // Get out of this loop break; } - else { - skipField(); - } } return doc; @@ -467,4 +471,4 @@ return null; } } -} +} \ No newline at end of file Index: lucene/src/java/org/apache/lucene/index/ParallelReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/ParallelReader.java (revision 963177) +++ lucene/src/java/org/apache/lucene/index/ParallelReader.java (working copy) @@ -21,6 +21,7 @@ import org.apache.lucene.document.FieldSelector; import org.apache.lucene.document.FieldSelectorResult; import org.apache.lucene.document.Fieldable; +import org.apache.lucene.document.LoadFirstFieldSelector; import org.apache.lucene.util.Bits; import org.apache.lucene.search.FieldCache; // not great (circular); used only to purge FieldCache entry on close import org.apache.lucene.util.BytesRef; @@ -45,6 +46,9 @@ * documents to one index, you need to add the same documents in the * same order to the other indexes. Failure to do so will result in * undefined behavior. + *

Warning: {@link LoadFirstFieldSelector} will not work + * as "expected" with this reader. Instead the first field per subreader is + * loaded. */ public class ParallelReader extends IndexReader { private List readers = new ArrayList(); @@ -357,11 +361,15 @@ boolean include = (fieldSelector==null); if (!include) { Collection fields = readerToFields.get(reader); - for (final String field : fields) - if (fieldSelector.accept(field) != FieldSelectorResult.NO_LOAD) { - include = true; + for (final String field : fields) { + final FieldSelectorResult fsr = fieldSelector.accept(field); + if (fsr != FieldSelectorResult.NO_LOAD) { + // if loaded or used in any way + include = (fsr != FieldSelectorResult.BREAK); break; } + // else: everything else is an included field of some sort + } } if (include) { List fields = reader.document(n, fieldSelector).getFields(); Index: lucene/src/java/org/apache/lucene/document/FieldSelectorResult.java =================================================================== --- lucene/src/java/org/apache/lucene/document/FieldSelectorResult.java (revision 963177) +++ lucene/src/java/org/apache/lucene/document/FieldSelectorResult.java (working copy) @@ -28,7 +28,7 @@ *

* {@link Document#add(Fieldable)} should be called by the Reader. */ - LOAD, + LOAD(true, false), /** * Lazily load this {@link Field}. This means the {@link Field} is valid, but it may not actually contain its data until @@ -36,8 +36,9 @@ * return a valid instance of a {@link Fieldable}. *

* {@link Document#add(Fieldable)} should be called by the Reader. + * {@link #isLoad()} will return true because this field may eventually be loaded. */ - LAZY_LOAD, + LAZY_LOAD(true, false), /** * Do not load the {@link Field}. {@link Document#getField(String)} and {@link Document#getFieldable(String)} should return null. @@ -45,7 +46,7 @@ *

* {@link Document#add(Fieldable)} should not be called by the Reader. */ - NO_LOAD, + NO_LOAD(false, false), /** * Load this field as in the {@link #LOAD} case, but immediately return from {@link Field} loading for the {@link Document}. Thus, the @@ -54,16 +55,16 @@ *

* {@link Document#add(Fieldable)} should be called by the Reader. */ - LOAD_AND_BREAK, + LOAD_AND_BREAK(true, true), /** Expert: Load the size of this {@link Field} rather than its value. * Size is measured as number of bytes required to store the field == bytes for a binary or any compressed value, and 2*chars for a String value. * The size is stored as a binary value, represented as an int in a byte[], with the higher order byte first in [0] */ - SIZE, + SIZE(false, false), /** Expert: Like {@link #SIZE} but immediately break from the field loading loop, i.e., stop loading further fields, after the size is loaded */ - SIZE_AND_BREAK, + SIZE_AND_BREAK(false, true), /** * Lazily load this {@link Field}, but do not cache the result. This means the {@link Field} is valid, but it may not actually contain its data until @@ -72,5 +73,18 @@ *

* {@link Document#add(Fieldable)} should be called by the Reader. */ - LATENT -} + LATENT(true, false), + + /** Expert: break immediately (do not load) */ + BREAK(false, true); + + /** Field Data is loaded */ + public final boolean loads; + /** Field processing should break with this result */ + public final boolean breaks; + + private FieldSelectorResult( final boolean loads, final boolean breaks ) { + this.loads = loads; + this.breaks = breaks; + } +} \ No newline at end of file