/** * 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.solr.search; import org.apache.lucene.search.*; import org.apache.lucene.index.IndexReader; import java.io.IOException; public class MissingStringLastComparatorSource extends FieldComparatorSource { public static final String bigString="\uffff\uffff\uffff\uffff\uffff\uffff\uffff\uffffNULL_VAL"; private final String missingValueProxy; public MissingStringLastComparatorSource() { this(bigString); } /** Creates a {@link FieldComparatorSource} that uses <tt>missingValueProxy</tt> as the value to return from ScoreDocComparator.sortValue() * which is only used my multisearchers to determine how to collate results from their searchers. * * @param missingValueProxy The value returned when sortValue() is called for a document missing the sort field. * This value is *not* normally used for sorting, but used to create */ public MissingStringLastComparatorSource(String missingValueProxy) { this.missingValueProxy=missingValueProxy; } @Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException { return new MissingLastOrdComparator(numHits, fieldname, sortPos, reversed, missingValueProxy); } } // Copied from Lucene and modified since the Lucene version couldn't // be extended or have it's values accessed. class MissingLastOrdComparator extends FieldComparator<String> { private static final int NULL_ORD = Integer.MAX_VALUE; private final String nullVal; private final int[] ords; private final String[] values; private final int[] readerGen; private int currentReaderGen = -1; private String[] lookup; private int[] order; private final String field; private int bottomSlot = -1; private int bottomOrd; private String bottomValue; private final boolean reversed; private final int sortPos; public MissingLastOrdComparator(int numHits, String field, int sortPos, boolean reversed, String nullVal) { ords = new int[numHits]; values = new String[numHits]; readerGen = new int[numHits]; this.sortPos = sortPos; this.reversed = reversed; this.field = field; this.nullVal = nullVal; } @Override public int compare(int slot1, int slot2) { if (readerGen[slot1] == readerGen[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); } @Override public int compareValues(String first, String second) { if (first == null) { if (second == null) { return 0; } else { return 1; } } else if (second == null) { return -1; } else { return first.compareTo(second); } } @Override public int compareBottom(int doc) { assert bottomSlot != -1; int order = this.order[doc]; int ord = (order == 0) ? NULL_ORD : order; final int cmp = bottomOrd - ord; if (cmp != 0) { return cmp; } final String val2 = lookup[order]; // take care of the case where both vals are null if (bottomValue == val2) return 0; return bottomValue.compareTo(val2); } private void convert(int slot) { readerGen[slot] = currentReaderGen; int index = 0; String value = values[slot]; if (value == null) { // should already be done // ords[slot] = NULL_ORD; return; } if (sortPos == 0 && bottomSlot != -1 && bottomSlot != slot) { // Since we are the primary sort, the entries in the // queue are bounded by bottomOrd: assert bottomOrd < lookup.length; if (reversed) { index = binarySearch(lookup, value, bottomOrd, lookup.length-1); } else { index = binarySearch(lookup, value, 0, bottomOrd); } } else { // Full binary search index = binarySearch(lookup, value); } if (index < 0) { index = -index - 2; } ords[slot] = index; } @Override public void copy(int slot, int doc) { final int ord = order[doc]; ords[slot] = ord == 0 ? NULL_ORD : ord; assert ord >= 0; values[slot] = lookup[ord]; readerGen[slot] = currentReaderGen; } @Override public void setNextReader(IndexReader reader, int docBase) throws IOException { FieldCache.StringIndex currentReaderValues = FieldCache.DEFAULT.getStringIndex(reader, field); currentReaderGen++; order = currentReaderValues.order; lookup = currentReaderValues.lookup; assert lookup.length > 0; if (bottomSlot != -1) { convert(bottomSlot); bottomOrd = ords[bottomSlot]; } } @Override public void setBottom(final int bottom) { bottomSlot = bottom; if (readerGen[bottom] != currentReaderGen) { convert(bottomSlot); } bottomOrd = ords[bottom]; assert bottomOrd >= 0; // assert bottomOrd < lookup.length; bottomValue = values[bottom]; } @Override public String value(int slot) { String v = values[slot]; return v==null ? nullVal : v; } @Override public double docValue(int doc) { return 0.0f; } public String[] getValues() { return values; } public int getBottomSlot() { return bottomSlot; } public String getField() { return field; } }