blob: 705454eb67bd8050fec19d249bcd33424cb4663f [file] [log] [blame]
/*
* 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.search;
import java.io.IOException;
import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.FilterNumericDocValues;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.util.NumericUtils;
/**
* Selects a value from the document's list to use as the representative value
* <p>
* This provides a NumericDocValues view over the SortedNumeric, for use with sorting,
* expressions, function queries, etc.
*/
public class SortedNumericSelector {
/**
* Type of selection to perform.
*/
public enum Type {
/**
* Selects the minimum value in the set
*/
MIN,
/**
* Selects the maximum value in the set
*/
MAX,
// TODO: we could do MEDIAN in constant time (at most 2 lookups)
}
/**
* Wraps a multi-valued SortedNumericDocValues as a single-valued view, using the specified selector
* and numericType.
*/
public static NumericDocValues wrap(SortedNumericDocValues sortedNumeric, Type selector, SortField.Type numericType) {
if (numericType != SortField.Type.INT &&
numericType != SortField.Type.LONG &&
numericType != SortField.Type.FLOAT &&
numericType != SortField.Type.DOUBLE) {
throw new IllegalArgumentException("numericType must be a numeric type");
}
final NumericDocValues view;
NumericDocValues singleton = DocValues.unwrapSingleton(sortedNumeric);
if (singleton != null) {
// it's actually single-valued in practice, but indexed as multi-valued,
// so just sort on the underlying single-valued dv directly.
// regardless of selector type, this optimization is safe!
view = singleton;
} else {
switch(selector) {
case MIN:
view = new MinValue(sortedNumeric);
break;
case MAX:
view = new MaxValue(sortedNumeric);
break;
default:
throw new AssertionError();
}
}
// undo the numericutils sortability
switch(numericType) {
case FLOAT:
return new FilterNumericDocValues(view) {
@Override
public long longValue() throws IOException {
return NumericUtils.sortableFloatBits((int) in.longValue());
}
};
case DOUBLE:
return new FilterNumericDocValues(view) {
@Override
public long longValue() throws IOException {
return NumericUtils.sortableDoubleBits(in.longValue());
}
};
default:
return view;
}
}
/** Wraps a SortedNumericDocValues and returns the first value (min) */
static class MinValue extends NumericDocValues {
final SortedNumericDocValues in;
private long value;
MinValue(SortedNumericDocValues in) {
this.in = in;
}
@Override
public int docID() {
return in.docID();
}
@Override
public int nextDoc() throws IOException {
int docID = in.nextDoc();
if (docID != NO_MORE_DOCS) {
value = in.nextValue();
}
return docID;
}
@Override
public int advance(int target) throws IOException {
int docID = in.advance(target);
if (docID != NO_MORE_DOCS) {
value = in.nextValue();
}
return docID;
}
@Override
public boolean advanceExact(int target) throws IOException {
if (in.advanceExact(target)) {
value = in.nextValue();
return true;
}
return false;
}
@Override
public long cost() {
return in.cost();
}
@Override
public long longValue() {
return value;
}
}
/** Wraps a SortedNumericDocValues and returns the last value (max) */
static class MaxValue extends NumericDocValues {
final SortedNumericDocValues in;
private long value;
MaxValue(SortedNumericDocValues in) {
this.in = in;
}
@Override
public int docID() {
return in.docID();
}
private void setValue() throws IOException {
int count = in.docValueCount();
for(int i=0;i<count;i++) {
value = in.nextValue();
}
}
@Override
public int nextDoc() throws IOException {
int docID = in.nextDoc();
if (docID != NO_MORE_DOCS) {
setValue();
}
return docID;
}
@Override
public int advance(int target) throws IOException {
int docID = in.advance(target);
if (docID != NO_MORE_DOCS) {
setValue();
}
return docID;
}
@Override
public boolean advanceExact(int target) throws IOException {
if (in.advanceExact(target)) {
setValue();
return true;
}
return false;
}
@Override
public long cost() {
return in.cost();
}
@Override
public long longValue() {
return value;
}
}
}