diff --git a/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java b/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java new file mode 100644 index 0000000..42855f0 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java @@ -0,0 +1,30 @@ +/* + * 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; + +/** + * Per-segment, per-document double values, which can be calculated at search-time + */ +public abstract class DoubleValues { + + /** + * Get the double value for a document + */ + public abstract double get(int doc); + +} diff --git a/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java b/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java new file mode 100644 index 0000000..2e25b19 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java @@ -0,0 +1,153 @@ +/* + * 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.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} + */ +public abstract class DoubleValuesSource { + + /** + * Returns a {@link DoubleValues} instance for the passed-in LeafReaderContext + */ + public abstract DoubleValues getValues(LeafReaderContext ctx) throws IOException; + + /** + * 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); + } + + /** + * Creates a DoubleValuesSource that wraps a double-valued field + */ + public static DoubleValuesSource fromDoubleField(String field) { + return fromDoubleField(field, Double::longBitsToDouble); + } + + public static DoubleValuesSource fromFloatField(String field) { + return fromDoubleField(field, (v) -> (double)Float.intBitsToFloat((int)v)); + } + + /** + * Expose a NumericDocValuesField as doubles + */ + public static DoubleValuesSource fromDoubleField(String field, LongToDoubleFunction decoder) { + return new FieldValuesSource(field, decoder); + } + + 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) throws IOException { + final NumericDocValues values = DocValues.getNumeric(ctx.reader(), field); + return new DoubleValues() { + @Override + public double get(int docID) { + long input = values.get(docID); + return decoder.applyAsDouble(input); + } + }; + } + } + + public static DoubleValuesSource fromLongField(String field) { + return fromDoubleField(field, (v) -> (double)(long)v); + } + + 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 String toString() { + StringBuilder buffer = new StringBuilder(getField()); + if (reverse) + buffer.append("!"); + return buffer.toString(); + } + + } + + private static class DoubleValuesComparatorSource extends FieldComparatorSource { + private final DoubleValuesSource producer; + + public DoubleValuesComparatorSource(DoubleValuesSource producer) { + this.producer = producer; + } + + @Override + public FieldComparator newComparator(String fieldname, int numHits, + int sortPos, boolean reversed) throws IOException { + return new FieldComparator.DoubleComparator(numHits, fieldname, 0.0){ + + DoubleValues values; + + @Override + protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException { + this.values = producer.getValues(context); + return new NumericDocValues() { + @Override + public long get(int docID) { + return Double.doubleToLongBits(values.get(docID)); + } + }; + } + + }; + } + } + +} diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDoubleDocValues.java b/lucene/core/src/test/org/apache/lucene/search/TestDoubleDocValues.java new file mode 100644 index 0000000..9532580 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/search/TestDoubleDocValues.java @@ -0,0 +1,145 @@ +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.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; +import org.junit.Test; + +public class TestDoubleDocValues 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(); + } + + @Test + 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); + BooleanQuery.Builder bq = new BooleanQuery.Builder(); + bq.add(new TermQuery(new Term("english", "one")), BooleanClause.Occur.SHOULD); + bq.add(new TermQuery(new Term("oddeven", "even")), BooleanClause.Occur.SHOULD); + checkSorts(bq.build(), sort); + // force in order + bq.add(new TermQuery(new Term("english", "two")), BooleanClause.Occur.SHOULD); + bq.setMinimumNumberShouldMatch(2); + checkSorts(bq.build(), 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: + case LONG: + mutated[i] = DoubleValuesSource.fromLongField(s.getField()).getSortField(reverse); + break; + case FLOAT: + case DOUBLE: + mutated[i] = DoubleValuesSource.fromDoubleField(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); + 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); + } + } +} diff --git a/lucene/expressions/build.xml b/lucene/expressions/build.xml index 1dddc6d..61ae64f 100644 --- a/lucene/expressions/build.xml +++ b/lucene/expressions/build.xml @@ -26,7 +26,6 @@ - @@ -35,16 +34,6 @@ - - - - - - - - - - diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java b/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java index 5ec2edb..809e6ba 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java @@ -16,7 +16,7 @@ */ package org.apache.lucene.expressions; -import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.DoubleValuesSource; /** * Binds variable names in expressions to actual data. @@ -35,10 +35,6 @@ public abstract class Bindings { /** * Returns a ValueSource bound to the variable name. */ - public abstract ValueSource getValueSource(String name); - - /** Returns a {@code ValueSource} over relevance scores */ - protected final ValueSource getScoreValueSource() { - return new ScoreValueSource(); - } + public abstract DoubleValuesSource getValueSource(String name); + } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java b/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java index 02be23b..04aafd7 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java @@ -16,9 +16,9 @@ */ package org.apache.lucene.expressions; -import org.apache.lucene.expressions.js.JavascriptCompiler; // javadocs -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.expressions.js.JavascriptCompiler; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.Rescorer; import org.apache.lucene.search.SortField; @@ -66,17 +66,17 @@ public abstract class Expression { * Evaluates the expression for the given document. * * @param document docId of the document to compute a value for - * @param functionValues {@link FunctionValues} for each element of {@link #variables}. + * @param functionValues {@link DoubleValues} for each element of {@link #variables}. * @return The computed value of the expression for the given document. */ - public abstract double evaluate(int document, FunctionValues[] functionValues); + public abstract double evaluate(int document, DoubleValues[] functionValues); /** * Get a value source which can compute the value of this expression in the context of the given bindings. * @param bindings Bindings to use for external values in this expression * @return A value source which will evaluate this expression when used */ - public ValueSource getValueSource(Bindings bindings) { + public DoubleValuesSource getValueSource(Bindings bindings) { return new ExpressionValueSource(bindings, this); } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionComparator.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionComparator.java index eabf6dd..b65fb1d 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionComparator.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionComparator.java @@ -17,12 +17,10 @@ package org.apache.lucene.expressions; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.FieldComparator; import org.apache.lucene.search.LeafFieldComparator; import org.apache.lucene.search.Scorer; @@ -33,28 +31,18 @@ class ExpressionComparator extends FieldComparator implements LeafFieldC private double bottom; private double topValue; - private ValueSource source; - private FunctionValues scores; - private LeafReaderContext readerContext; + private DoubleValuesSource source; + private DoubleValues scores; - public ExpressionComparator(ValueSource source, int numHits) { + public ExpressionComparator(DoubleValuesSource source, int numHits) { values = new double[numHits]; this.source = source; } - - // TODO: change FieldComparator.setScorer to throw IOException and remove this try-catch + @Override public void setScorer(Scorer scorer) { - // TODO: might be cleaner to lazy-init 'source' and set scorer after? - assert readerContext != null; - try { - Map context = new HashMap<>(); - assert scorer != null; - context.put("scorer", scorer); - scores = source.getValues(context, readerContext); - } catch (IOException e) { - throw new RuntimeException(e); - } + assert scores != null; + ExpressionFunctionValues.setScorer(scores, scorer); } @Override @@ -74,17 +62,17 @@ class ExpressionComparator extends FieldComparator implements LeafFieldC @Override public int compareBottom(int doc) throws IOException { - return Double.compare(bottom, scores.doubleVal(doc)); + return Double.compare(bottom, scores.get(doc)); } @Override public void copy(int slot, int doc) throws IOException { - values[slot] = scores.doubleVal(doc); + values[slot] = scores.get(doc); } @Override public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException { - this.readerContext = context; + this.scores = source.getValues(context); return this; } @@ -95,6 +83,6 @@ class ExpressionComparator extends FieldComparator implements LeafFieldC @Override public int compareTop(int doc) throws IOException { - return Double.compare(topValue, scores.doubleVal(doc)); + return Double.compare(topValue, scores.get(doc)); } } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionFunctionValues.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionFunctionValues.java index ad195cd..9bfcdf2 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionFunctionValues.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionFunctionValues.java @@ -16,20 +16,18 @@ */ package org.apache.lucene.expressions; -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; -import org.apache.lucene.queries.function.docvalues.DoubleDocValues; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.Scorer; -/** A {@link FunctionValues} which evaluates an expression */ -class ExpressionFunctionValues extends DoubleDocValues { +/** A {@link DoubleValues} which evaluates an expression */ +class ExpressionFunctionValues extends ScoringValues { final Expression expression; - final FunctionValues[] functionValues; + final DoubleValues[] functionValues; int currentDocument = -1; double currentValue; - ExpressionFunctionValues(ValueSource parent, Expression expression, FunctionValues[] functionValues) { - super(parent); + ExpressionFunctionValues(Expression expression, DoubleValues[] functionValues) { if (expression == null) { throw new NullPointerException(); } @@ -39,9 +37,9 @@ class ExpressionFunctionValues extends DoubleDocValues { this.expression = expression; this.functionValues = functionValues; } - + @Override - public double doubleVal(int document) { + public double get(int document) { if (currentDocument != document) { currentDocument = document; currentValue = expression.evaluate(document, functionValues); @@ -49,4 +47,12 @@ class ExpressionFunctionValues extends DoubleDocValues { return currentValue; } + + @Override + public void setScorer(Scorer scorer) { + for (DoubleValues subValues : functionValues) { + setScorer(subValues, scorer); + } + } + } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java index 33e8428..ef32e9f 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java @@ -20,13 +20,11 @@ package org.apache.lucene.expressions; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.ReaderUtil; -import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.DoubleValues; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Rescorer; @@ -49,7 +47,7 @@ class ExpressionRescorer extends SortRescorer { private final Expression expression; private final Bindings bindings; - /** Uses the provided {@link ValueSource} to assign second + /** Uses the provided {@link org.apache.lucene.search.DoubleValuesSource} to assign second * pass scores. */ public ExpressionRescorer(Expression expression, Bindings bindings) { super(new Sort(expression.getSortField(bindings, true))); @@ -65,18 +63,16 @@ class ExpressionRescorer extends SortRescorer { int subReader = ReaderUtil.subIndex(docID, leaves); LeafReaderContext readerContext = leaves.get(subReader); int docIDInSegment = docID - readerContext.docBase; - Map context = new HashMap<>(); FakeScorer fakeScorer = new FakeScorer(); fakeScorer.score = firstPassExplanation.getValue(); fakeScorer.doc = docIDInSegment; - context.put("scorer", fakeScorer); - List subs = new ArrayList<>(Arrays.asList(superExpl.getDetails())); for(String variable : expression.variables) { - subs.add(Explanation.match((float) bindings.getValueSource(variable).getValues(context, readerContext).doubleVal(docIDInSegment), - "variable \"" + variable + "\"")); + DoubleValues values = bindings.getValueSource(variable).getValues(readerContext); + ScoringValues.setScorer(values, fakeScorer); + subs.add(Explanation.match((float) values.get(docIDInSegment), "variable \"" + variable + "\"")); } return Explanation.match(superExpl.getValue(), superExpl.getDescription(), subs); diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java index fcba455..e42b1ab 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java @@ -22,16 +22,15 @@ import java.util.HashMap; import java.util.Map; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.SortField; /** - * A {@link ValueSource} which evaluates a {@link Expression} given the context of an {@link Bindings}. + * A {@link DoubleValuesSource} which evaluates a {@link Expression} given the context of an {@link Bindings}. */ -@SuppressWarnings({"rawtypes", "unchecked"}) -final class ExpressionValueSource extends ValueSource { - final ValueSource variables[]; +final class ExpressionValueSource extends DoubleValuesSource { + final DoubleValuesSource variables[]; final Expression expression; final boolean needsScores; @@ -39,10 +38,10 @@ final class ExpressionValueSource extends ValueSource { if (bindings == null) throw new NullPointerException(); if (expression == null) throw new NullPointerException(); this.expression = expression; - variables = new ValueSource[expression.variables.length]; + variables = new DoubleValuesSource[expression.variables.length]; boolean needsScores = false; for (int i = 0; i < variables.length; i++) { - ValueSource source = bindings.getValueSource(expression.variables[i]); + DoubleValuesSource source = bindings.getValueSource(expression.variables[i]); if (source instanceof ScoreValueSource) { needsScores = true; } else if (source instanceof ExpressionValueSource) { @@ -58,20 +57,14 @@ final class ExpressionValueSource extends ValueSource { } @Override - public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException { - Map valuesCache = (Map)context.get("valuesCache"); - if (valuesCache == null) { - valuesCache = new HashMap<>(); - context = new HashMap(context); - context.put("valuesCache", valuesCache); - } - FunctionValues[] externalValues = new FunctionValues[expression.variables.length]; - + public DoubleValues getValues(LeafReaderContext readerContext) throws IOException { + Map valuesCache = new HashMap<>(); + DoubleValues[] externalValues = new DoubleValues[expression.variables.length]; for (int i = 0; i < variables.length; ++i) { String externalName = expression.variables[i]; - FunctionValues values = valuesCache.get(externalName); + DoubleValues values = valuesCache.get(externalName); if (values == null) { - values = variables[i].getValues(context, readerContext); + values = variables[i].getValues(readerContext); if (values == null) { throw new RuntimeException("Internal error. External (" + externalName + ") does not exist."); } @@ -80,7 +73,7 @@ final class ExpressionValueSource extends ValueSource { externalValues[i] = values; } - return new ExpressionFunctionValues(this, expression, externalValues); + return new ExpressionFunctionValues(expression, externalValues); } @Override @@ -89,7 +82,7 @@ final class ExpressionValueSource extends ValueSource { } @Override - public String description() { + public String toString() { return "expr(" + expression.sourceText + ")"; } @@ -132,7 +125,7 @@ final class ExpressionValueSource extends ValueSource { return true; } - boolean needsScores() { + public boolean needsScores() { return needsScores; } } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java index e310c06..387e710 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java @@ -18,25 +18,25 @@ package org.apache.lucene.expressions; import java.io.IOException; -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; -import org.apache.lucene.queries.function.docvalues.DoubleDocValues; +import org.apache.lucene.search.DoubleValues; import org.apache.lucene.search.Scorer; /** - * A utility class to allow expressions to access the score as a {@link FunctionValues}. + * A utility class to allow expressions to access the score as a {@link DoubleValues}. */ -class ScoreFunctionValues extends DoubleDocValues { - final Scorer scorer; +class ScoreFunctionValues extends ScoringValues { - ScoreFunctionValues(ValueSource parent, Scorer scorer) { - super(parent); + Scorer scorer; + + @Override + public void setScorer(Scorer scorer) { this.scorer = scorer; } @Override - public double doubleVal(int document) { + public double get(int document) { try { + assert scorer != null; assert document == scorer.docID(); return scorer.score(); } catch (IOException exception) { diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreValueSource.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreValueSource.java index ea1669c..d88e010 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreValueSource.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreValueSource.java @@ -17,31 +17,29 @@ package org.apache.lucene.expressions; +import java.io.IOException; + import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.Scorer; -import java.io.IOException; -import java.util.Map; - /** - * A {@link ValueSource} which uses the {@link Scorer} passed through + * A {@link DoubleValuesSource} which uses the {@link Scorer} passed through * the context map by {@link ExpressionComparator}. */ -@SuppressWarnings({"rawtypes"}) -class ScoreValueSource extends ValueSource { +final class ScoreValueSource extends DoubleValuesSource { + + static ScoreValueSource INSTANCE = new ScoreValueSource(); + + private ScoreValueSource() {} /** * context must contain a key "scorer" which is a {@link Scorer}. */ @Override - public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException { - Scorer v = (Scorer) context.get("scorer"); - if (v == null) { - throw new IllegalStateException("Expressions referencing the score can only be used for sorting"); - } - return new ScoreFunctionValues(this, v); + public DoubleValues getValues(LeafReaderContext readerContext) throws IOException { + return new ScoreFunctionValues(); } @Override @@ -55,7 +53,7 @@ class ScoreValueSource extends ValueSource { } @Override - public String description() { + public String toString() { return "score()"; } } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoringValues.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoringValues.java new file mode 100644 index 0000000..803f183 --- /dev/null +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoringValues.java @@ -0,0 +1,36 @@ +/* + * 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.expressions; + +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.Scorer; + +/** + * DoubleValues implementation that also allows access to a Scorer + */ +abstract class ScoringValues extends DoubleValues { + + public abstract void setScorer(Scorer scorer); + + static void setScorer(DoubleValues values, Scorer scorer) { + if (values instanceof ScoringValues) { + ((ScoringValues)values).setScorer(scorer); + } + } + +} diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java b/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java index e64249e..9f5006c 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java @@ -20,11 +20,7 @@ package org.apache.lucene.expressions; import java.util.HashMap; import java.util.Map; -import org.apache.lucene.queries.function.ValueSource; -import org.apache.lucene.queries.function.valuesource.DoubleFieldSource; -import org.apache.lucene.queries.function.valuesource.FloatFieldSource; -import org.apache.lucene.queries.function.valuesource.IntFieldSource; -import org.apache.lucene.queries.function.valuesource.LongFieldSource; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.SortField; /** @@ -64,9 +60,9 @@ public final class SimpleBindings extends Bindings { } /** - * Bind a {@link ValueSource} directly to the given name. + * Bind a {@link DoubleValuesSource} directly to the given name. */ - public void add(String name, ValueSource source) { map.put(name, source); } + public void add(String name, DoubleValuesSource source) { map.put(name, source); } /** * Adds an Expression to the bindings. @@ -78,27 +74,25 @@ public final class SimpleBindings extends Bindings { } @Override - public ValueSource getValueSource(String name) { + public DoubleValuesSource getValueSource(String name) { Object o = map.get(name); if (o == null) { throw new IllegalArgumentException("Invalid reference '" + name + "'"); } else if (o instanceof Expression) { return ((Expression)o).getValueSource(this); - } else if (o instanceof ValueSource) { - return ((ValueSource)o); + } else if (o instanceof DoubleValuesSource) { + return ((DoubleValuesSource)o); } SortField field = (SortField) o; switch(field.getType()) { case INT: - return new IntFieldSource(field.getField()); case LONG: - return new LongFieldSource(field.getField()); + return DoubleValuesSource.fromLongField(field.getField()); case FLOAT: - return new FloatFieldSource(field.getField()); case DOUBLE: - return new DoubleFieldSource(field.getField()); + return DoubleValuesSource.fromDoubleField(field.getField()); case SCORE: - return getScoreValueSource(); + return ScoreValueSource.INSTANCE; default: throw new UnsupportedOperationException(); } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java b/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java index c78f6a97..a4ab037 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java @@ -39,7 +39,7 @@ import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import org.apache.lucene.expressions.Expression; import org.apache.lucene.expressions.js.JavascriptParser.ExpressionContext; -import org.apache.lucene.queries.function.FunctionValues; +import org.apache.lucene.search.DoubleValues; import org.apache.lucene.util.IOUtils; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; @@ -93,13 +93,13 @@ public final class JavascriptCompiler { private static final String COMPILED_EXPRESSION_INTERNAL = COMPILED_EXPRESSION_CLASS.replace('.', '/'); static final Type EXPRESSION_TYPE = Type.getType(Expression.class); - static final Type FUNCTION_VALUES_TYPE = Type.getType(FunctionValues.class); + static final Type FUNCTION_VALUES_TYPE = Type.getType(DoubleValues.class); private static final org.objectweb.asm.commons.Method EXPRESSION_CTOR = getAsmMethod(void.class, "", String.class, String[].class), - EVALUATE_METHOD = getAsmMethod(double.class, "evaluate", int.class, FunctionValues[].class); + EVALUATE_METHOD = getAsmMethod(double.class, "evaluate", int.class, DoubleValues[].class); - static final org.objectweb.asm.commons.Method DOUBLE_VAL_METHOD = getAsmMethod(double.class, "doubleVal", int.class); + static final org.objectweb.asm.commons.Method DOUBLE_VAL_METHOD = getAsmMethod(double.class, "get", int.class); /** create an ASM Method object from return type, method name, and parameters. */ private static org.objectweb.asm.commons.Method getAsmMethod(Class rtype, String name, Class... ptypes) { @@ -155,8 +155,8 @@ public final class JavascriptCompiler { */ @SuppressWarnings({"unused", "null"}) private static void unusedTestCompile() { - FunctionValues f = null; - double ret = f.doubleVal(2); + DoubleValues f = null; + double ret = f.get(2); } /** diff --git a/lucene/expressions/src/test/org/apache/lucene/expressions/TestDemoExpressions.java b/lucene/expressions/src/test/org/apache/lucene/expressions/TestDemoExpressions.java index 01b3394..11ad7c3 100644 --- a/lucene/expressions/src/test/org/apache/lucene/expressions/TestDemoExpressions.java +++ b/lucene/expressions/src/test/org/apache/lucene/expressions/TestDemoExpressions.java @@ -16,18 +16,20 @@ */ package org.apache.lucene.expressions; +import java.io.IOException; + import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.expressions.js.JavascriptCompiler; import org.apache.lucene.expressions.js.VariableContext; import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; -import org.apache.lucene.queries.function.ValueSource; -import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource; -import org.apache.lucene.queries.function.valuesource.IntFieldSource; import org.apache.lucene.search.CheckHits; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.FieldDoc; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; @@ -39,9 +41,9 @@ import org.apache.lucene.search.TopFieldDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.util.LuceneTestCase; +import static org.apache.lucene.expressions.js.VariableContext.Type.INT_INDEX; import static org.apache.lucene.expressions.js.VariableContext.Type.MEMBER; import static org.apache.lucene.expressions.js.VariableContext.Type.STR_INDEX; -import static org.apache.lucene.expressions.js.VariableContext.Type.INT_INDEX; /** simple demo of using expressions */ @@ -236,7 +238,7 @@ public class TestDemoExpressions extends LuceneTestCase { public void testStaticExtendedVariableExample() throws Exception { Expression popularity = JavascriptCompiler.compile("doc[\"popularity\"].value"); SimpleBindings bindings = new SimpleBindings(); - bindings.add("doc['popularity'].value", new IntFieldSource("popularity")); + bindings.add("doc['popularity'].value", DoubleValuesSource.fromLongField("popularity")); Sort sort = new Sort(popularity.getSortField(bindings, true)); TopFieldDocs td = searcher.search(new MatchAllDocsQuery(), 3, sort); @@ -250,6 +252,25 @@ public class TestDemoExpressions extends LuceneTestCase { assertEquals(2D, (Double)d.fields[0], 1E-4); } + static class ConstantValuesSource extends DoubleValuesSource { + + final double value; + + ConstantValuesSource(double value) { + this.value = value; + } + + @Override + public DoubleValues getValues(LeafReaderContext ctx) throws IOException { + return new DoubleValues() { + @Override + public double get(int doc) { + return value; + } + }; + } + } + public void testDynamicExtendedVariableExample() throws Exception { Expression popularity = JavascriptCompiler.compile("doc['popularity'].value + magicarray[0] + fourtytwo"); @@ -258,7 +279,7 @@ public class TestDemoExpressions extends LuceneTestCase { // filled in with proper error messages for a real use case. Bindings bindings = new Bindings() { @Override - public ValueSource getValueSource(String name) { + public DoubleValuesSource getValueSource(String name) { VariableContext[] var = VariableContext.parse(name); assert var[0].type == MEMBER; String base = var[0].text; @@ -266,7 +287,7 @@ public class TestDemoExpressions extends LuceneTestCase { if (var.length > 1 && var[1].type == STR_INDEX) { String field = var[1].text; if (var.length > 2 && var[2].type == MEMBER && var[2].text.equals("value")) { - return new IntFieldSource(field); + return DoubleValuesSource.fromLongField(field); } else { fail("member: " + var[2].text);// error case, non/missing "value" member access } @@ -275,12 +296,12 @@ public class TestDemoExpressions extends LuceneTestCase { } } else if (base.equals("magicarray")) { if (var.length > 1 && var[1].type == INT_INDEX) { - return new DoubleConstValueSource(2048); + return new ConstantValuesSource(2048); } else { fail();// error case, magic array isn't an array } } else if (base.equals("fourtytwo")) { - return new DoubleConstValueSource(42); + return new ConstantValuesSource(42); } else { fail();// error case (variable doesn't exist) } diff --git a/lucene/expressions/src/test/org/apache/lucene/expressions/TestExpressionValueSource.java b/lucene/expressions/src/test/org/apache/lucene/expressions/TestExpressionValueSource.java index 3129d8c..e920885 100644 --- a/lucene/expressions/src/test/org/apache/lucene/expressions/TestExpressionValueSource.java +++ b/lucene/expressions/src/test/org/apache/lucene/expressions/TestExpressionValueSource.java @@ -24,14 +24,12 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.expressions.js.JavascriptCompiler; -import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.RandomIndexWriter; -import org.apache.lucene.queries.function.FunctionValues; -import org.apache.lucene.queries.function.ValueSource; -import org.apache.lucene.queries.function.ValueSourceScorer; -import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.SortField; import org.apache.lucene.store.Directory; import org.apache.lucene.util.LuceneTestCase; @@ -82,65 +80,14 @@ public class TestExpressionValueSource extends LuceneTestCase { Expression expr = JavascriptCompiler.compile("2*popularity"); SimpleBindings bindings = new SimpleBindings(); bindings.add(new SortField("popularity", SortField.Type.LONG)); - ValueSource vs = expr.getValueSource(bindings); + DoubleValuesSource vs = expr.getValueSource(bindings); assertEquals(1, reader.leaves().size()); LeafReaderContext leaf = reader.leaves().get(0); - FunctionValues values = vs.getValues(new HashMap(), leaf); - - assertEquals(10, values.doubleVal(0), 0); - assertEquals(10, values.floatVal(0), 0); - assertEquals(10, values.longVal(0)); - assertEquals(10, values.intVal(0)); - assertEquals(10, values.shortVal(0)); - assertEquals(10, values.byteVal(0)); - assertEquals("10.0", values.strVal(0)); - assertEquals(new Double(10), values.objectVal(0)); - - assertEquals(40, values.doubleVal(1), 0); - assertEquals(40, values.floatVal(1), 0); - assertEquals(40, values.longVal(1)); - assertEquals(40, values.intVal(1)); - assertEquals(40, values.shortVal(1)); - assertEquals(40, values.byteVal(1)); - assertEquals("40.0", values.strVal(1)); - assertEquals(new Double(40), values.objectVal(1)); - - assertEquals(4, values.doubleVal(2), 0); - assertEquals(4, values.floatVal(2), 0); - assertEquals(4, values.longVal(2)); - assertEquals(4, values.intVal(2)); - assertEquals(4, values.shortVal(2)); - assertEquals(4, values.byteVal(2)); - assertEquals("4.0", values.strVal(2)); - assertEquals(new Double(4), values.objectVal(2)); - } - - public void testRangeScorer() throws Exception { - Expression expr = JavascriptCompiler.compile("2*popularity"); - SimpleBindings bindings = new SimpleBindings(); - bindings.add(new SortField("popularity", SortField.Type.LONG)); - ValueSource vs = expr.getValueSource(bindings); - - assertEquals(1, reader.leaves().size()); - LeafReaderContext leaf = reader.leaves().get(0); - FunctionValues values = vs.getValues(new HashMap(), leaf); - - // everything - ValueSourceScorer scorer = values.getRangeScorer(leaf, "4", "40", true, true); - DocIdSetIterator iter = scorer.iterator(); - assertEquals(-1, iter.docID()); - assertEquals(0, iter.nextDoc()); - assertEquals(1, iter.nextDoc()); - assertEquals(2, iter.nextDoc()); - assertEquals(DocIdSetIterator.NO_MORE_DOCS, iter.nextDoc()); - - // just the first doc - scorer = values.getRangeScorer(leaf, "4", "40", false, false); - iter = scorer.iterator(); - assertEquals(-1, scorer.docID()); - assertEquals(0, iter.nextDoc()); - assertEquals(DocIdSetIterator.NO_MORE_DOCS, iter.nextDoc()); + DoubleValues values = vs.getValues(leaf); + assertEquals(10, values.get(0), 0); + assertEquals(40, values.get(1), 0); + assertEquals(4, values.get(2), 0); } public void testEquals() throws Exception { @@ -150,7 +97,7 @@ public class TestExpressionValueSource extends LuceneTestCase { bindings.add(new SortField("a", SortField.Type.INT)); bindings.add(new SortField("b", SortField.Type.INT)); - ValueSource vs1 = expr.getValueSource(bindings); + DoubleValuesSource vs1 = expr.getValueSource(bindings); // same instance assertEquals(vs1, vs1); // null @@ -158,20 +105,20 @@ public class TestExpressionValueSource extends LuceneTestCase { // other object assertFalse(vs1.equals("foobar")); // same bindings and expression instances - ValueSource vs2 = expr.getValueSource(bindings); + DoubleValuesSource vs2 = expr.getValueSource(bindings); assertEquals(vs1.hashCode(), vs2.hashCode()); assertEquals(vs1, vs2); // equiv bindings (different instance) SimpleBindings bindings2 = new SimpleBindings(); bindings2.add(new SortField("a", SortField.Type.INT)); bindings2.add(new SortField("b", SortField.Type.INT)); - ValueSource vs3 = expr.getValueSource(bindings2); + DoubleValuesSource vs3 = expr.getValueSource(bindings2); assertEquals(vs1, vs3); // different bindings (same names, different types) SimpleBindings bindings3 = new SimpleBindings(); - bindings3.add(new SortField("a", SortField.Type.LONG)); + bindings3.add(new SortField("a", SortField.Type.FLOAT)); bindings3.add(new SortField("b", SortField.Type.INT)); - ValueSource vs4 = expr.getValueSource(bindings3); + DoubleValuesSource vs4 = expr.getValueSource(bindings3); assertFalse(vs1.equals(vs4)); } } diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java index 49d4b77..4d19fab 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java @@ -19,8 +19,12 @@ package org.apache.lucene.queries.function; import java.io.IOException; import java.util.IdentityHashMap; import java.util.Map; +import java.util.Objects; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.queries.function.docvalues.DoubleDocValues; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.FieldComparator; import org.apache.lucene.search.FieldComparatorSource; import org.apache.lucene.search.IndexSearcher; @@ -36,6 +40,51 @@ import org.apache.lucene.search.SortField; */ public abstract class ValueSource { + private static class WrappedDoubleValuesSource extends ValueSource { + + final DoubleValuesSource in; + + public WrappedDoubleValuesSource(DoubleValuesSource in) { + this.in = in; + } + + @Override + public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException { + DoubleValues values = in.getValues(readerContext); + return new DoubleDocValues(this) { + @Override + public double doubleVal(int doc) { + return values.get(doc); + } + }; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WrappedDoubleValuesSource that = (WrappedDoubleValuesSource) o; + return Objects.equals(in, that.in); + } + + @Override + public int hashCode() { + return Objects.hash(in); + } + + @Override + public String description() { + return in.toString(); + } + } + + /** + * Convert a {@link DoubleValuesSource} to a ValueSource + */ + public static ValueSource wrapDoubleValuesSource(DoubleValuesSource in) { + return new WrappedDoubleValuesSource(in); + } + /** * Gets the values for this reader and the context that was previously * passed to createWeight() diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java b/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java index 509e0ab..7d30efe 100644 --- a/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java +++ b/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java @@ -16,10 +16,10 @@ */ package org.apache.lucene.queries.function; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.io.IOException; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.MockAnalyzer; @@ -29,52 +29,23 @@ import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.document.SortedDocValuesField; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; -import org.apache.lucene.index.Term; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.index.Term; import org.apache.lucene.queries.function.docvalues.FloatDocValues; -import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource; -import org.apache.lucene.queries.function.valuesource.ConstValueSource; -import org.apache.lucene.queries.function.valuesource.DivFloatFunction; -import org.apache.lucene.queries.function.valuesource.DocFreqValueSource; -import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource; -import org.apache.lucene.queries.function.valuesource.DoubleFieldSource; -import org.apache.lucene.queries.function.valuesource.FloatFieldSource; -import org.apache.lucene.queries.function.valuesource.IDFValueSource; -import org.apache.lucene.queries.function.valuesource.IfFunction; -import org.apache.lucene.queries.function.valuesource.IntFieldSource; -import org.apache.lucene.queries.function.valuesource.JoinDocFreqValueSource; -import org.apache.lucene.queries.function.valuesource.LinearFloatFunction; -import org.apache.lucene.queries.function.valuesource.LiteralValueSource; -import org.apache.lucene.queries.function.valuesource.LongFieldSource; -import org.apache.lucene.queries.function.valuesource.MaxDocValueSource; -import org.apache.lucene.queries.function.valuesource.MaxFloatFunction; -import org.apache.lucene.queries.function.valuesource.MinFloatFunction; -import org.apache.lucene.queries.function.valuesource.MultiFloatFunction; -import org.apache.lucene.queries.function.valuesource.MultiFunction; -import org.apache.lucene.queries.function.valuesource.NormValueSource; -import org.apache.lucene.queries.function.valuesource.NumDocsValueSource; -import org.apache.lucene.queries.function.valuesource.PowFloatFunction; -import org.apache.lucene.queries.function.valuesource.ProductFloatFunction; -import org.apache.lucene.queries.function.valuesource.QueryValueSource; -import org.apache.lucene.queries.function.valuesource.RangeMapFloatFunction; -import org.apache.lucene.queries.function.valuesource.ReciprocalFloatFunction; -import org.apache.lucene.queries.function.valuesource.ScaleFloatFunction; -import org.apache.lucene.queries.function.valuesource.SumFloatFunction; -import org.apache.lucene.queries.function.valuesource.SumTotalTermFreqValueSource; -import org.apache.lucene.queries.function.valuesource.TFValueSource; -import org.apache.lucene.queries.function.valuesource.TermFreqValueSource; -import org.apache.lucene.queries.function.valuesource.TotalTermFreqValueSource; +import org.apache.lucene.queries.function.valuesource.*; import org.apache.lucene.search.CheckHits; +import org.apache.lucene.search.DoubleValues; +import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; -import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.similarities.ClassicSimilarity; import org.apache.lucene.search.similarities.Similarity; import org.apache.lucene.store.Directory; @@ -82,6 +53,7 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LuceneTestCase; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Test; // TODO: add separate docvalues test /** @@ -663,5 +635,30 @@ public class TestValueSources extends LuceneTestCase { /** @see ExistsValueSource */ private static final ValueSource ALL_EXIST_VS = new ExistsValueSource(true); /** @see ExistsValueSource */ - private static final ValueSource NONE_EXIST_VS = new ExistsValueSource(false); + private static final ValueSource NONE_EXIST_VS = new ExistsValueSource(false); + + @Test + public void testDoubleValuesSourceConversion() throws IOException { + + final double value = 15.0; + + DoubleValuesSource source = new DoubleValuesSource() { + @Override + public DoubleValues getValues(LeafReaderContext ctx) throws IOException { + return new DoubleValues() { + @Override + public double get(int doc) { + return value; + } + }; + } + }; + + ValueSource vs = ValueSource.wrapDoubleValuesSource(source); + FunctionValues fv = vs.getValues(null, null); + assertEquals(fv.longVal(0), 15); + assertEquals(fv.intVal(0), 15); + assertEquals(fv.doubleVal(0), 15.0, 0.00000001); + + } }