| /* |
| * 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.queries.function; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.lucene.analysis.Analyzer; |
| import org.apache.lucene.analysis.MockAnalyzer; |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.document.NumericDocValuesField; |
| import org.apache.lucene.document.SortedDocValuesField; |
| import org.apache.lucene.document.SortedNumericDocValuesField; |
| import org.apache.lucene.document.StringField; |
| import org.apache.lucene.document.TextField; |
| import org.apache.lucene.index.IndexReader; |
| import org.apache.lucene.index.IndexWriterConfig; |
| 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.*; |
| import org.apache.lucene.search.CheckHits; |
| import org.apache.lucene.search.DoubleValuesSource; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.MatchNoDocsQuery; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.ScoreDoc; |
| import org.apache.lucene.search.ScoreMode; |
| import org.apache.lucene.search.Sort; |
| import org.apache.lucene.search.SortField; |
| import org.apache.lucene.search.SortedNumericSelector.Type; |
| 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; |
| import org.apache.lucene.util.BytesRef; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.apache.lucene.util.NumericUtils; |
| import org.junit.AfterClass; |
| import org.junit.BeforeClass; |
| |
| // TODO: add separate docvalues test |
| /** |
| * barebones tests for function queries. |
| */ |
| public class TestValueSources extends LuceneTestCase { |
| static Directory dir; |
| static Analyzer analyzer; |
| static IndexReader reader; |
| static IndexSearcher searcher; |
| |
| static final ValueSource BOGUS_FLOAT_VS = new FloatFieldSource("bogus_field"); |
| static final ValueSource BOGUS_DOUBLE_VS = new DoubleFieldSource("bogus_field"); |
| static final ValueSource BOGUS_INT_VS = new IntFieldSource("bogus_field"); |
| static final ValueSource BOGUS_LONG_VS = new LongFieldSource("bogus_field"); |
| |
| static final List<String[]> documents = Arrays.asList(new String[][] { |
| /* id, double, float, int, long, string, text, double MV (x3), int MV (x3)*/ |
| new String[] { "0", "3.63", "5.2", "35", "4343", "test", "this is a test test test", "2.13", "3.69", "0.11", "1", "7", "5"}, |
| new String[] { "1", "5.65", "9.3", "54", "1954", "bar", "second test", "12.79", "123.456", "0.01", "12", "900", "3" }, |
| }); |
| |
| @BeforeClass |
| public static void beforeClass() throws Exception { |
| dir = newDirectory(); |
| analyzer = new MockAnalyzer(random()); |
| IndexWriterConfig iwConfig = newIndexWriterConfig(analyzer); |
| iwConfig.setMergePolicy(newLogMergePolicy()); |
| RandomIndexWriter iw = new RandomIndexWriter(random(), dir, iwConfig); |
| for (String [] doc : documents) { |
| Document document = new Document(); |
| document.add(new StringField("id", doc[0], Field.Store.NO)); |
| document.add(new SortedDocValuesField("id", new BytesRef(doc[0]))); |
| document.add(new NumericDocValuesField("double", Double.doubleToRawLongBits(Double.parseDouble(doc[1])))); |
| document.add(new NumericDocValuesField("float", Float.floatToRawIntBits(Float.parseFloat(doc[2])))); |
| document.add(new NumericDocValuesField("int", Integer.parseInt(doc[3]))); |
| document.add(new NumericDocValuesField("long", Long.parseLong(doc[4]))); |
| document.add(new StringField("string", doc[5], Field.Store.NO)); |
| document.add(new SortedDocValuesField("string", new BytesRef(doc[5]))); |
| document.add(new TextField("text", doc[6], Field.Store.NO)); |
| document.add(new SortedNumericDocValuesField("floatMv", NumericUtils.floatToSortableInt(Float.parseFloat(doc[7])))); |
| document.add(new SortedNumericDocValuesField("floatMv", NumericUtils.floatToSortableInt(Float.parseFloat(doc[8])))); |
| document.add(new SortedNumericDocValuesField("floatMv", NumericUtils.floatToSortableInt(Float.parseFloat(doc[9])))); |
| document.add(new SortedNumericDocValuesField("doubleMv", NumericUtils.doubleToSortableLong(Double.parseDouble(doc[7])))); |
| document.add(new SortedNumericDocValuesField("doubleMv", NumericUtils.doubleToSortableLong(Double.parseDouble(doc[8])))); |
| document.add(new SortedNumericDocValuesField("doubleMv", NumericUtils.doubleToSortableLong(Double.parseDouble(doc[9])))); |
| document.add(new SortedNumericDocValuesField("intMv", Long.parseLong(doc[10]))); |
| document.add(new SortedNumericDocValuesField("intMv", Long.parseLong(doc[11]))); |
| document.add(new SortedNumericDocValuesField("intMv", Long.parseLong(doc[12]))); |
| document.add(new SortedNumericDocValuesField("longMv", Long.parseLong(doc[10]))); |
| document.add(new SortedNumericDocValuesField("longMv", Long.parseLong(doc[11]))); |
| document.add(new SortedNumericDocValuesField("longMv", Long.parseLong(doc[12]))); |
| iw.addDocument(document); |
| } |
| |
| reader = iw.getReader(); |
| searcher = newSearcher(reader); |
| iw.close(); |
| } |
| |
| @AfterClass |
| public static void afterClass() throws Exception { |
| searcher = null; |
| reader.close(); |
| reader = null; |
| dir.close(); |
| dir = null; |
| analyzer.close(); |
| analyzer = null; |
| } |
| |
| public void testConst() throws Exception { |
| ValueSource vs = new ConstValueSource(0.3f); |
| assertHits(new FunctionQuery(vs), |
| new float[] { 0.3f, 0.3f }); |
| assertAllExist(vs); |
| } |
| |
| public void testDiv() throws Exception { |
| ValueSource vs = new DivFloatFunction(new ConstValueSource(10f), new ConstValueSource(5f)); |
| assertHits(new FunctionQuery(vs), |
| new float[] { 2f, 2f }); |
| assertAllExist(vs); |
| vs = new DivFloatFunction(new ConstValueSource(10f), BOGUS_FLOAT_VS); |
| assertNoneExist(vs); |
| vs = new DivFloatFunction(BOGUS_FLOAT_VS, new ConstValueSource(10f)); |
| assertNoneExist(vs); |
| } |
| |
| public void testDocFreq() throws Exception { |
| ValueSource vs = new DocFreqValueSource("bogus", "bogus", "text", new BytesRef("test")); |
| assertHits(new FunctionQuery(vs), new float[] { 2f, 2f }); |
| assertAllExist(vs); |
| } |
| |
| public void testDoubleConst() throws Exception { |
| ValueSource vs = new DoubleConstValueSource(0.3d); |
| assertHits(new FunctionQuery(vs), new float[] { 0.3f, 0.3f }); |
| assertAllExist(vs); |
| } |
| |
| public void testDouble() throws Exception { |
| ValueSource vs = new DoubleFieldSource("double"); |
| assertHits(new FunctionQuery(vs), new float[] { 3.63f, 5.65f }); |
| assertAllExist(vs); |
| assertNoneExist(BOGUS_DOUBLE_VS); |
| } |
| |
| public void testDoubleMultiValued() throws Exception { |
| ValueSource vs = new MultiValuedDoubleFieldSource("doubleMv", Type.MAX); |
| assertHits(new FunctionQuery(vs), new float[] { 3.69f, 123.456f }); |
| assertAllExist(vs); |
| |
| vs = new MultiValuedDoubleFieldSource("doubleMv", Type.MIN); |
| assertHits(new FunctionQuery(vs), new float[] { 0.11f, 0.01f }); |
| assertAllExist(vs); |
| } |
| |
| public void testFloat() throws Exception { |
| ValueSource vs = new FloatFieldSource("float"); |
| assertHits(new FunctionQuery(vs), new float[] { 5.2f, 9.3f }); |
| assertAllExist(vs); |
| assertNoneExist(BOGUS_FLOAT_VS); |
| } |
| |
| public void testFloatMultiValued() throws Exception { |
| ValueSource vs = new MultiValuedFloatFieldSource("floatMv", Type.MAX); |
| assertHits(new FunctionQuery(vs), new float[] { 3.69f, 123.456f }); |
| assertAllExist(vs); |
| |
| vs = new MultiValuedFloatFieldSource("floatMv", Type.MIN); |
| assertHits(new FunctionQuery(vs), new float[] { 0.11f, 0.01f }); |
| assertAllExist(vs); |
| } |
| |
| public void testIDF() throws Exception { |
| Similarity saved = searcher.getSimilarity(); |
| try { |
| searcher.setSimilarity(new ClassicSimilarity()); |
| ValueSource vs = new IDFValueSource("bogus", "bogus", "text", new BytesRef("test")); |
| assertHits(new FunctionQuery(vs), new float[] { 1.0f, 1.0f }); |
| assertAllExist(vs); |
| } finally { |
| searcher.setSimilarity(saved); |
| } |
| } |
| |
| public void testIf() throws Exception { |
| ValueSource vs = new IfFunction(new BytesRefFieldSource("id"), |
| new ConstValueSource(1.0f), // match |
| new ConstValueSource(2.0f)); |
| assertHits(new FunctionQuery(vs), new float[] { 1f, 1f }); |
| assertAllExist(vs); |
| |
| // true just if a test value exists... |
| vs = new IfFunction(new LiteralValueSource("false"), |
| new ConstValueSource(1.0f), // match |
| new ConstValueSource(2.0f)); |
| assertHits(new FunctionQuery(vs), new float[] { 1f, 1f }); |
| assertAllExist(vs); |
| |
| // false value if tests value does not exist |
| vs = new IfFunction(BOGUS_FLOAT_VS, |
| new ConstValueSource(1.0f), |
| new ConstValueSource(2.0f)); // match |
| assertHits(new FunctionQuery(vs), new float[] { 2F, 2F }); |
| assertAllExist(vs); |
| |
| // final value may still not exist |
| vs = new IfFunction(new BytesRefFieldSource("id"), |
| BOGUS_FLOAT_VS, // match |
| new ConstValueSource(1.0f)); |
| assertNoneExist(vs); |
| } |
| |
| public void testInt() throws Exception { |
| ValueSource vs = new IntFieldSource("int"); |
| assertHits(new FunctionQuery(vs), new float[] { 35f, 54f }); |
| assertAllExist(vs); |
| assertNoneExist(BOGUS_INT_VS); |
| } |
| |
| public void testIntMultiValued() throws Exception { |
| ValueSource vs = new MultiValuedIntFieldSource("intMv", Type.MAX); |
| assertHits(new FunctionQuery(vs), new float[] { 7f, 900f }); |
| assertAllExist(vs); |
| |
| vs = new MultiValuedIntFieldSource("intMv", Type.MIN); |
| assertHits(new FunctionQuery(vs), new float[] { 1f, 3f }); |
| assertAllExist(vs); |
| } |
| |
| public void testJoinDocFreq() throws Exception { |
| assertHits(new FunctionQuery(new JoinDocFreqValueSource("string", "text")), |
| new float[] { 2f, 0f }); |
| |
| // TODO: what *should* the rules be for exist() ? |
| } |
| |
| public void testLinearFloat() throws Exception { |
| ValueSource vs = new LinearFloatFunction(new ConstValueSource(2.0f), 3, 1); |
| assertHits(new FunctionQuery(vs), new float[] { 7f, 7f }); |
| assertAllExist(vs); |
| vs = new LinearFloatFunction(BOGUS_FLOAT_VS, 3, 1); |
| assertNoneExist(vs); |
| } |
| |
| public void testLong() throws Exception { |
| ValueSource vs = new LongFieldSource("long"); |
| assertHits(new FunctionQuery(vs), new float[] { 4343f, 1954f }); |
| assertAllExist(vs); |
| assertNoneExist(BOGUS_LONG_VS); |
| } |
| |
| public void testLongMultiValued() throws Exception { |
| ValueSource vs = new MultiValuedLongFieldSource("longMv", Type.MAX); |
| assertHits(new FunctionQuery(vs), new float[] { 7f, 900f }); |
| assertAllExist(vs); |
| |
| vs = new MultiValuedLongFieldSource("longMv", Type.MIN); |
| assertHits(new FunctionQuery(vs), new float[] { 1f, 3f }); |
| assertAllExist(vs); |
| } |
| |
| public void testMaxDoc() throws Exception { |
| ValueSource vs = new MaxDocValueSource(); |
| assertHits(new FunctionQuery(vs), new float[] { 2f, 2f }); |
| assertAllExist(vs); |
| } |
| |
| public void testMaxFloat() throws Exception { |
| ValueSource vs = new MaxFloatFunction(new ValueSource[] { |
| new ConstValueSource(1f), new ConstValueSource(2f)}); |
| |
| assertHits(new FunctionQuery(vs), new float[] { 2f, 2f }); |
| assertAllExist(vs); |
| |
| // as long as one value exists, then max exists |
| vs = new MaxFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, new ConstValueSource(2F)}); |
| assertAllExist(vs); |
| vs = new MaxFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, new ConstValueSource(2F), BOGUS_DOUBLE_VS}); |
| assertAllExist(vs); |
| // if none exist, then max doesn't exist |
| vs = new MaxFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, BOGUS_INT_VS, BOGUS_DOUBLE_VS}); |
| assertNoneExist(vs); |
| } |
| |
| public void testMinFloat() throws Exception { |
| ValueSource vs = new MinFloatFunction(new ValueSource[] { |
| new ConstValueSource(1f), new ConstValueSource(2f)}); |
| |
| assertHits(new FunctionQuery(vs), new float[] { 1f, 1f }); |
| assertAllExist(vs); |
| |
| // as long as one value exists, then min exists |
| vs = new MinFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, new ConstValueSource(2F)}); |
| assertHits(new FunctionQuery(vs), new float[] { 2F, 2F }); |
| assertAllExist(vs); |
| vs = new MinFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, new ConstValueSource(2F), BOGUS_DOUBLE_VS}); |
| assertAllExist(vs); |
| |
| // if none exist, then min doesn't exist |
| vs = new MinFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, BOGUS_INT_VS, BOGUS_DOUBLE_VS}); |
| assertNoneExist(vs); |
| } |
| |
| public void testNorm() throws Exception { |
| Similarity saved = searcher.getSimilarity(); |
| try { |
| // no norm field (so agnostic to indexed similarity) |
| searcher.setSimilarity(new ClassicSimilarity()); |
| ValueSource vs = new NormValueSource("byte"); |
| assertHits(new FunctionQuery(vs), new float[] { 1f, 1f }); |
| |
| // regardless of whether norms exist, value source exists == 0 |
| assertAllExist(vs); |
| |
| vs = new NormValueSource("text"); |
| assertAllExist(vs); |
| |
| } finally { |
| searcher.setSimilarity(saved); |
| } |
| } |
| |
| public void testNumDocs() throws Exception { |
| ValueSource vs = new NumDocsValueSource(); |
| assertHits(new FunctionQuery(vs), new float[] { 2f, 2f }); |
| assertAllExist(vs); |
| } |
| |
| public void testPow() throws Exception { |
| ValueSource vs = new PowFloatFunction(new ConstValueSource(2f), new ConstValueSource(3f)); |
| assertHits(new FunctionQuery(vs), new float[] { 8f, 8f }); |
| assertAllExist(vs); |
| vs = new PowFloatFunction(BOGUS_FLOAT_VS, new ConstValueSource(3f)); |
| assertNoneExist(vs); |
| vs = new PowFloatFunction(new ConstValueSource(3f), BOGUS_FLOAT_VS); |
| assertNoneExist(vs); |
| } |
| |
| public void testProduct() throws Exception { |
| ValueSource vs = new ProductFloatFunction(new ValueSource[] { |
| new ConstValueSource(2f), new ConstValueSource(3f)}); |
| assertHits(new FunctionQuery(vs), new float[] { 6f, 6f }); |
| assertAllExist(vs); |
| |
| vs = new ProductFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, new ConstValueSource(3f)}); |
| assertNoneExist(vs); |
| } |
| |
| public void testQueryWrapedFuncWrapedQuery() throws Exception { |
| ValueSource vs = new QueryValueSource(new FunctionQuery(new ConstValueSource(2f)), 0f); |
| assertHits(new FunctionQuery(vs), new float[] { 2f, 2f }); |
| assertAllExist(vs); |
| |
| vs = new QueryValueSource(new FunctionRangeQuery(new IntFieldSource("int"), Integer.MIN_VALUE, Integer.MAX_VALUE, true, true), 0f); |
| assertHits(new FunctionQuery(vs), new float[] { 35f, 54f }); |
| assertAllExist(vs); |
| } |
| |
| public void testQuery() throws Exception { |
| Similarity saved = searcher.getSimilarity(); |
| |
| try { |
| searcher.setSimilarity(new ClassicSimilarity()); |
| |
| ValueSource vs = new QueryValueSource(new TermQuery(new Term("string","bar")), 42F); |
| assertHits(new FunctionQuery(vs), new float[] { 42F, 1.4054651F }); |
| |
| // valuesource should exist only for things matching the term query |
| // sanity check via quick & dirty wrapper arround tf |
| ValueSource expected = new MultiFloatFunction(new ValueSource[] { |
| new TFValueSource("bogus", "bogus", "string", new BytesRef("bar"))}) { |
| |
| @Override |
| protected String name() { return "tf_based_exists"; } |
| |
| @Override |
| protected float func(int doc, FunctionValues[] valsArr) throws IOException { |
| return valsArr[0].floatVal(doc); |
| } |
| @Override |
| protected boolean exists(int doc, FunctionValues[] valsArr) throws IOException { |
| // if tf > 0, then it should exist |
| return 0 < func(doc, valsArr); |
| } |
| }; |
| |
| assertExists(expected, vs); |
| |
| |
| // Query matches all docs, func exists for all docs |
| vs = new QueryValueSource(new TermQuery(new Term("text","test")), 0F); |
| assertAllExist(vs); |
| |
| // Query matches no docs, func exists for no docs |
| vs = new QueryValueSource(new TermQuery(new Term("bogus","does not exist")), 0F); |
| assertNoneExist(vs); |
| |
| // doc doesn't match the query, so default value should be returned |
| vs = new QueryValueSource(new MatchNoDocsQuery(), 5.0f); |
| final LeafReaderContext leaf = searcher.getIndexReader().leaves().get(0); |
| FunctionValues fv = vs.getValues(ValueSource.newContext(searcher), leaf); |
| assertEquals(5.0f, fv.objectVal(1)); |
| |
| // test with def value but doc matches the query, so def value shouldn't be returned |
| vs = new QueryValueSource(new TermQuery(new Term("text","test")), 2F); |
| fv = vs.getValues(ValueSource.newContext(searcher), leaf); |
| assertNotEquals(2f, fv.objectVal(1)); |
| } finally { |
| searcher.setSimilarity(saved); |
| } |
| } |
| |
| public void testRangeMap() throws Exception { |
| assertHits(new FunctionQuery(new RangeMapFloatFunction(new FloatFieldSource("float"), |
| 5, 6, 1, 0f)), |
| new float[] { 1f, 0f }); |
| assertHits(new FunctionQuery(new RangeMapFloatFunction(new FloatFieldSource("float"), |
| 5, 6, new SumFloatFunction(new ValueSource[] {new ConstValueSource(1f), new ConstValueSource(2f)}), |
| new ConstValueSource(11f))), |
| new float[] { 3f, 11f }); |
| |
| // TODO: what *should* the rules be for exist() ? |
| // ((source exists && source in range && target exists) OR (source not in range && default exists)) ? |
| } |
| |
| public void testReciprocal() throws Exception { |
| ValueSource vs = new ReciprocalFloatFunction(new ConstValueSource(2f), 3, 1, 4); |
| assertHits(new FunctionQuery(vs), new float[] { 0.1f, 0.1f }); |
| assertAllExist(vs); |
| |
| vs = new ReciprocalFloatFunction(BOGUS_FLOAT_VS, 3, 1, 4); |
| assertNoneExist(vs); |
| } |
| |
| public void testScale() throws Exception { |
| ValueSource vs = new ScaleFloatFunction(new IntFieldSource("int"), 0, 1); |
| assertHits(new FunctionQuery(vs), new float[] { 0.0f, 1.0f }); |
| assertAllExist(vs); |
| |
| vs = new ScaleFloatFunction(BOGUS_INT_VS, 0, 1); |
| assertNoneExist(vs); |
| } |
| |
| public void testSumFloat() throws Exception { |
| ValueSource vs = new SumFloatFunction(new ValueSource[] { |
| new ConstValueSource(1f), new ConstValueSource(2f)}); |
| assertHits(new FunctionQuery(vs), new float[] { 3f, 3f }); |
| assertAllExist(vs); |
| |
| vs = new SumFloatFunction(new ValueSource[] { |
| BOGUS_FLOAT_VS, new ConstValueSource(2f)}); |
| assertNoneExist(vs); |
| } |
| |
| public void testSumTotalTermFreq() throws Exception { |
| ValueSource vs = new SumTotalTermFreqValueSource("text"); |
| assertHits(new FunctionQuery(vs), new float[] { 8f, 8f }); |
| assertAllExist(vs); |
| } |
| |
| public void testTermFreq() throws Exception { |
| ValueSource vs = new TermFreqValueSource("bogus", "bogus", "text", new BytesRef("test")); |
| assertHits(new FunctionQuery(vs), new float[] { 3f, 1f }); |
| assertAllExist(vs); |
| |
| vs = new TermFreqValueSource("bogus", "bogus", "string", new BytesRef("bar")); |
| assertHits(new FunctionQuery(vs), new float[] { 0f, 1f }); |
| assertAllExist(vs); |
| |
| // regardless of whether norms exist, value source exists == 0 |
| vs = new TermFreqValueSource("bogus", "bogus", "bogus", new BytesRef("bogus")); |
| assertHits(new FunctionQuery(vs), new float[] { 0F, 0F }); |
| assertAllExist(vs); |
| } |
| |
| public void testMultiBoolFunction() throws Exception { |
| // verify toString and description |
| List<ValueSource> valueSources = new ArrayList<>(Arrays.asList( |
| new ConstValueSource(4.1f), new ConstValueSource(1.2f), new DoubleFieldSource("some_double") |
| )); |
| ValueSource vs = new MultiBoolFunction(valueSources) { |
| @Override |
| protected String name() { |
| return "test"; |
| } |
| |
| @Override |
| protected boolean func(int doc, FunctionValues[] vals) throws IOException { |
| return false; |
| } |
| }; |
| assertEquals("test(const(4.1),const(1.2),double(some_double))", vs.description()); |
| |
| final LeafReaderContext leaf = searcher.getIndexReader().leaves().get(0); |
| FunctionValues fv = vs.getValues(ValueSource.newContext(searcher), leaf); |
| // doesn't matter what is the docId, verify toString |
| assertEquals("test(const(4.1),const(1.2),double(some_double)=0.0)", fv.toString(1)); |
| |
| } |
| |
| public void testTF() throws Exception { |
| Similarity saved = searcher.getSimilarity(); |
| try { |
| // no norm field (so agnostic to indexed similarity) |
| searcher.setSimilarity(new ClassicSimilarity()); |
| |
| ValueSource vs = new TFValueSource("bogus", "bogus", "text", new BytesRef("test")); |
| assertHits(new FunctionQuery(vs), |
| new float[] { (float)Math.sqrt(3d), (float)Math.sqrt(1d) }); |
| assertAllExist(vs); |
| |
| vs = new TFValueSource("bogus", "bogus", "string", new BytesRef("bar")); |
| assertHits(new FunctionQuery(vs), new float[] { 0f, 1f }); |
| assertAllExist(vs); |
| |
| // regardless of whether norms exist, value source exists == 0 |
| vs = new TFValueSource("bogus", "bogus", "bogus", new BytesRef("bogus")); |
| assertHits(new FunctionQuery(vs), new float[] { 0F, 0F }); |
| assertAllExist(vs); |
| |
| } finally { |
| searcher.setSimilarity(saved); |
| } |
| } |
| |
| public void testTotalTermFreq() throws Exception { |
| ValueSource vs = new TotalTermFreqValueSource("bogus", "bogus", "text", new BytesRef("test")); |
| assertHits(new FunctionQuery(vs), new float[] { 4f, 4f }); |
| assertAllExist(vs); |
| } |
| |
| public void testMultiFunctionHelperEquivilence() throws IOException { |
| // the 2 arg versions of these methods should return the exact same results as |
| // the multi arg versions with a 2 element array |
| |
| // actual doc / index is not relevant for this test |
| final LeafReaderContext leaf = searcher.getIndexReader().leaves().get(0); |
| final Map context = ValueSource.newContext(searcher); |
| |
| ALL_EXIST_VS.createWeight(context, searcher); |
| NONE_EXIST_VS.createWeight(context, searcher); |
| |
| final FunctionValues ALL = ALL_EXIST_VS.getValues(context, leaf); |
| final FunctionValues NONE = NONE_EXIST_VS.getValues(context, leaf); |
| |
| // quick sanity checks of explicit results |
| assertTrue(MultiFunction.allExists(1, ALL, ALL)); |
| assertTrue(MultiFunction.allExists(1, new FunctionValues[] {ALL, ALL})); |
| assertTrue(MultiFunction.anyExists(1, ALL, NONE)); |
| assertTrue(MultiFunction.anyExists(1, new FunctionValues[] {ALL, NONE})); |
| // |
| assertFalse(MultiFunction.allExists(1, ALL, NONE)); |
| assertFalse(MultiFunction.allExists(1, new FunctionValues[] {ALL, NONE})); |
| assertFalse(MultiFunction.anyExists(1, NONE, NONE)); |
| assertFalse(MultiFunction.anyExists(1, new FunctionValues[] {NONE, NONE})); |
| |
| |
| |
| // iterate all permutations and verify equivilence |
| for (FunctionValues firstArg : new FunctionValues[] {ALL, NONE}) { |
| for (FunctionValues secondArg : new FunctionValues[] {ALL, NONE}) { |
| assertEquals("allExists("+firstArg+","+secondArg+")", |
| MultiFunction.allExists(1, firstArg,secondArg), |
| MultiFunction.allExists(1, new FunctionValues[] { firstArg,secondArg})); |
| assertEquals("anyExists("+firstArg+","+secondArg+")", |
| MultiFunction.anyExists(1, firstArg,secondArg), |
| MultiFunction.anyExists(1, new FunctionValues[] { firstArg,secondArg})); |
| |
| // future proof against posibility of someone "optimizing" the array method |
| // if .length==2 ... redundent third arg should give same results as well... |
| assertEquals("allExists("+firstArg+","+secondArg+","+secondArg+")", |
| MultiFunction.allExists(1, firstArg,secondArg), |
| MultiFunction.allExists(1, new FunctionValues[] { firstArg,secondArg,secondArg})); |
| assertEquals("anyExists("+firstArg+","+secondArg+","+secondArg+")", |
| MultiFunction.anyExists(1, firstArg,secondArg), |
| MultiFunction.anyExists(1, new FunctionValues[] { firstArg,secondArg,secondArg})); |
| |
| } |
| } |
| } |
| |
| public void testWrappingAsDoubleValues() throws IOException { |
| |
| FunctionScoreQuery q = FunctionScoreQuery.boostByValue(new TermQuery(new Term("f", "t")), |
| new DoubleFieldSource("double").asDoubleValuesSource()); |
| |
| searcher.createWeight(searcher.rewrite(q), ScoreMode.COMPLETE, 1); |
| |
| // assert that the query has not cached a reference to the IndexSearcher |
| FunctionScoreQuery.MultiplicativeBoostValuesSource source1 = (FunctionScoreQuery.MultiplicativeBoostValuesSource) q.getSource(); |
| ValueSource.WrappedDoubleValuesSource source2 = (ValueSource.WrappedDoubleValuesSource) source1.boost; |
| assertNull(source2.searcher); |
| |
| } |
| |
| public void testBuildingFromDoubleValues() throws Exception { |
| DoubleValuesSource dvs = ValueSource.fromDoubleValuesSource(DoubleValuesSource.fromDoubleField("double")).asDoubleValuesSource(); |
| assertHits(new FunctionQuery(ValueSource.fromDoubleValuesSource(dvs)), new float[] { 3.63f, 5.65f }); |
| } |
| |
| /** |
| * Asserts that for every doc, the {@link FunctionValues#exists} value |
| * from the {@link ValueSource} is <b>true</b>. |
| */ |
| void assertAllExist(ValueSource vs) { |
| assertExists(ALL_EXIST_VS, vs); |
| } |
| /** |
| * Asserts that for every doc, the {@link FunctionValues#exists} value |
| * from the {@link ValueSource} is <b>false</b>. |
| */ |
| void assertNoneExist(ValueSource vs) { |
| assertExists(NONE_EXIST_VS, vs); |
| } |
| /** |
| * Asserts that for every doc, the {@link FunctionValues#exists} value from the |
| * <code>actual</code> {@link ValueSource} matches the {@link FunctionValues#exists} |
| * value from the <code>expected</code> {@link ValueSource} |
| */ |
| void assertExists(ValueSource expected, ValueSource actual) { |
| Map context = ValueSource.newContext(searcher); |
| try { |
| expected.createWeight(context, searcher); |
| actual.createWeight(context, searcher); |
| |
| for (org.apache.lucene.index.LeafReaderContext leaf : searcher.getIndexReader().leaves()) { |
| final FunctionValues expectedVals = expected.getValues(context, leaf); |
| final FunctionValues actualVals = actual.getValues(context, leaf); |
| |
| String msg = expected.toString() + " ?= " + actual.toString() + " -> "; |
| for (int i = 0; i < leaf.reader().maxDoc(); ++i) { |
| assertEquals(msg + i, expectedVals.exists(i), actualVals.exists(i)); |
| } |
| } |
| } catch (IOException e) { |
| throw new RuntimeException(actual.toString(), e); |
| } |
| } |
| |
| void assertHits(Query q, float scores[]) throws Exception { |
| ScoreDoc expected[] = new ScoreDoc[scores.length]; |
| int expectedDocs[] = new int[scores.length]; |
| for (int i = 0; i < expected.length; i++) { |
| expectedDocs[i] = i; |
| expected[i] = new ScoreDoc(i, scores[i]); |
| } |
| TopDocs docs = searcher.search(q, documents.size(), |
| new Sort(new SortField("id", SortField.Type.STRING)), true); |
| CheckHits.checkHits(random(), q, "", searcher, expectedDocs); |
| CheckHits.checkHitsQuery(q, expected, docs.scoreDocs, expectedDocs); |
| CheckHits.checkExplanations(q, "", searcher); |
| } |
| |
| /** |
| * Simple test value source that implements {@link FunctionValues#exists} as a constant |
| * @see #ALL_EXIST_VS |
| * @see #NONE_EXIST_VS |
| */ |
| private static final class ExistsValueSource extends ValueSource { |
| |
| final boolean expected; |
| final int value; |
| |
| public ExistsValueSource(boolean expected) { |
| this.expected = expected; |
| this.value = expected ? 1 : 0; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return o == this; |
| } |
| |
| @Override |
| public int hashCode() { |
| return value; |
| } |
| |
| @Override |
| public String description() { |
| return expected ? "ALL_EXIST" : "NONE_EXIST"; |
| } |
| |
| @Override |
| public FunctionValues getValues(Map context, LeafReaderContext readerContext) { |
| return new FloatDocValues(this) { |
| @Override |
| public float floatVal(int doc) { |
| return (float)value; |
| } |
| @Override |
| public boolean exists(int doc) { |
| return expected; |
| } |
| }; |
| } |
| }; |
| |
| /** @see ExistsValueSource */ |
| private static final ValueSource ALL_EXIST_VS = new ExistsValueSource(true); |
| /** @see ExistsValueSource */ |
| private static final ValueSource NONE_EXIST_VS = new ExistsValueSource(false); |
| } |