Index: src/java/org/apache/lucene/search/TopFieldCollector.java =================================================================== --- src/java/org/apache/lucene/search/TopFieldCollector.java (revision 0) +++ src/java/org/apache/lucene/search/TopFieldCollector.java (revision 0) @@ -0,0 +1,204 @@ +package org.apache.lucene.search; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.FieldValueHitQueue.Entry; + +/** + * A {@link HitCollector} that sorts by {@link SortField} using + * {@link FieldComparator}s. + * + * NOTE: This API is experimental and might change in + * incompatible ways in the next release. + */ +public class TopFieldCollector extends MultiReaderHitCollector { + + private final FieldValueHitQueue queue; + + private final FieldComparator[] comparators; + private FieldComparator comparator1; + private final int numComparators; + private int[] reverseMul; + private int reverseMul1 = 0; + + private int subReaderIndex = -1; + + private final int numHits; + private int totalHits; + private FieldValueHitQueue.Entry weakest = null; + + /** Stores the maximum score value encountered, needed for normalizing. */ + private float maxScore = Float.NEGATIVE_INFINITY; + + private boolean queueFull; + + private boolean fillFields; + + private ComparatorPolicy[] comparatorPolicies; + + + public TopFieldCollector(Sort sort, int numHits, IndexReader[] subReaders, boolean fillFields) + throws IOException { + queue = new FieldValueHitQueue(sort.fields, numHits, subReaders); + comparators = queue.getComparators(); + comparatorPolicies = queue.getComparatorPolicies(); + reverseMul = queue.getReverseMul(); + numComparators = comparatorPolicies.length; + + + this.numHits = numHits; + + this.fillFields = fillFields; + + } + + static boolean first = true; + + // javadoc inherited + public void setNextReader(IndexReader reader, int docBase) throws IOException { + final int numSlotsFull; + if (queueFull) + numSlotsFull = numHits; + else + numSlotsFull = totalHits; + + subReaderIndex++; + for (int i = 0; i < numComparators; i++) { + FieldComparator comparator = comparatorPolicies[i].nextComparator(comparators[i], reader, numHits, numSlotsFull); + comparator.setNextReader(reader, docBase, numSlotsFull); + // nocommit + if (first) { + System.out.println("COMP=" + comparator); + first = false; + } + comparators[i] = comparator; + } + if (numComparators == 1) { + comparator1 = comparators[0]; + reverseMul1 = reverseMul[0]; + } else { + comparator1 = null; + reverseMul1 = 0; + } + queue.setComparator1(comparator1); + queue.setReverseMul1(reverseMul1); + } + + // javadoc inherited + public void collect(int doc, float score) { + if (score > 0.0f) { + + maxScore = Math.max(maxScore, score); + totalHits++; + + if (queueFull) { + // Fastmatch: return if this hit is not competitive + if (numComparators == 1) { + // Common case + if (reverseMul1*comparator1.compareBottom(doc, score) <= 0) { + // Definitely not competitive + return; + } + + // This hit is competitive -- replace weakest + // element in queue & adjustTop + comparator1.copy(weakest.slot, doc, score); + } else { + for(int i=0;;i++) { + final int c = reverseMul[i] * comparators[i].compare(weakest.slot, doc, score); + if (c < 0) { + // Definitely not competitive + return; + } else if (c > 0) { + // Definitely competitive + break; + } else if (i == numComparators-1) { + // This is the equals case. We don't need to + // fallback to docID comparison because lower + // docIDs always win, and this docID must be + // greater than all docIDs in the queue now, + // so this hit is not competitive. + return; + } + } + + // This hit is competitive -- replace weakest + // element in queue & adjustTop + for (int i = 0; i < numComparators; i++) { + comparators[i].copy(weakest.slot, doc, score); + } + } + + weakest.subReaderIndex = subReaderIndex; + weakest.subDocID = doc; + weakest.score = score; + queue.adjustTop(); + weakest = (FieldValueHitQueue.Entry) queue.top(); + + comparators[0].setBottom(weakest.slot); + } else { + // Startup transient: queue hasn't gathered numHits + // yet + + final int slot = totalHits-1; + + // Copy hit into queue + if (numComparators == 1) { + // Common case + comparator1.copy(slot, doc, score); + } else { + for (int i = 0; i < numComparators; i++) { + comparators[i].copy(slot, doc, score); + } + } + + queue.put(new FieldValueHitQueue.Entry(slot, + subReaderIndex, doc, score)); + + weakest = (FieldValueHitQueue.Entry) queue.top(); + queueFull = totalHits == numHits; + + if (queueFull) { + comparators[0].setBottom(weakest.slot); + } + } + } + } + + // javadoc inherited + public TopDocs topDocs(int[] starts) { + ScoreDoc[] scoreDocs = new ScoreDoc[queue.size()]; + if (fillFields) { + for (int i = queue.size() - 1; i >= 0; i--) { + scoreDocs[i] = queue.fillFields((FieldValueHitQueue.Entry) queue.pop(), + starts); + } + } else { + Entry entry = (FieldValueHitQueue.Entry) queue.pop(); + for (int i = queue.size() - 1; i >= 0; i--) { + scoreDocs[i] = new FieldDoc(entry.subDocID + starts[entry.subReaderIndex], + entry.score); + } + } + + return new TopFieldDocs(totalHits, scoreDocs, queue.getFields(), maxScore); + } +} Property changes on: src/java/org/apache/lucene/search/TopFieldCollector.java ___________________________________________________________________ Name: svn:eol-style + native Index: src/java/org/apache/lucene/search/FieldComparator.java =================================================================== --- src/java/org/apache/lucene/search/FieldComparator.java (revision 0) +++ src/java/org/apache/lucene/search/FieldComparator.java (revision 0) @@ -0,0 +1,1256 @@ +package org.apache.lucene.search; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.ExtendedFieldCache.DoubleParser; +import org.apache.lucene.search.ExtendedFieldCache.LongParser; +import org.apache.lucene.search.FieldCache.ByteParser; +import org.apache.lucene.search.FieldCache.FloatParser; +import org.apache.lucene.search.FieldCache.IntParser; +import org.apache.lucene.search.FieldCache.ShortParser; +import org.apache.lucene.search.FieldCache.StringIndex; + +/** + * A FieldComparator compares hits across multiple IndexReaders. + * + * A comparator can compare a hit at hit 'slot a' with hit 'slot b', + * compare a hit on 'doc i' with hit 'slot a', or copy a hit at 'doc i' + * to 'slot a'. Each slot refers to a hit while each doc refers to the + * current IndexReader. + * + * NOTE: This API is experimental and might change in + * incompatible ways in the next release. + */ +public abstract class FieldComparator { + + public static final class ByteComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final byte[] values; + private byte[] currentReaderValues; + private final String field; + private ByteParser parser; + + ByteComparator(int numHits, String field, FieldCache.Parser parser) { + values = new byte[numHits]; + this.field = field; + this.parser = (ByteParser) parser; + } + + public int compare(int slot1, int slot2) { + return values[slot1] - values[slot2]; + } + + public int compare(int slot, int doc, float score) { + return (values[slot] - currentReaderValues[doc]); + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT + .getBytes(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT + .getBytes(reader, field); + } + + public int sortType() { + return SortField.BYTE; + } + + public Comparable value(int slot) { + return new Byte(values[slot]); + } + }; + + public static final class DocComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final int[] docIDs; + private int docBase; + + DocComparator(int numHits) { + docIDs = new int[numHits]; + } + + public int compare(int slot1, int slot2) { + return docIDs[slot1] - docIDs[slot2]; + } + + public int compare(int slot, int doc, float score) { + return docIDs[slot] - docBase - doc; + } + + public void copy(int slot, int doc, float score) { + docIDs[slot] = docBase + doc; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) { + // TODO: can we "map" our docIDs to the current + // reader? saves having to then subtract on every + // compare call + this.docBase = docBase; + } + + public int sortType() { + return SortField.DOC; + } + + public Comparable value(int slot) { + return new Integer(docIDs[slot]); + } + }; + + public static final class DoubleComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final double[] values; + private double[] currentReaderValues; + private final String field; + private DoubleParser parser; + + DoubleComparator(int numHits, String field, FieldCache.Parser parser) { + values = new double[numHits]; + this.field = field; + this.parser = (DoubleParser) parser; + } + + public int compare(int slot1, int slot2) { + if (values[slot1] > values[slot2]) + return 1; + if (values[slot1] < values[slot2]) + return -1; + return 0; + } + + public int compare(int slot, int doc, float score) { + if (values[slot] > currentReaderValues[doc]) + return 1; + if (values[slot] < currentReaderValues[doc]) + return -1; + return 0; + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT + .getDoubles(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT + .getDoubles(reader, field); + } + + public int sortType() { + return SortField.DOUBLE; + } + + public Comparable value(int slot) { + return new Double(values[slot]); + } + }; + + public static final class FloatComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final float[] values; + private float[] currentReaderValues; + private final String field; + private FloatParser parser; + + FloatComparator(int numHits, String field, FieldCache.Parser parser) { + values = new float[numHits]; + this.field = field; + this.parser = (FloatParser) parser; + } + + public int compare(int slot1, int slot2) { + if (values[slot1] > values[slot2]) + return 1; + if (values[slot1] < values[slot2]) + return -1; + return 0; + } + + public int compare(int slot, int doc, float score) { + float val = currentReaderValues[doc]; + if (values[slot] > val) + return 1; + if (values[slot] < val) + return -1; + return 0; + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = parser != null ? FieldCache.DEFAULT.getFloats( + reader, field, parser) : FieldCache.DEFAULT.getFloats(reader, field); + } + + public int sortType() { + return SortField.FLOAT; + } + + public Comparable value(int slot) { + return new Float(values[slot]); + } + }; + + public static final class IntComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final int[] values; + private int[] currentReaderValues; + private final String field; + private IntParser parser; + + IntComparator(int numHits, String field, FieldCache.Parser parser) { + values = new int[numHits]; + this.field = field; + this.parser = (IntParser) parser; + } + + public int compare(int slot1, int slot2) { + if (values[slot1] > values[slot2]) + return 1; + if (values[slot1] < values[slot2]) + return -1; + return 0; + } + + public int compare(int slot, int doc, float score) { + return values[slot] - currentReaderValues[doc]; + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = parser != null ? FieldCache.DEFAULT.getInts(reader, + field, parser) : FieldCache.DEFAULT.getInts(reader, field); + } + + public int sortType() { + return SortField.INT; + } + + public Comparable value(int slot) { + return new Integer(values[slot]); + } + }; + + public static final class LongComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final long[] values; + private long[] currentReaderValues; + private final String field; + private LongParser parser; + + LongComparator(int numHits, String field, FieldCache.Parser parser) { + values = new long[numHits]; + this.field = field; + this.parser = (LongParser) parser; + } + + public int compare(int slot1, int slot2) { + if (values[slot1] > values[slot2]) + return 1; + if (values[slot1] < values[slot2]) + return -1; + return 0; + } + + public int compare(int slot, int doc, float score) { + if (values[slot] > currentReaderValues[doc]) + return 1; + if (values[slot] < currentReaderValues[doc]) + return -1; + return 0; + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT + .getLongs(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT + .getLongs(reader, field); + } + + public int sortType() { + return SortField.LONG; + } + + public Comparable value(int slot) { + return new Long(values[slot]); + } + }; + + public static final class RelevanceComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final float[] scores; + + RelevanceComparator(int numHits) { + scores = new float[numHits]; + } + + public int compare(int slot1, int slot2) { + float score1 = scores[slot1]; + float score2 = scores[slot2]; + if (score1 > score2) + return -1; + if (score1 < score2) + return 1; + return 0; + } + + public int compare(int slot, int doc, float score) { + float slotScore = scores[slot]; + if (slotScore > score) + return -1; + if (slotScore < score) + return 1; + return 0; + } + + public void copy(int slot, int doc, float score) { + scores[slot] = score; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) { + } + + public int sortType() { + return SortField.SCORE; + } + + public Comparable value(int slot) { + return new Float(scores[slot]); + } + }; + + public static final class ShortComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + + private final short[] values; + private short[] currentReaderValues; + private final String field; + private ShortParser parser; + + ShortComparator(int numHits, String field, FieldCache.Parser parser) { + values = new short[numHits]; + this.field = field; + this.parser = (ShortParser) parser; + } + + public int compare(int slot1, int slot2) { + if (values[slot1] > values[slot2]) + return 1; + if (values[slot1] < values[slot2]) + return -1; + return 0; + } + + public int compare(int slot, int doc, float score) { + if (values[slot] > currentReaderValues[doc]) + return 1; + if (values[slot] < currentReaderValues[doc]) + return -1; + return 0; + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT + .getShorts(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT + .getShorts(reader, field); + } + + public int sortType() { + return SortField.BYTE; + } + + public Comparable value(int slot) { + return new Short(values[slot]); + } + }; + + public static final class StringComparatorLocale extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private final String[] values; + private String[] currentReaderValues; + private final String field; + final Collator collator; + + StringComparatorLocale(int numHits, String field, Locale locale) { + values = new String[numHits]; + this.field = field; + collator = Collator.getInstance(locale); + } + + public int compare(int slot1, int slot2) { + final String val1 = values[slot1]; + final String val2 = values[slot2]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + return collator.compare(val1, val2); + } + + public int compare(int slot, int doc, float score) { + return values[slot].compareTo(currentReaderValues[doc]); + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStrings(reader, + field); + } + + public int sortType() { + return SortField.STRING; + } + + public Comparable value(int slot) { + return values[slot]; + } + }; + + /** + * This Comparator can only be used a single IndexReader or the first IndexReader in a series + * of IndexReaders. + */ + public static final class StringOrdComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private int[] subords; + final int[] ords; + String[] values; + String[] lookup; + private int[] order; + final String field; + + StringOrdComparator(String field, String[] values, int[] ords, int[] subords) { + this.field = field; + this.values = values; + this.ords = ords; + this.subords = subords; + } + + StringOrdComparator(int numHits, String field) { + ords = new int[numHits]; + values = new String[numHits]; + subords = new int[numHits]; + this.field = field; + } + + public int compare(int slot1, int slot2) { + return ords[slot1] - ords[slot2]; + } + + public int compare(int slot, int doc, float score) { + int ord = order[doc]; + return ords[slot] - ord; + } + + public void copy(int slot, int doc, float score) { + int ord = order[doc]; + ords[slot] = ord; + subords[slot] = 0; + values[slot] = lookup[ord]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT + .getStringIndex(reader, field); + + lookup = currentReaderValues.lookup; + order = currentReaderValues.order; + } + + public int sortType() { + return SortField.STRING_ORD; + } + + public Comparable value(int slot) { + return values[slot]; + } + } + + /** + * This Comparator can only be used a single IndexReader or the first IndexReader in a series + * of IndexReaders. + */ + public static final class StringOrd2Comparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private int[] subords; + final int[] ords; + String[] values; + String[] lookup; + private int[] order; + final String field; + + StringOrd2Comparator(String field, String[] values, int[] ords, int[] subords) { + this.field = field; + this.values = values; + this.ords = ords; + this.subords = subords; + } + + StringOrd2Comparator(int numHits, String field) { + ords = new int[numHits]; + values = new String[numHits]; + subords = new int[numHits]; + this.field = field; + } + + private int bottomOrd; + + public void setBottom(int slot) { + bottomOrd = ords[slot]; + } + + public int compareBottom(int doc, float score) { + return bottomOrd - order[doc]; + } + + public int compare(int slot1, int slot2) { + return ords[slot1] - ords[slot2]; + } + + public int compare(int slot, int doc, float score) { + int ord = order[doc]; + return ords[slot] - ord; + } + + public void copy(int slot, int doc, float score) { + int ord = order[doc]; + ords[slot] = ord; + subords[slot] = 0; + values[slot] = lookup[ord]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT + .getStringIndex(reader, field); + + lookup = currentReaderValues.lookup; + order = currentReaderValues.order; + } + + public int sortType() { + return SortField.STRING_ORD; + } + + public Comparable value(int slot) { + return values[slot]; + } + } + + public static final class StringOrdSubOrdComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private int[] subords; + final int[] ords; + String[] values; + String[] lookup; + private int[] order; + final String field; + + StringOrdSubOrdComparator(String field, String[] values, int[] ords, int[] subords) { + this.field = field; + this.values = values; + this.ords = ords; + this.subords = subords; + } + + StringOrdSubOrdComparator(int numHits, String field) { + ords = new int[numHits]; + values = new String[numHits]; + subords = new int[numHits]; + this.field = field; + } + + public int compare(int slot1, int slot2) { + int cmp = ords[slot1] - ords[slot2]; + if (cmp != 0) { + return cmp; + } + return subords[slot1] - subords[slot2]; + } + + public int compare(int slot, int doc, float score) { + int ord = order[doc]; + final int cmp = ords[slot] - ord; + if (cmp != 0) { return cmp;} + return 1; + } + + public void copy(int slot, int doc, float score) { + int ord = order[doc]; + ords[slot] = ord; + subords[slot] = 0; + values[slot] = lookup[ord]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + // Map ords in queue to ords in reader + + StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT + .getStringIndex(reader, field); + + lookup = currentReaderValues.lookup; + order = currentReaderValues.order; + + if (lookup.length == 0) { + return; + } + Map map = new HashMap(); + for (int i = 0; i < numSlotsFull; i++) { + String val = values[i]; + if (val == null) { + subords[i] = 0; + ords[i] = 0; + continue; + } + + int index = binarySearch(lookup, val); + + if (index < 0) { + index = -index - 2; + + Integer intOrd = Integer.valueOf(index); + List slotVals = (List) map.get(intOrd); + if (slotVals == null) { + slotVals = new ArrayList(); + slotVals.add(new SlotValPair(i, val)); + map.put(intOrd, slotVals); + } else { + slotVals.add(new SlotValPair(i, val)); + } + + } else { + subords[i] = 0; + } + ords[i] = index; + } + + Iterator dit = map.values().iterator(); + while (dit.hasNext()) { + List list = (List) dit.next(); + if (list.size() > 1) { + Collections.sort(list); + int sz = list.size(); + int cnt = 1; + SlotValPair svp = (SlotValPair) list.get(0); + String last = svp.val; + for (int i = 1; i < sz; i++) { + if(last.equals(svp.val) ) { + subords[svp.i] = cnt; + } else { + subords[svp.i] = ++cnt; + } + last = svp.val; + svp = (SlotValPair) list.get(i); + } + if(last.equals(svp.val) ) { + subords[svp.i] = cnt; + } else { + subords[svp.i] = ++cnt; + } + } else { + SlotValPair svp = (SlotValPair) list.get(0); + subords[svp.i] = 1; + } + } + + } + + public int sortType() { + return SortField.STRING_ORD; + } + + public Comparable value(int slot) { + return values[slot]; + } + + class SlotValPair implements Comparable { + public SlotValPair(int i, String val) { + this.val = val; + this.i = i; + } + + String val; + int i; + + public int compareTo(Object o) { + SlotValPair odoub = (SlotValPair) o; + return val.compareTo(odoub.val); + } + + public String toString() { + return "i:" + i + " val:" + val; + } + } + } + + public static final class StringOrdValOnDemComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private final int[] ords; + String[] values; + private final int[] readerIndex; + private int currentReaderIndex = -1; + private String[] lookup; + private int length; + private int[] order; + private final String field; + + public StringOrdValOnDemComparator(String field, String[] values, int[] ords, int currentReaderIndex) { + this.field = field; + this.values = values; + this.ords = ords; + readerIndex = new int[values.length]; + } + + StringOrdValOnDemComparator(int numHits, String field) { + ords = new int[numHits]; + values = new String[numHits]; + readerIndex = new int[numHits]; + this.field = field; + } + + public int compare(int slot1, int slot2) { + if (readerIndex[slot1] != currentReaderIndex) { + convert(slot1); + } + if (readerIndex[slot2] != currentReaderIndex) { + convert(slot2); + } + int cmp = ords[slot1] - ords[slot2]; + if (cmp != 0) { + return cmp; + } + final String val1 = values[slot1]; + final String val2 = values[slot2]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + + return val1.compareTo(val2); + } + + public int compare(int slot, int doc, float score) { + if (readerIndex[slot] != currentReaderIndex) { + convert(slot); + } + int ord = ords[slot]; + int order = this.order[doc]; + final int cmp = ord - order; + if (cmp != 0) { + return cmp; + } + + final String val1 = values[slot]; + final String val2 = lookup[order]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + return val1.compareTo(val2); + } + + private void convert(int slot) { + readerIndex[slot] = currentReaderIndex; + if (length == 0) { + return; + } + + int index = 0; + String value = values[slot]; + if (value == null) { + ords[slot] = 0; + return; + } + index = binarySearch(lookup, value); + if (index < 0) { + index = -index - 2; + } + ords[slot] = index; + } + + public void copy(int slot, int doc, float score) { + int ord = order[doc]; + ords[slot] = ord; + values[slot] = lookup[ord]; + readerIndex[slot] = currentReaderIndex; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT + .getStringIndex(reader, field); + order = currentReaderValues.order; + lookup = currentReaderValues.lookup; + currentReaderIndex++; + length = lookup.length; + } + + public int sortType() { + return SortField.STRING_ORD_VAL; + } + + public Comparable value(int slot) { + return values[slot]; + } + + }; + + public static final class StringOrdValOnDem2Comparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private final int[] ords; + String[] values; + private final int[] readerIndex; + private int currentReaderIndex = -1; + private String[] lookup; + private int length; + private int[] order; + private final String field; + + private int bottomOrd; + private String bottomVal; + private int bottomSlot; + + public StringOrdValOnDem2Comparator(String field, String[] values, int[] ords, int currentReaderIndex) { + this.field = field; + this.values = values; + this.ords = ords; + readerIndex = new int[values.length]; + } + + StringOrdValOnDem2Comparator(int numHits, String field) { + ords = new int[numHits]; + values = new String[numHits]; + readerIndex = new int[numHits]; + this.field = field; + } + + public void setBottom(int slot) { + assert readerIndex[slot] == currentReaderIndex; + bottomOrd = ords[slot]; + bottomVal = values[slot]; + bottomSlot = slot; + } + + public int compareBottom(int doc, float score) { + final int order = this.order[doc]; + int cmp = bottomOrd - order; + if (cmp != 0) { + return cmp; + } + + final String val1 = bottomVal; + final String val2 = lookup[order]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + return val1.compareTo(val2); + } + + public int compare(int slot1, int slot2) { + if (readerIndex[slot1] != currentReaderIndex) { + convert(slot1); + } + if (readerIndex[slot2] != currentReaderIndex) { + convert(slot2); + } + int cmp = ords[slot1] - ords[slot2]; + if (cmp != 0) { + return cmp; + } + final String val1 = values[slot1]; + final String val2 = values[slot2]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + + return val1.compareTo(val2); + } + + public int compare(int slot, int doc, float score) { + if (readerIndex[slot] != currentReaderIndex) { + convert(slot); + } + int ord = ords[slot]; + int order = this.order[doc]; + final int cmp = ord - order; + if (cmp != 0) { + return cmp; + } + + final String val1 = values[slot]; + final String val2 = lookup[order]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + return val1.compareTo(val2); + } + + private void convert(int slot) { + readerIndex[slot] = currentReaderIndex; + if (length == 0) { + return; + } + + int index = 0; + String value = values[slot]; + if (value == null) { + ords[slot] = 0; + return; + } + index = binarySearch(lookup, value); + if (index < 0) { + index = -index - 2; + } + ords[slot] = index; + } + + public void copy(int slot, int doc, float score) { + int ord = order[doc]; + ords[slot] = ord; + values[slot] = lookup[ord]; + readerIndex[slot] = currentReaderIndex; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT + .getStringIndex(reader, field); + order = currentReaderValues.order; + lookup = currentReaderValues.lookup; + currentReaderIndex++; + length = lookup.length; + bottomOrd = ords[bottomSlot]; + } + + public int sortType() { + return SortField.STRING_ORD_VAL; + } + + public Comparable value(int slot) { + return values[slot]; + } + + }; + + public static final class StringOrdValComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private final int[] ords; + private final String[] values; + private String[] lookup; + private int[] order; + private final String field; + + StringOrdValComparator(int numHits, String field) { + ords = new int[numHits]; + values = new String[numHits]; + this.field = field; + } + + public int compare(int slot1, int slot2) { + int cmp = ords[slot1] - ords[slot2]; + if (cmp != 0) { return cmp;} + final String val1 = values[slot1]; + final String val2 = values[slot2]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + return val1.compareTo(val2); + } + + public int compare(int slot, int doc, float score) { + final int ord = order[doc]; + int cmp = ords[slot] - ord; + if (cmp != 0) { return cmp;} + final String val1 = values[slot]; + final String val2 = lookup[ord]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + + return val1.compareTo(val2); + } + + public void copy(int slot, int doc, float score) { + int ord = order[doc]; + ords[slot] = ord; + values[slot] = lookup[ord]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT + .getStringIndex(reader, field); + + order = currentReaderValues.order; + lookup = currentReaderValues.lookup; + + int index = 0; + for (int i = 0; i < numSlotsFull; i++) { + String value = values[i]; + if (value == null) { + continue; + } + + index = binarySearch(lookup, value); + + if (index < 0) { + index = -index - 2; + } + ords[i] = index; + } + } + + public int sortType() { + return SortField.STRING_ORD_VAL; + } + + public Comparable value(int slot) { + return values[slot]; + } + + }; + + public static final class StringValComparator extends FieldComparator { + + // nocommit -- maybe "setcurrentscoredoc"? + private String[] values; + private String[] currentReaderValues; + private final String field; + + StringValComparator(int numHits, String field) { + values = new String[numHits]; + this.field = field; + } + + public int compare(int slot1, int slot2) { + final String val1 = values[slot1]; + final String val2 = values[slot2]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + + return val1.compareTo(val2); + } + + public int compare(int slot, int doc, float score) { + final String val1 = values[slot]; + final String val2 = currentReaderValues[doc]; + if (val1 == null) { + if (val2 == null) { + return 0; + } + return -1; + } else if (val2 == null) { + return 1; + } + return val1.compareTo(val2); + } + + public void copy(int slot, int doc, float score) { + values[slot] = currentReaderValues[doc]; + } + + public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { + currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStrings(reader, + field); + } + + public int sortType() { + return SortField.STRING_VAL; + } + + public Comparable value(int slot) { + return values[slot]; + } + }; + + private static int binarySearch(String[] a, String key) { + int low = 0; + int high = a.length - 1; + + while (low <= high) { + int mid = (low + high) >>> 1; + String midVal = a[mid]; + int cmp; + if (midVal != null) { + cmp = midVal.compareTo(key); + } else { + cmp = -1; + } + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; + } + return -(low + 1); + }; + + /** + * Compare hit at slot1 with hit at slot2. + * + * @param slot1 + * @param slot2 + * @return + */ + public abstract int compare(int slot1, int slot2); + + // nocommit -- allowed to return -X or only -1 (and X, 1)??? + /** + * Compare hit at slot with hit (doc,score). + * + * @param slot hit slot + * @param doc doc in currentIndexReader + * @param score hit score + * @return + */ + public abstract int compare(int slot, int doc, float score); + + /** + * Copy hit (doc,score) to hit slot. + * + * @param slot + * @param doc + * @param score + */ + public abstract void copy(int slot, int doc, float score); + + /** + * Set a new Reader. All doc correspond to the current Reader. + * + * @param reader + * @param objects + * @throws IOException + * @throws IOException + */ + public abstract void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException; + + /** + * @return SortField.TYPE + */ + public abstract int sortType(); + + /** + * Return the actual value at slot. + * + * @param slot the value + * @return + */ + public abstract Comparable value(int slot); + + private int bottomSlot; + + public void setBottom(int slot) { + bottomSlot = slot; + } + + public int compareBottom(int doc, float score) { + return compare(bottomSlot, doc, score); + } +} Property changes on: src/java/org/apache/lucene/search/FieldComparator.java ___________________________________________________________________ Name: svn:eol-style + native Index: src/java/org/apache/lucene/search/SortField.java =================================================================== --- src/java/org/apache/lucene/search/SortField.java (revision 732086) +++ src/java/org/apache/lucene/search/SortField.java (working copy) @@ -17,9 +17,14 @@ * limitations under the License. */ +import java.io.IOException; import java.io.Serializable; import java.util.Locale; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.FieldComparator.StringOrdValOnDemComparator; +import org.apache.lucene.search.FieldComparator.StringOrdValOnDem2Comparator; + /** * Stores information about how to sort documents by terms in an individual * field. Fields must be indexed in order to sort by them. @@ -80,8 +85,23 @@ * Sort using term values as encoded bytes. Sort values are bytes and lower values are at the front */ public static final int BYTE = 10; + + public static final int STRING_ORD = 11; + + public static final int STRING_VAL = 12; + + public static final int STRING_ORD_VAL = 13; + + public static final int STRING_ORD_VAL_DEM = 14; + + public static final int STRING_ORD_VAL_DEM_WIN = 15; + public static final int STRING_ORD_SUBORD = 16; + public static final int STRING_ORD_VAL_DEM2 = 17; + + public static final int STRING_ORD2 = 18; + // IMPLEMENTATION NOTE: the FieldCache.STRING_INDEX is in the same "namespace" // as the above static int values. Any new values must not have the same value // as FieldCache.STRING_INDEX. @@ -101,6 +121,10 @@ private SortComparatorSource factory; private FieldCache.Parser parser; + private FieldComparatorSource comparatorSource; + + private boolean useLegacy = false; // remove in Lucene 3.0 + /** Creates a sort by terms in the given field where the type of term value * is determined dynamically ({@link #AUTO AUTO}). * @param field Name of field to sort by, cannot be null. @@ -206,22 +230,44 @@ /** Creates a sort with a custom comparison function. * @param field Name of field to sort by; cannot be null. * @param comparator Returns a comparator for sorting hits. + * @deprecated use SortField (String field, FieldComparatorSource comparator) */ public SortField (String field, SortComparatorSource comparator) { initFieldType(field, CUSTOM); this.factory = comparator; } + + /** Creates a sort with a custom comparison function. + * @param field Name of field to sort by; cannot be null. + * @param comparator Returns a comparator for sorting hits. + */ + public SortField (String field, FieldComparatorSource comparator) { + initFieldType(field, CUSTOM); + this.comparatorSource = comparator; + } /** Creates a sort, possibly in reverse, with a custom comparison function. * @param field Name of field to sort by; cannot be null. * @param comparator Returns a comparator for sorting hits. * @param reverse True if natural order should be reversed. + * @deprecated use SortField (String field, FieldComparatorSource comparator, boolean reverse) */ public SortField (String field, SortComparatorSource comparator, boolean reverse) { initFieldType(field, CUSTOM); this.reverse = reverse; this.factory = comparator; } + + /** Creates a sort, possibly in reverse, with a custom comparison function. + * @param field Name of field to sort by; cannot be null. + * @param comparator Returns a comparator for sorting hits. + * @param reverse True if natural order should be reversed. + */ + public SortField (String field, FieldComparatorSource comparator, boolean reverse) { + initFieldType(field, CUSTOM); + this.reverse = reverse; + this.comparatorSource = comparator; + } // Sets field & type, and ensures field is not NULL unless // type is SCORE or DOC @@ -273,9 +319,36 @@ return reverse; } + /** + * @deprecated use {@link #getComparatorSource()} + */ public SortComparatorSource getFactory() { return factory; } + + public FieldComparatorSource getComparatorSource() { + return comparatorSource; + } + + /** + * Use legacy IndexSearch semantics: search with a MultiSegmentReader rather + * than passing a single hit collector to multiple SegmentReaders. + * + * @param legacy true for legacy behavior + * @deprecated will be removed in Lucene 3.0. + */ + public void setUseLegacySearch(boolean legacy) { + this.useLegacy = legacy; + } + + /** + * @return if true, IndexSearch will use legacy sorting search semantics. + * eg. multiple Priority Queues. + * @deprecated will be removed in Lucene 3.0. + */ + public boolean getUseLegacySearch() { + return this.useLegacy; + } public String toString() { StringBuffer buffer = new StringBuffer(); @@ -333,4 +406,200 @@ if (parser != null) hash += parser.hashCode()^0x3aaf56ff; return hash; } + + ComparatorPolicy getComparatorPolicy(IndexReader[] subReaders, final int numHits) throws IOException { + switch (type) { + case SortField.SCORE: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.RelevanceComparator(numHits); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.DOC: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.DocComparator(numHits); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.INT: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.IntComparator(numHits, field, parser); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.FLOAT: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.FloatComparator(numHits, field, parser); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.LONG: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.LongComparator(numHits, field, parser); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.DOUBLE: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.DoubleComparator(numHits, field, parser); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.BYTE: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.ByteComparator(numHits, field, parser); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.SHORT: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.ShortComparator(numHits, field, parser); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + case SortField.CUSTOM: + return new ComparatorPolicy(){ + private FieldComparator comparator = comparatorSource.newComparator(field); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + case SortField.STRING: + if (locale != null) { + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringComparatorLocale(numHits, field, locale); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + } + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrdSubOrdComparator(numHits, field);; + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + case SortField.STRING_VAL: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringValComparator(numHits, field); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + case SortField.STRING_ORD: + if (locale != null) { + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringComparatorLocale(numHits, field, locale); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + } + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrdComparator(numHits, field); + private boolean first = true; + private boolean second = true; + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + if(first) { + first = false; + return comparator; + } else if(second){ + StringOrdValOnDemComparator comp = new FieldComparator.StringOrdValOnDemComparator(numHits, field); + comp.values = ((FieldComparator.StringOrdComparator)comparator).values; + comparator = comp; + second = false; + return comp; + } + return comparator; + }}; + + case SortField.STRING_ORD2: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrd2Comparator(numHits, field); + private boolean first = true; + private boolean second = true; + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + if(first) { + first = false; + return comparator; + } else if(second){ + StringOrdValOnDem2Comparator comp = new FieldComparator.StringOrdValOnDem2Comparator(numHits, field); + comp.values = ((FieldComparator.StringOrd2Comparator)comparator).values; + comparator = comp; + second = false; + return comp; + } + return comparator; + }}; + + case SortField.STRING_ORD_SUBORD: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrdSubOrdComparator(numHits, field); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + case SortField.STRING_ORD_VAL: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrdValComparator(numHits, field); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + case SortField.STRING_ORD_VAL_DEM: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrdValOnDemComparator(numHits, field); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + case SortField.STRING_ORD_VAL_DEM2: + return new ComparatorPolicy(){ + private FieldComparator comparator = new FieldComparator.StringOrdValOnDem2Comparator(numHits, field); + public FieldComparator nextComparator(FieldComparator oldComparator, + IndexReader reader, int numHits, int numSlotsFull) + throws IOException { + return comparator; + }}; + + default: + throw new IllegalStateException("Illegal sort type: " + type); + } + + } + } Index: src/java/org/apache/lucene/search/FieldValueHitQueue.java =================================================================== --- src/java/org/apache/lucene/search/FieldValueHitQueue.java (revision 0) +++ src/java/org/apache/lucene/search/FieldValueHitQueue.java (revision 0) @@ -0,0 +1,228 @@ +package org.apache.lucene.search; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermEnum; +import org.apache.lucene.util.PriorityQueue; + +import java.io.IOException;; + +/** + * Expert: A hit queue for sorting by hits by terms in more than one field. + * Uses FieldCache.DEFAULT for maintaining + * internal term lookup tables. + * + * NOTE: This API is experimental and might change in + * incompatible ways in the next release. + * + * @since lucene 2.9 + * @version $Id: + * @see Searcher#search(Query,Filter,int,Sort) + * @see FieldCache + */ +public class FieldValueHitQueue extends PriorityQueue { + + final static class Entry { + int slot; + int subReaderIndex; + int subDocID; + float score; + Entry(int slot, int subReaderIndex, int subDocID, float score) { + this.slot = slot; + this.subReaderIndex = subReaderIndex; + this.subDocID = subDocID; + this.score = score; + } + + public String toString() { + return "slot:" + slot + " subR:" + subReaderIndex + " subid:" + subDocID; + } + } + + // nocommit + private static boolean first = true; + + /** + * Creates a hit queue sorted by the given list of fields. + * @param reader + * @param fields Fieldable names, in priority order (highest priority first). Cannot be null or empty. + * @param size The number of hits to retain. Must be greater than zero. + * @throws IOException + */ + public FieldValueHitQueue(SortField[] fields, int size, IndexReader[] subReaders) throws IOException { + numComparators = fields.length; + comparators = new FieldComparator[numComparators]; + comparatorPolicies = new ComparatorPolicy[numComparators]; + reverseMul = new int[numComparators]; + + this.fields = fields; + for (int i=0; ia is less relevant than b. + * @param a ScoreDoc + * @param b ScoreDoc + * @return true if document a should be sorted after document b. + */ + protected boolean lessThan (final Object a, final Object b) { + final Entry hitA = (Entry) a; + final Entry hitB = (Entry) b; + + if (numComparators == 1) { + // Common case + final int c = reverseMul1 * comparator1.compare (hitA.slot, hitB.slot); + if (c != 0) { + return c > 0; + } + } else { + // run comparators + for (int i=0; i 0; + } + } + } + + // avoid random sort order that could lead to duplicates (bug #31241): + return hitA.subReaderIndex > hitB.subReaderIndex || + (hitA.subReaderIndex == hitB.subReaderIndex && + hitA.subDocID > hitB.subDocID); + } + + + /** + * Given a FieldDoc object, stores the values used + * to sort the given document. These values are not the raw + * values out of the index, but the internal representation + * of them. This is so the given search hit can be collated + * by a MultiSearcher with other search hits. + * @param doc The FieldDoc to store sort values into. + * @return The same FieldDoc passed in. + * @see Searchable#search(Weight,Filter,int,Sort) + */ + FieldDoc fillFields (final Entry entry, int[] starts) { + final int n = comparators.length; + final Comparable[] fields = new Comparable[n]; + for (int i=0; i 1.0f) doc.score /= maxscore; // normalize scores + return new FieldDoc(entry.subDocID + starts[entry.subReaderIndex], + entry.score, + fields); + } + + + /** Returns the SortFields being used by this hit queue. */ + SortField[] getFields() { + return fields; + } + + /** + * Attempts to detect the given field type for an IndexReader. + */ + static int detectFieldType(IndexReader reader, String fieldKey) throws IOException { + String field = ((String)fieldKey).intern(); + TermEnum enumerator = reader.terms (new Term (field)); + try { + Term term = enumerator.term(); + if (term == null) { + throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type"); + } + int ret = 0; + if (term.field() == field) { + String termtext = term.text().trim(); + + /** + * Java 1.4 level code: + + if (pIntegers.matcher(termtext).matches()) + return IntegerSortedHitQueue.comparator (reader, enumerator, field); + + else if (pFloats.matcher(termtext).matches()) + return FloatSortedHitQueue.comparator (reader, enumerator, field); + */ + + // Java 1.3 level code: + try { + Integer.parseInt (termtext); + ret = SortField.INT; + } catch (NumberFormatException nfe1) { + try { + Long.parseLong(termtext); + ret = SortField.LONG; + } catch (NumberFormatException nfe2) { + try { + Float.parseFloat (termtext); + ret = SortField.FLOAT; + } catch (NumberFormatException nfe3) { + ret = SortField.STRING; + } + } + } + } else { + throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed"); + } + return ret; + } finally { + enumerator.close(); + } + } + + ComparatorPolicy[] getComparatorPolicies() { + return comparatorPolicies; + } + + //void setReversMulti1(int rev) { + void setReverseMul1(int rev) { + this.reverseMul1 = rev; + } + + void setComparator1(FieldComparator comp) { + this.comparator1 = comp; + } +} Property changes on: src/java/org/apache/lucene/search/FieldValueHitQueue.java ___________________________________________________________________ Name: svn:eol-style + native