mirror of https://github.com/apache/lucene.git
LUCENE-5325: Add LongValuesSource and DoubleValuesSource in core
This commit is contained in:
parent
87b6c2c8fc
commit
3f24fd81c8
|
@ -89,6 +89,11 @@ New features
|
|||
http://blog.mikemccandless.com/2012/04/lucenes-tokenstreams-are-actually.html
|
||||
for details. (Mike McCandless)
|
||||
|
||||
* LUCENE-5325: Added LongValuesSource and DoubleValuesSource, intended as
|
||||
type-safe replacements for ValueSource in the queries module. These
|
||||
expose per-segment LongValues or DoubleValues iterators, similar to the
|
||||
existing DocValues iterator API. (Alan Woodward, Adrien Grand)
|
||||
|
||||
Bug Fixes
|
||||
|
||||
* LUCENE-7547: JapaneseTokenizerFactory was failing to close the
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Per-segment, per-document double values, which can be calculated at search-time
|
||||
*/
|
||||
public abstract class DoubleValues {
|
||||
|
||||
/**
|
||||
* Get the double value for the current document
|
||||
*/
|
||||
public abstract double doubleValue() throws IOException;
|
||||
|
||||
/**
|
||||
* Advance this instance to the given document id
|
||||
* @return true if there is a value for this document
|
||||
*/
|
||||
public abstract boolean advanceExact(int doc) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* 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 java.util.Objects;
|
||||
import java.util.function.DoubleToLongFunction;
|
||||
import java.util.function.LongToDoubleFunction;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
|
||||
/**
|
||||
* Base class for producing {@link DoubleValues}
|
||||
*
|
||||
* To obtain a {@link DoubleValues} object for a leaf reader, clients should
|
||||
* call {@link #getValues(LeafReaderContext, DoubleValues)}.
|
||||
*
|
||||
* DoubleValuesSource objects for NumericDocValues fields can be obtained by calling
|
||||
* {@link #fromDoubleField(String)}, {@link #fromFloatField(String)}, {@link #fromIntField(String)}
|
||||
* or {@link #fromLongField(String)}, or from {@link #fromField(String, LongToDoubleFunction)} if
|
||||
* special long-to-double encoding is required.
|
||||
*
|
||||
* Scores may be used as a source for value calculations by wrapping a {@link Scorer} using
|
||||
* {@link #fromScorer(Scorer)} and passing the resulting DoubleValues to {@link #getValues(LeafReaderContext, DoubleValues)}.
|
||||
* The scores can then be accessed using the {@link #SCORES} DoubleValuesSource.
|
||||
*/
|
||||
public abstract class DoubleValuesSource {
|
||||
|
||||
/**
|
||||
* Returns a {@link DoubleValues} instance for the passed-in LeafReaderContext and scores
|
||||
*
|
||||
* If scores are not needed to calculate the values (ie {@link #needsScores() returns false}, callers
|
||||
* may safely pass {@code null} for the {@code scores} parameter.
|
||||
*/
|
||||
public abstract DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException;
|
||||
|
||||
/**
|
||||
* Return true if document scores are needed to calculate values
|
||||
*/
|
||||
public abstract boolean needsScores();
|
||||
|
||||
/**
|
||||
* Create a sort field based on the value of this producer
|
||||
* @param reverse true if the sort should be decreasing
|
||||
*/
|
||||
public SortField getSortField(boolean reverse) {
|
||||
return new DoubleValuesSortField(this, reverse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to a LongValuesSource by casting the double values to longs
|
||||
*/
|
||||
public final LongValuesSource toLongValuesSource() {
|
||||
return new LongValuesSource() {
|
||||
@Override
|
||||
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||
DoubleValues in = DoubleValuesSource.this.getValues(ctx, scores);
|
||||
return new LongValues() {
|
||||
@Override
|
||||
public long longValue() throws IOException {
|
||||
return (long) in.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return in.advanceExact(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return DoubleValuesSource.this.needsScores();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DoubleValuesSource that wraps a generic NumericDocValues field
|
||||
*
|
||||
* @param field the field to wrap, must have NumericDocValues
|
||||
* @param decoder a function to convert the long-valued doc values to doubles
|
||||
*/
|
||||
public static DoubleValuesSource fromField(String field, LongToDoubleFunction decoder) {
|
||||
return new FieldValuesSource(field, decoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DoubleValuesSource that wraps a double-valued field
|
||||
*/
|
||||
public static DoubleValuesSource fromDoubleField(String field) {
|
||||
return fromField(field, Double::longBitsToDouble);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DoubleValuesSource that wraps a float-valued field
|
||||
*/
|
||||
public static DoubleValuesSource fromFloatField(String field) {
|
||||
return fromField(field, (v) -> (double)Float.intBitsToFloat((int)v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DoubleValuesSource that wraps a long-valued field
|
||||
*/
|
||||
public static DoubleValuesSource fromLongField(String field) {
|
||||
return fromField(field, (v) -> (double) v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DoubleValuesSource that wraps an int-valued field
|
||||
*/
|
||||
public static DoubleValuesSource fromIntField(String field) {
|
||||
return fromLongField(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* A DoubleValuesSource that exposes a document's score
|
||||
*
|
||||
* If this source is used as part of a values calculation, then callers must not
|
||||
* pass {@code null} as the {@link DoubleValues} parameter on {@link #getValues(LeafReaderContext, DoubleValues)}
|
||||
*/
|
||||
public static final DoubleValuesSource SCORES = new DoubleValuesSource() {
|
||||
@Override
|
||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||
assert scores != null;
|
||||
return scores;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a DoubleValues instance that wraps scores returned by a Scorer
|
||||
*/
|
||||
public static DoubleValues fromScorer(Scorer scorer) {
|
||||
return new DoubleValues() {
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
return scorer.score();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
assert scorer.docID() == doc;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class FieldValuesSource extends DoubleValuesSource {
|
||||
|
||||
final String field;
|
||||
final LongToDoubleFunction decoder;
|
||||
|
||||
private FieldValuesSource(String field, LongToDoubleFunction decoder) {
|
||||
this.field = field;
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FieldValuesSource that = (FieldValuesSource) o;
|
||||
return Objects.equals(field, that.field) &&
|
||||
Objects.equals(decoder, that.decoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(field, decoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||
final NumericDocValues values = DocValues.getNumeric(ctx.reader(), field);
|
||||
return toDoubleValues(values, decoder::applyAsDouble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleValuesSortField extends SortField {
|
||||
|
||||
final DoubleValuesSource producer;
|
||||
|
||||
public DoubleValuesSortField(DoubleValuesSource producer, boolean reverse) {
|
||||
super(producer.toString(), new DoubleValuesComparatorSource(producer), reverse);
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return producer.needsScores();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buffer = new StringBuilder("<");
|
||||
buffer.append(getField()).append(">");
|
||||
if (reverse)
|
||||
buffer.append("!");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DoubleValuesHolder {
|
||||
DoubleValues values;
|
||||
}
|
||||
|
||||
private static class DoubleValuesComparatorSource extends FieldComparatorSource {
|
||||
private final DoubleValuesSource producer;
|
||||
|
||||
public DoubleValuesComparatorSource(DoubleValuesSource producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldComparator<Double> newComparator(String fieldname, int numHits,
|
||||
int sortPos, boolean reversed) throws IOException {
|
||||
return new FieldComparator.DoubleComparator(numHits, fieldname, 0.0){
|
||||
|
||||
LeafReaderContext ctx;
|
||||
DoubleValuesHolder holder = new DoubleValuesHolder();
|
||||
|
||||
@Override
|
||||
protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException {
|
||||
ctx = context;
|
||||
return asNumericDocValues(holder, Double::doubleToLongBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScorer(Scorer scorer) throws IOException {
|
||||
holder.values = producer.getValues(ctx, fromScorer(scorer));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static DoubleValues toDoubleValues(NumericDocValues in, LongToDoubleFunction map) {
|
||||
return new DoubleValues() {
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
return map.applyAsDouble(in.longValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int target) throws IOException {
|
||||
return in.advanceExact(target);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private static NumericDocValues asNumericDocValues(DoubleValuesHolder in, DoubleToLongFunction converter) {
|
||||
return new NumericDocValues() {
|
||||
@Override
|
||||
public long longValue() throws IOException {
|
||||
return converter.applyAsLong(in.values.doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int target) throws IOException {
|
||||
return in.values.advanceExact(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int docID() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextDoc() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int advance(int target) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long cost() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Per-segment, per-document long values, which can be calculated at search-time
|
||||
*/
|
||||
public abstract class LongValues {
|
||||
|
||||
/**
|
||||
* Get the long value for the current document
|
||||
*/
|
||||
public abstract long longValue() throws IOException;
|
||||
|
||||
/**
|
||||
* Advance this instance to the given document id
|
||||
* @return true if there is a value for this document
|
||||
*/
|
||||
public abstract boolean advanceExact(int doc) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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 java.util.Objects;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
|
||||
/**
|
||||
* Base class for producing {@link LongValues}
|
||||
*
|
||||
* To obtain a {@link LongValues} object for a leaf reader, clients should
|
||||
* call {@link #getValues(LeafReaderContext, DoubleValues)}.
|
||||
*
|
||||
* LongValuesSource objects for long and int-valued NumericDocValues fields can
|
||||
* be obtained by calling {@link #fromLongField(String)} and {@link #fromIntField(String)}.
|
||||
*
|
||||
* To obtain a LongValuesSource from a float or double-valued NumericDocValues field,
|
||||
* use {@link DoubleValuesSource#fromFloatField(String)} or {@link DoubleValuesSource#fromDoubleField(String)}
|
||||
* and then call {@link DoubleValuesSource#toLongValuesSource()}.
|
||||
*/
|
||||
public abstract class LongValuesSource {
|
||||
|
||||
/**
|
||||
* Returns a {@link LongValues} instance for the passed-in LeafReaderContext and scores
|
||||
*
|
||||
* If scores are not needed to calculate the values (ie {@link #needsScores() returns false}, callers
|
||||
* may safely pass {@code null} for the {@code scores} parameter.
|
||||
*/
|
||||
public abstract LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException;
|
||||
|
||||
/**
|
||||
* Return true if document scores are needed to calculate values
|
||||
*/
|
||||
public abstract boolean needsScores();
|
||||
|
||||
/**
|
||||
* Create a sort field based on the value of this producer
|
||||
* @param reverse true if the sort should be decreasing
|
||||
*/
|
||||
public SortField getSortField(boolean reverse) {
|
||||
return new LongValuesSortField(this, reverse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LongValuesSource that wraps a long-valued field
|
||||
*/
|
||||
public static LongValuesSource fromLongField(String field) {
|
||||
return new FieldValuesSource(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LongValuesSource that wraps an int-valued field
|
||||
*/
|
||||
public static LongValuesSource fromIntField(String field) {
|
||||
return fromLongField(field);
|
||||
}
|
||||
|
||||
private static class FieldValuesSource extends LongValuesSource {
|
||||
|
||||
final String field;
|
||||
|
||||
private FieldValuesSource(String field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FieldValuesSource that = (FieldValuesSource) o;
|
||||
return Objects.equals(field, that.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||
final NumericDocValues values = DocValues.getNumeric(ctx.reader(), field);
|
||||
return toLongValues(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongValuesSortField extends SortField {
|
||||
|
||||
final LongValuesSource producer;
|
||||
|
||||
public LongValuesSortField(LongValuesSource producer, boolean reverse) {
|
||||
super(producer.toString(), new LongValuesComparatorSource(producer), reverse);
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return producer.needsScores();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buffer = new StringBuilder("<");
|
||||
buffer.append(getField()).append(">");
|
||||
if (reverse)
|
||||
buffer.append("!");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class LongValuesHolder {
|
||||
LongValues values;
|
||||
}
|
||||
|
||||
private static class LongValuesComparatorSource extends FieldComparatorSource {
|
||||
private final LongValuesSource producer;
|
||||
|
||||
public LongValuesComparatorSource(LongValuesSource producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldComparator<Long> newComparator(String fieldname, int numHits,
|
||||
int sortPos, boolean reversed) throws IOException {
|
||||
return new FieldComparator.LongComparator(numHits, fieldname, 0L){
|
||||
|
||||
LeafReaderContext ctx;
|
||||
LongValuesHolder holder = new LongValuesHolder();
|
||||
|
||||
@Override
|
||||
protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException {
|
||||
ctx = context;
|
||||
return asNumericDocValues(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScorer(Scorer scorer) throws IOException {
|
||||
holder.values = producer.getValues(ctx, DoubleValuesSource.fromScorer(scorer));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static LongValues toLongValues(NumericDocValues in) {
|
||||
return new LongValues() {
|
||||
@Override
|
||||
public long longValue() throws IOException {
|
||||
return in.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int target) throws IOException {
|
||||
return in.advanceExact(target);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private static NumericDocValues asNumericDocValues(LongValuesHolder in) {
|
||||
return new NumericDocValues() {
|
||||
@Override
|
||||
public long longValue() throws IOException {
|
||||
return in.values.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int target) throws IOException {
|
||||
return in.values.advanceExact(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int docID() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextDoc() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int advance(int target) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long cost() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.DoubleDocValuesField;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FloatDocValuesField;
|
||||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.RandomIndexWriter;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.English;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
|
||||
public class TestDoubleValuesSource extends LuceneTestCase {
|
||||
|
||||
private Directory dir;
|
||||
private IndexReader reader;
|
||||
private IndexSearcher searcher;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dir = newDirectory();
|
||||
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||
int numDocs = TestUtil.nextInt(random(), 2049, 4000);
|
||||
for (int i = 0; i < numDocs; i++) {
|
||||
Document document = new Document();
|
||||
document.add(newTextField("english", English.intToEnglish(i), Field.Store.NO));
|
||||
document.add(newTextField("oddeven", (i % 2 == 0) ? "even" : "odd", Field.Store.NO));
|
||||
document.add(new NumericDocValuesField("int", random().nextInt()));
|
||||
document.add(new NumericDocValuesField("long", random().nextLong()));
|
||||
document.add(new FloatDocValuesField("float", random().nextFloat()));
|
||||
document.add(new DoubleDocValuesField("double", random().nextDouble()));
|
||||
iw.addDocument(document);
|
||||
}
|
||||
reader = iw.getReader();
|
||||
iw.close();
|
||||
searcher = newSearcher(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
reader.close();
|
||||
dir.close();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testSimpleFieldEquivalences() throws Exception {
|
||||
checkSorts(new MatchAllDocsQuery(), new Sort(new SortField("int", SortField.Type.INT, random().nextBoolean())));
|
||||
checkSorts(new MatchAllDocsQuery(), new Sort(new SortField("long", SortField.Type.LONG, random().nextBoolean())));
|
||||
checkSorts(new MatchAllDocsQuery(), new Sort(new SortField("float", SortField.Type.FLOAT, random().nextBoolean())));
|
||||
checkSorts(new MatchAllDocsQuery(), new Sort(new SortField("double", SortField.Type.DOUBLE, random().nextBoolean())));
|
||||
}
|
||||
|
||||
public void testHashCodeAndEquals() {
|
||||
DoubleValuesSource vs1 = DoubleValuesSource.fromDoubleField("double");
|
||||
DoubleValuesSource vs2 = DoubleValuesSource.fromDoubleField("double");
|
||||
assertEquals(vs1, vs2);
|
||||
assertEquals(vs1.hashCode(), vs2.hashCode());
|
||||
DoubleValuesSource v3 = DoubleValuesSource.fromLongField("long");
|
||||
assertFalse(vs1.equals(v3));
|
||||
}
|
||||
|
||||
public void testSimpleFieldSortables() throws Exception {
|
||||
int n = atLeast(4);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Sort sort = randomSort();
|
||||
checkSorts(new MatchAllDocsQuery(), sort);
|
||||
checkSorts(new TermQuery(new Term("english", "one")), sort);
|
||||
}
|
||||
}
|
||||
|
||||
Sort randomSort() throws Exception {
|
||||
boolean reversed = random().nextBoolean();
|
||||
SortField fields[] = new SortField[] {
|
||||
new SortField("int", SortField.Type.INT, reversed),
|
||||
new SortField("long", SortField.Type.LONG, reversed),
|
||||
new SortField("float", SortField.Type.FLOAT, reversed),
|
||||
new SortField("double", SortField.Type.DOUBLE, reversed),
|
||||
new SortField("score", SortField.Type.SCORE)
|
||||
};
|
||||
Collections.shuffle(Arrays.asList(fields), random());
|
||||
int numSorts = TestUtil.nextInt(random(), 1, fields.length);
|
||||
return new Sort(Arrays.copyOfRange(fields, 0, numSorts));
|
||||
}
|
||||
|
||||
// Take a Sort, and replace any field sorts with Sortables
|
||||
Sort convertSortToSortable(Sort sort) {
|
||||
SortField original[] = sort.getSort();
|
||||
SortField mutated[] = new SortField[original.length];
|
||||
for (int i = 0; i < mutated.length; i++) {
|
||||
if (random().nextInt(3) > 0) {
|
||||
SortField s = original[i];
|
||||
boolean reverse = s.getType() == SortField.Type.SCORE || s.getReverse();
|
||||
switch (s.getType()) {
|
||||
case INT:
|
||||
mutated[i] = DoubleValuesSource.fromIntField(s.getField()).getSortField(reverse);
|
||||
break;
|
||||
case LONG:
|
||||
mutated[i] = DoubleValuesSource.fromLongField(s.getField()).getSortField(reverse);
|
||||
break;
|
||||
case FLOAT:
|
||||
mutated[i] = DoubleValuesSource.fromFloatField(s.getField()).getSortField(reverse);
|
||||
break;
|
||||
case DOUBLE:
|
||||
mutated[i] = DoubleValuesSource.fromDoubleField(s.getField()).getSortField(reverse);
|
||||
break;
|
||||
case SCORE:
|
||||
mutated[i] = DoubleValuesSource.SCORES.getSortField(reverse);
|
||||
break;
|
||||
default:
|
||||
mutated[i] = original[i];
|
||||
}
|
||||
} else {
|
||||
mutated[i] = original[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new Sort(mutated);
|
||||
}
|
||||
|
||||
void checkSorts(Query query, Sort sort) throws Exception {
|
||||
int size = TestUtil.nextInt(random(), 1, searcher.getIndexReader().maxDoc() / 5);
|
||||
TopDocs expected = searcher.search(query, size, sort, random().nextBoolean(), random().nextBoolean());
|
||||
Sort mutatedSort = convertSortToSortable(sort);
|
||||
TopDocs actual = searcher.search(query, size, mutatedSort, random().nextBoolean(), random().nextBoolean());
|
||||
|
||||
CheckHits.checkEqual(query, expected.scoreDocs, actual.scoreDocs);
|
||||
|
||||
if (size < actual.totalHits) {
|
||||
expected = searcher.searchAfter(expected.scoreDocs[size-1], query, size, sort);
|
||||
actual = searcher.searchAfter(actual.scoreDocs[size-1], query, size, mutatedSort);
|
||||
CheckHits.checkEqual(query, expected.scoreDocs, actual.scoreDocs);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.RandomIndexWriter;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.English;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
|
||||
public class TestLongValuesSource extends LuceneTestCase {
|
||||
|
||||
private Directory dir;
|
||||
private IndexReader reader;
|
||||
private IndexSearcher searcher;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dir = newDirectory();
|
||||
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||
int numDocs = TestUtil.nextInt(random(), 2049, 4000);
|
||||
for (int i = 0; i < numDocs; i++) {
|
||||
Document document = new Document();
|
||||
document.add(newTextField("english", English.intToEnglish(i), Field.Store.NO));
|
||||
document.add(newTextField("oddeven", (i % 2 == 0) ? "even" : "odd", Field.Store.NO));
|
||||
document.add(new NumericDocValuesField("int", random().nextInt()));
|
||||
document.add(new NumericDocValuesField("long", random().nextLong()));
|
||||
iw.addDocument(document);
|
||||
}
|
||||
reader = iw.getReader();
|
||||
iw.close();
|
||||
searcher = newSearcher(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
reader.close();
|
||||
dir.close();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testSimpleFieldEquivalences() throws Exception {
|
||||
checkSorts(new MatchAllDocsQuery(), new Sort(new SortField("int", SortField.Type.INT, random().nextBoolean())));
|
||||
checkSorts(new MatchAllDocsQuery(), new Sort(new SortField("long", SortField.Type.LONG, random().nextBoolean())));
|
||||
}
|
||||
|
||||
public void testHashCodeAndEquals() {
|
||||
LongValuesSource vs1 = LongValuesSource.fromLongField("long");
|
||||
LongValuesSource vs2 = LongValuesSource.fromLongField("long");
|
||||
assertEquals(vs1, vs2);
|
||||
assertEquals(vs1.hashCode(), vs2.hashCode());
|
||||
LongValuesSource v3 = LongValuesSource.fromLongField("int");
|
||||
assertFalse(vs1.equals(v3));
|
||||
}
|
||||
|
||||
public void testSimpleFieldSortables() throws Exception {
|
||||
int n = atLeast(4);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Sort sort = randomSort();
|
||||
checkSorts(new MatchAllDocsQuery(), sort);
|
||||
checkSorts(new TermQuery(new Term("english", "one")), sort);
|
||||
}
|
||||
}
|
||||
|
||||
Sort randomSort() throws Exception {
|
||||
boolean reversed = random().nextBoolean();
|
||||
SortField fields[] = new SortField[] {
|
||||
new SortField("int", SortField.Type.INT, reversed),
|
||||
new SortField("long", SortField.Type.LONG, reversed)
|
||||
};
|
||||
Collections.shuffle(Arrays.asList(fields), random());
|
||||
int numSorts = TestUtil.nextInt(random(), 1, fields.length);
|
||||
return new Sort(Arrays.copyOfRange(fields, 0, numSorts));
|
||||
}
|
||||
|
||||
// Take a Sort, and replace any field sorts with Sortables
|
||||
Sort convertSortToSortable(Sort sort) {
|
||||
SortField original[] = sort.getSort();
|
||||
SortField mutated[] = new SortField[original.length];
|
||||
for (int i = 0; i < mutated.length; i++) {
|
||||
if (random().nextInt(3) > 0) {
|
||||
SortField s = original[i];
|
||||
boolean reverse = s.getType() == SortField.Type.SCORE || s.getReverse();
|
||||
switch (s.getType()) {
|
||||
case INT:
|
||||
mutated[i] = LongValuesSource.fromIntField(s.getField()).getSortField(reverse);
|
||||
break;
|
||||
case LONG:
|
||||
mutated[i] = LongValuesSource.fromLongField(s.getField()).getSortField(reverse);
|
||||
break;
|
||||
default:
|
||||
mutated[i] = original[i];
|
||||
}
|
||||
} else {
|
||||
mutated[i] = original[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new Sort(mutated);
|
||||
}
|
||||
|
||||
void checkSorts(Query query, Sort sort) throws Exception {
|
||||
int size = TestUtil.nextInt(random(), 1, searcher.getIndexReader().maxDoc() / 5);
|
||||
Sort mutatedSort = convertSortToSortable(sort);
|
||||
TopDocs actual = searcher.search(query, size, mutatedSort, random().nextBoolean(), random().nextBoolean());
|
||||
TopDocs expected = searcher.search(query, size, sort, random().nextBoolean(), random().nextBoolean());
|
||||
|
||||
CheckHits.checkEqual(query, expected.scoreDocs, actual.scoreDocs);
|
||||
|
||||
if (size < actual.totalHits) {
|
||||
expected = searcher.searchAfter(expected.scoreDocs[size-1], query, size, sort);
|
||||
actual = searcher.searchAfter(actual.scoreDocs[size-1], query, size, mutatedSort);
|
||||
CheckHits.checkEqual(query, expected.scoreDocs, actual.scoreDocs);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue