Index: src/test/org/apache/lucene/search/function/TestFieldScoreQuery.java =================================================================== --- src/test/org/apache/lucene/search/function/TestFieldScoreQuery.java (revision 0) +++ src/test/org/apache/lucene/search/function/TestFieldScoreQuery.java (revision 0) @@ -0,0 +1,203 @@ +package org.apache.lucene.search.function; + +/** + * 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.HashMap; + +import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryUtils; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.TopDocs; + +/** + * Test FieldScoreQuery search. + *
+ * Tests here create an index with a few documents, each having + * an int value indexed field and a float value indexed field. + * The values of these fields are later used for scoring. + *
+ * The order tests use Hits to verify that docs are ordered (by score) as expected. + *
+ * The exact score tests use TopDocs top to verify the exact score.
+ */
+public class TestFieldScoreQuery extends FunctionTestSetup {
+
+ /* @override constructor */
+ public TestFieldScoreQuery(String name) {
+ super(name);
+ }
+
+ /* @override */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /* @override */
+ protected void setUp() throws Exception {
+ // prepare a small index with just a few documents.
+ super.setUp();
+ }
+
+ /** Test that FieldScoreQuery of Type.BYTE returns docs in expected order. */
+ public void testOrderByte () throws CorruptIndexException, Exception {
+ // INT field values are small enough to be parsed as byte
+ doTestOrder(INT_FIELD,FieldScoreQuery.Type.BYTE);
+ }
+
+ /** Test that FieldScoreQuery of Type.SHORT returns docs in expected order. */
+ public void testOrderShort () throws CorruptIndexException, Exception {
+ // INT field values are small enough to be parsed as short
+ doTestOrder(INT_FIELD,FieldScoreQuery.Type.SHORT);
+ }
+
+ /** Test that FieldScoreQuery of Type.INT returns docs in expected order. */
+ public void testOrderInt () throws CorruptIndexException, Exception {
+ doTestOrder(INT_FIELD,FieldScoreQuery.Type.INT);
+ }
+
+ /** Test that FieldScoreQuery of Type.FLOAT returns docs in expected order. */
+ public void testOrderFloat () throws CorruptIndexException, Exception {
+ // INT field can be parsed as float
+ doTestOrder(INT_FIELD,FieldScoreQuery.Type.FLOAT);
+ // same values, but in flot format
+ doTestOrder(FLOAT_FIELD,FieldScoreQuery.Type.FLOAT);
+ }
+
+ // Test that FieldScoreQuery returns docs in expected order.
+ private void doTestOrder (String field, FieldScoreQuery.Type tp) throws CorruptIndexException, Exception {
+ IndexSearcher s = new IndexSearcher(dir);
+ Query q = new FieldScoreQuery(field,tp);
+ log("test: "+q);
+ QueryUtils.check(q,s);
+ Hits h = s.search(q);
+ assertEquals("All docs should be matched!",N_DOCS,h.length());
+ String prevID = "ID"+(N_DOCS+1); // greater than all ids of docs in this test
+ for (int i=0; i
+ * Usually the value source would be the (cached) value of a field.
+ *
+ * Score is set as: Score(doc,query) = query.getBoost()2 * valueSource(doc).
+ *
+ * @author yonik
+ */
+public class ValueSourceQuery extends Query {
+ ValueSource valSrc;
+
+ /**
+ * Create a value source query
+ * @param valSrc provides the values defines the function to be used for scoring
+ */
+ public ValueSourceQuery(ValueSource valSrc) {
+ this.valSrc=valSrc;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
+ public Query rewrite(IndexReader reader) throws IOException {
+ return this;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#extractTerms(java.util.Set) */
+ public void extractTerms(Set terms) {
+ // no terms involved here
+ }
+
+ private class ValueSourceWeight implements Weight {
+ Searcher searcher;
+ float queryNorm;
+ float queryWeight;
+
+ public ValueSourceWeight(Searcher searcher) {
+ this.searcher = searcher;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#getQuery() */
+ public Query getQuery() {
+ return ValueSourceQuery.this;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#getValue() */
+ public float getValue() {
+ return queryWeight;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#sumOfSquaredWeights() */
+ public float sumOfSquaredWeights() throws IOException {
+ queryWeight = getBoost();
+ return queryWeight * queryWeight;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#normalize(float) */
+ public void normalize(float norm) {
+ this.queryNorm = norm;
+ queryWeight *= this.queryNorm;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader) */
+ public Scorer scorer(IndexReader reader) throws IOException {
+ return new ValueSourceScorer(getSimilarity(searcher), reader, this);
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int) */
+ public Explanation explain(IndexReader reader, int doc) throws IOException {
+ return scorer(reader).explain(doc);
+ }
+ }
+
+ /**
+ * A scorer that (simply) matches all documents, and scores each document with
+ * the value of the value soure in effect. As an example, if the value source
+ * is a (cached) field source, then value of that field in that document will
+ * be used. (assuming field is indexed for this doc, with a single token.)
+ */
+ private class ValueSourceScorer extends Scorer {
+ private final IndexReader reader;
+ private final ValueSourceWeight weight;
+ private final int maxDoc;
+ private final float qWeight;
+ private int doc=-1;
+ private final DocValues vals;
+
+ // constructor
+ private ValueSourceScorer(Similarity similarity, IndexReader reader, ValueSourceWeight w) throws IOException {
+ super(similarity);
+ this.weight = w;
+ this.qWeight = w.getValue();
+ this.reader = reader;
+ this.maxDoc = reader.maxDoc();
+ // this is when/where the values are first created.
+ vals = valSrc.getValues(reader);
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#next() */
+ public boolean next() throws IOException {
+ for(;;) {
+ ++doc;
+ if (doc>=maxDoc) {
+ return false;
+ }
+ if (reader.isDeleted(doc)) {
+ continue;
+ }
+ return true;
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#doc()
+ */
+ public int doc() {
+ return doc;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#score() */
+ public float score() throws IOException {
+ return qWeight * vals.floatVal(doc);
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#skipTo(int) */
+ public boolean skipTo(int target) throws IOException {
+ doc=target-1;
+ return next();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#explain(int) */
+ public Explanation explain(int doc) throws IOException {
+ float sc = qWeight * vals.floatVal(doc);
+
+ Explanation result = new ComplexExplanation(
+ true, sc, ValueSourceQuery.this.toString() + ", product of:");
+
+ result.addDetail(vals.explain(doc));
+ result.addDetail(new Explanation(getBoost(), "boost"));
+ result.addDetail(new Explanation(weight.queryNorm,"queryNorm"));
+ return result;
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) */
+ protected Weight createWeight(Searcher searcher) {
+ return new ValueSourceQuery.ValueSourceWeight(searcher);
+ }
+
+ /* (non-Javadoc) @see org.apache.lucene.search.Query#toString(java.lang.String) */
+ public String toString(String field) {
+ String s = ToStringUtils.simpleClassName(getClass())+"("+valSrc.toString()+")";
+ return ToStringUtils.boost(getBoost(),s);
+ }
+
+ /** Returns true if Mandatory: every DocValues implementation must implement at least this method.
+ * @param doc document whose float value is requested.
+ */
+ public abstract float floatVal(int doc);
+
+ /**
+ * Return doc value as an int.
+ * Optional: DocValues implementation can (but don't have to) override this method.
+ * @param doc document whose int value is requested.
+ */
+ public int intVal(int doc) {
+ return (int) floatVal(doc);
+ }
+
+ /**
+ * Return doc value as a long.
+ * Optional: DocValues implementation can (but don't have to) override this method.
+ * @param doc document whose long value is requested.
+ */
+ public long longVal(int doc) {
+ return (long) floatVal(doc);
+ }
+
+ /**
+ * Return doc value as a double.
+ * Optional: DocValues implementation can (but don't have to) override this method.
+ * @param doc document whose double value is requested.
+ */
+ public double doubleVal(int doc) {
+ return (double) floatVal(doc);
+ }
+
+ /**
+ * Return doc value as a string.
+ * Optional: DocValues implementation can (but don't have to) override this method.
+ * @param doc document whose string value is requested.
+ */
+ public String strVal(int doc) {
+ return Float.toString(floatVal(doc));
+ }
+
+ /**
+ * Return a string representation of a doc value, as reuired for Explanations.
+ */
+ public abstract String toString(int doc);
+
+ /**
+ * Explain the scoring value for the input doc.
+ */
+ public Explanation explain(int doc) {
+ return new Explanation(floatVal(doc), toString(doc));
+ }
+
+ /**
+ * Expert: for test purposes only, return the inner array of values, or null if not applicable.
+ *
+ * Allows tests to verify that loaded values are:
+ *
+ * The query matches all documents, and scores each document according to the numeric
+ * value of that field.
+ *
+ * It is assumed, and expected, that:
+ *
+ * Combining this query in a FunctionQuery allows much freedom in affecting document scores.
+ * Note, that with this freedmo comes resposibility: it is more than likely that the
+ * default Lucene scoring is superior in quality to scoring modified as explained here.
+ * However, in some cases, and certainly for research experiments, this capability may turn useful.
+ *
+ * When creating this query, select the right type. That type should match the data stored in the
+ * field. So in fact the "right" type should be selected before indexing. Whichever type is selected
+ * has effect on the RAM usage:
+ *
+ * Caching:
+ * Values for the numeric field are loaded once and cached in memory for further use with the same IndexReader.
+ * To take advantage of this, it is extremely important to reuse index-readers or index-searchers,
+ * otherwise, for instance if for each query a new index reader is opened, large penalties would be
+ * payd for loading the field values into memory over and over again!
+ */
+public class FieldScoreQuery extends ValueSourceQuery {
+
+ /**
+ * Type of score field, indicating how field values are interpreted/parsed.
+ *
+ * The type selected at search search time should match the data stored in the field.
+ * Different types have different RAM requirements:
+ *
+ * The
+ * Fields used herein nust be indexed (doesn't matter if these fields are stored or not).
+ *
+ * It is assumed that each such indexed field is untokenized, or at least has a single token in a document.
+ * For documents with multiple tokens of the same field, behavior is undefined (It is likely that current
+ * code would use the value of one of these tokens, but this is not guaranteed).
+ *
+ * Document with no tokens in this field are assigned the
+ * Subclasses need only override this method to modify the custom score.
+ *
+ * The default computation herein is:
+ *
+ * Note: only has effect when the ValueSource part is not null.
+ */
+ public boolean isStrict() {
+ return strict;
+ }
+
+ /**
+ * Set the strict mode of this query.
+ * @param strict The strict mode to set.
+ * @see #isStrict()
+ */
+ public void setStrict(boolean strict) {
+ this.strict = strict;
+ }
+
+}
Property changes on: src/java/org/apache/lucene/search/function/CustomScoreQuery.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/ValueSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/ValueSource.java (revision 0)
+++ src/java/org/apache/lucene/search/function/ValueSource.java (revision 0)
@@ -0,0 +1,69 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.function.DocValues;
+import org.apache.lucene.util.ToStringUtils;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Expert: source of values for basic function queries.
+ * At its default/simplest form, values - one per doc - are used as the score of that doc.
+ * Values are instantiated as
+ * {@link org.apache.lucene.search.function.DocValues DocValues} for a particular reader.
+ * ValueSource implementations differ in RAM requirements: it would always be a factor
+ * of the number of documents, but for each document the number of bytes can be 1, 2, 4, or 8.
+ *
+ * @author yonik
+ */
+public abstract class ValueSource implements Serializable {
+
+ /**
+ * Return the DocValues used by the function query.
+ * @param reader the IndexReader used to read these values.
+ * If any caching is involved, that caching would also be IndexReader based.
+ * @throws IOException for any error.
+ */
+ public abstract DocValues getValues(IndexReader reader) throws IOException;
+
+ /**
+ * description of field, used in explain()
+ */
+ public abstract String description();
+
+ /* (non-Javadoc) @see java.lang.Object#toString() */
+ public String toString() {
+ return ToStringUtils.simpleClassName(getClass()) + ":" + description();
+ }
+
+ /**
+ * Needed for possible caching of query results - used by {@link ValueSourceQuery#equals(Object)}.
+ * @see Object#equals(Object)
+ */
+ public abstract boolean equals(Object o);
+
+ /**
+ * Needed for possible caching of query results - used by {@link ValueSourceQuery#hashCode()}.
+ * @see Object#hashCode()
+ */
+ public abstract int hashCode();
+
+}
Property changes on: src/java/org/apache/lucene/search/function/ValueSource.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/package.html
===================================================================
--- src/java/org/apache/lucene/search/function/package.html (revision 0)
+++ src/java/org/apache/lucene/search/function/package.html (revision 0)
@@ -0,0 +1,189 @@
+
+
+
+ Note: code snippets here should work, but they were never really compiled... So,
+ tests sources TestCustomScoreQuery and TestFieldScoreQuery may also be useful.
+
+ Indexing:
+
+ Search:
+
+
+ Dividing the original score of each document by a square root of its docid
+ (just to demonstrate what it takes to manipulate scores this way)
+
+ For more informative debug info on the custom query, also override the toString() method:
+
+ Taking the square root of the original score and multiplying it by a "short field driven score", ie, the
+ short value that was indexed for the scored doc in a certain field:
+ reader.maxDoc() of the value each document
+ * has in the given field.
+ * @param reader Used to get field values.
+ * @param field Which field contains the single byte values.
+ * @return The values in the given field for each document.
+ * @throws IOException If any error occurs.
+ */
+ public byte[] getBytes (IndexReader reader, String field)
+ throws IOException;
+
+ /** Checks the internal cache for an appropriate entry, and if none is found,
+ * reads the terms in field as bytes and returns an array of
+ * size reader.maxDoc() of the value each document has in the
+ * given field.
+ * @param reader Used to get field values.
+ * @param field Which field contains the bytes.
+ * @param parser Computes byte for string values.
+ * @return The values in the given field for each document.
+ * @throws IOException If any error occurs.
+ */
+ public byte[] getBytes (IndexReader reader, String field, ByteParser parser)
+ throws IOException;
+
+ /** Checks the internal cache for an appropriate entry, and if none is
+ * found, reads the terms in field as shorts and returns an array
+ * of size reader.maxDoc() of the value each document
+ * has in the given field.
+ * @param reader Used to get field values.
+ * @param field Which field contains the shorts.
+ * @return The values in the given field for each document.
+ * @throws IOException If any error occurs.
+ */
+ public short[] getShorts (IndexReader reader, String field)
+ throws IOException;
+
+ /** Checks the internal cache for an appropriate entry, and if none is found,
+ * reads the terms in field as shorts and returns an array of
+ * size reader.maxDoc() of the value each document has in the
+ * given field.
+ * @param reader Used to get field values.
+ * @param field Which field contains the shorts.
+ * @param parser Computes short for string values.
+ * @return The values in the given field for each document.
+ * @throws IOException If any error occurs.
+ */
+ public short[] getShorts (IndexReader reader, String field, ShortParser parser)
+ throws IOException;
+
+ /** Checks the internal cache for an appropriate entry, and if none is
* found, reads the terms in field as integers and returns an array
* of size reader.maxDoc() of the value each document
* has in the given field.
Index: src/java/org/apache/lucene/search/function/IntFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/IntFieldSource.java (revision 0)
+++ src/java/org/apache/lucene/search/function/IntFieldSource.java (revision 0)
@@ -0,0 +1,102 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.DocValues;
+
+import java.io.IOException;
+
+/**
+ * Expert: obtains int field values from the
+ * {@link org.apache.lucene.search.FieldCache FieldCache}
+ * using getInts() and makes those values
+ * available as other numeric types, casting as needed.
+ *
+ * @see org.apache.lucene.search.function.FieldCacheSource for requirements
+ * on the field.
+ *
+ * @author yonik
+ */
+public class IntFieldSource extends FieldCacheSource {
+ private FieldCache.IntParser parser;
+
+ /**
+ * Create a cached int field source with default string-to-int parser.
+ */
+ public IntFieldSource(String field) {
+ this(field, null);
+ }
+
+ /**
+ * Create a cached int field source with a specific string-to-int parser.
+ */
+ public IntFieldSource(String field, FieldCache.IntParser parser) {
+ super(field);
+ this.parser = parser;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.ValueSource#description() */
+ public String description() {
+ return "int(" + super.description() + ')';
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#getCachedValues(org.apache.lucene.search.FieldCache, java.lang.String, org.apache.lucene.index.IndexReader) */
+ public DocValues getCachedFieldValues (FieldCache cache, String field, IndexReader reader) throws IOException {
+ final int[] arr = (parser==null) ?
+ cache.getInts(reader, field) :
+ cache.getInts(reader, field, parser);
+ return new DocValues() {
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#floatVal(int) */
+ public float floatVal(int doc) {
+ return (float) arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#intVal(int) */
+ public int intVal(int doc) {
+ return arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#toString(int) */
+ public String toString(int doc) {
+ return description() + '=' + intVal(doc);
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#getInnerArray() */
+ Object getInnerArray() {
+ return arr;
+ }
+ };
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceEquals(org.apache.lucene.search.function.FieldCacheSource) */
+ public boolean cachedFieldSourceEquals(FieldCacheSource o) {
+ if (o.getClass() != IntFieldSource.class) {
+ return false;
+ }
+ IntFieldSource other = (IntFieldSource)o;
+ return this.parser==null ?
+ other.parser==null :
+ this.parser.getClass() == other.parser.getClass();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceHashCode() */
+ public int cachedFieldSourceHashCode() {
+ return parser==null ?
+ Integer.class.hashCode() : parser.getClass().hashCode();
+ }
+
+}
Property changes on: src/java/org/apache/lucene/search/function/IntFieldSource.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/ValueSourceQuery.java
===================================================================
--- src/java/org/apache/lucene/search/function/ValueSourceQuery.java (revision 0)
+++ src/java/org/apache/lucene/search/function/ValueSourceQuery.java (revision 0)
@@ -0,0 +1,196 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.*;
+import org.apache.lucene.util.ToStringUtils;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Expert: A Query that sets the scores of document to the
+ * values obtained from a {@link org.apache.lucene.search.function.ValueSource ValueSource}.
+ * o is equal to this. */
+ public boolean equals(Object o) {
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ ValueSourceQuery other = (ValueSourceQuery)o;
+ return this.getBoost() == other.getBoost()
+ && this.valSrc.equals(other.valSrc);
+ }
+
+ /** Returns a hash code value for this object. */
+ public int hashCode() {
+ return (getClass().hashCode() + valSrc.hashCode()) ^ Float.floatToIntBits(getBoost());
+ }
+
+}
Property changes on: src/java/org/apache/lucene/search/function/ValueSourceQuery.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/ShortFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/ShortFieldSource.java (revision 0)
+++ src/java/org/apache/lucene/search/function/ShortFieldSource.java (revision 0)
@@ -0,0 +1,100 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.DocValues;
+
+import java.io.IOException;
+
+/**
+ * Expert: obtains short field values from the
+ * {@link org.apache.lucene.search.FieldCache FieldCache}
+ * using getShorts() and makes those values
+ * available as other numeric types, casting as needed.
+ *
+ * @see org.apache.lucene.search.function.FieldCacheSource for requirements
+ * on the field.
+ */
+public class ShortFieldSource extends FieldCacheSource {
+ private FieldCache.ShortParser parser;
+
+ /**
+ * Create a cached short field source with default string-to-short parser.
+ */
+ public ShortFieldSource(String field) {
+ this(field, null);
+ }
+
+ /**
+ * Create a cached short field source with a specific string-to-short parser.
+ */
+ public ShortFieldSource(String field, FieldCache.ShortParser parser) {
+ super(field);
+ this.parser = parser;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.ValueSource#description() */
+ public String description() {
+ return "short(" + super.description() + ')';
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#getCachedValues(org.apache.lucene.search.FieldCache, java.lang.String, org.apache.lucene.index.IndexReader) */
+ public DocValues getCachedFieldValues (FieldCache cache, String field, IndexReader reader) throws IOException {
+ final short[] arr = (parser==null) ?
+ cache.getShorts(reader, field) :
+ cache.getShorts(reader, field, parser);
+ return new DocValues() {
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#floatVal(int) */
+ public float floatVal(int doc) {
+ return (float) arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#intVal(int) */
+ public int intVal(int doc) {
+ return arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#toString(int) */
+ public String toString(int doc) {
+ return description() + '=' + intVal(doc);
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#getInnerArray() */
+ Object getInnerArray() {
+ return arr;
+ }
+ };
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceEquals(org.apache.lucene.search.function.FieldCacheSource) */
+ public boolean cachedFieldSourceEquals(FieldCacheSource o) {
+ if (o.getClass() != ShortFieldSource.class) {
+ return false;
+ }
+ ShortFieldSource other = (ShortFieldSource)o;
+ return this.parser==null ?
+ other.parser==null :
+ this.parser.getClass() == other.parser.getClass();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceHashCode() */
+ public int cachedFieldSourceHashCode() {
+ return parser==null ?
+ Short.class.hashCode() : parser.getClass().hashCode();
+ }
+
+}
Property changes on: src/java/org/apache/lucene/search/function/ShortFieldSource.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/DocValues.java
===================================================================
--- src/java/org/apache/lucene/search/function/DocValues.java (revision 0)
+++ src/java/org/apache/lucene/search/function/DocValues.java (revision 0)
@@ -0,0 +1,105 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.search.Explanation;
+
+/**
+ * Expert: represents field values as different types.
+ * Normally created via a
+ * {@link org.apache.lucene.search.function.ValueSource ValueSuorce}
+ * for a particular field and reader.
+ *
+ * @author yonik
+ */
+public abstract class DocValues {
+ /*
+ * DocValues is distinct from ValueSource because
+ * there needs to be an object created at query evaluation time that
+ * is not referenced by the query itself because:
+ * - Query objects should be MT safe
+ * - For caching, Query objects are often used as keys... you don't
+ * want the Query carrying around big objects
+ */
+
+ /**
+ * Return doc value as a float.
+ *
+ *
+ */
+ abstract Object getInnerArray();
+}
Property changes on: src/java/org/apache/lucene/search/function/DocValues.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/FieldScoreQuery.java
===================================================================
--- src/java/org/apache/lucene/search/function/FieldScoreQuery.java (revision 0)
+++ src/java/org/apache/lucene/search/function/FieldScoreQuery.java (revision 0)
@@ -0,0 +1,122 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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.
+ */
+
+/**
+ * A query that scores each document as the value of the numeric input field.
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+ public static class Type {
+
+ /** field values are interpreted as numeric byte values. */
+ public static final Type BYTE = new Type("byte");
+
+ /** field values are interpreted as numeric short values. */
+ public static final Type SHORT = new Type("short");
+
+ /** field values are interpreted as numeric int values. */
+ public static final Type INT = new Type("int");
+
+ /** field values are interpreted as numeric float values. */
+ public static final Type FLOAT = new Type("float");
+
+ private String typeName;
+ private Type (String name) {
+ this.typeName = name;
+ }
+ /*(non-Javadoc) @see java.lang.Object#toString() */
+ public String toString() {
+ return getClass().getName()+"::"+typeName;
+ }
+ }
+
+ /**
+ * Create a FieldScoreQuery - a query that scores each document as the value of the numeric input field.
+ * type param tells how to parse the field string values into a numeric score value.
+ * @param field the numeric field to be used.
+ * @param type the type of the field: either
+ * {@link Type#BYTE}, {@link Type#SHORT}, {@link Type#INT}, or {@link Type#FLOAT}.
+ */
+ public FieldScoreQuery(String field, Type type) {
+ super(getValueSource(field,type));
+ }
+
+ // create the appropriate (cached) field value source.
+ private static ValueSource getValueSource(String field, Type type) {
+ if (type == Type.BYTE) {
+ return new ByteFieldSource(field);
+ }
+ if (type == Type.SHORT) {
+ return new ShortFieldSource(field);
+ }
+ if (type == Type.INT) {
+ return new IntFieldSource(field);
+ }
+ if (type == Type.FLOAT) {
+ return new FloatFieldSource(field);
+ }
+ throw new IllegalArgumentException(type+" is not a known Field Score Qurey Type!");
+ }
+
+}
Property changes on: src/java/org/apache/lucene/search/function/FieldScoreQuery.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/FieldCacheSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/FieldCacheSource.java (revision 0)
+++ src/java/org/apache/lucene/search/function/FieldCacheSource.java (revision 0)
@@ -0,0 +1,100 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+
+/**
+ * Expert: A base class for ValueSource implementations that retrieve values for
+ * a single field from the {@link org.apache.lucene.search.FieldCache FieldCache}.
+ * Zero value.
+ *
+ * @author yonik
+ */
+public abstract class FieldCacheSource extends ValueSource {
+ private String field;
+ private FieldCache cache = FieldCache.DEFAULT;
+
+ /**
+ * Create a cached field source for the input field.
+ */
+ public FieldCacheSource(String field) {
+ this.field=field;
+ }
+
+ /* (non-Javadoc) @see org.apache.lucene.search.function.ValueSource#getValues(org.apache.lucene.index.IndexReader) */
+ public final DocValues getValues(IndexReader reader) throws IOException {
+ return getCachedFieldValues(cache, field, reader);
+ }
+
+ /* (non-Javadoc) @see org.apache.lucene.search.function.ValueSource#description() */
+ public String description() {
+ return field;
+ }
+
+ /**
+ * Return cached DocValues for input field and reader.
+ * @param cache FieldCache so that values of a field are loaded once per reader (RAM allwing)
+ * @param field Field for which values are required.
+ * @see ValueSource
+ */
+ public abstract DocValues getCachedFieldValues(FieldCache cache, String field, IndexReader reader) throws IOException;
+
+ /*(non-Javadoc) @see java.lang.Object#equals(java.lang.Object) */
+ public final boolean equals(Object o) {
+ if (!(o instanceof FieldCacheSource)) {
+ return false;
+ }
+ FieldCacheSource other = (FieldCacheSource) o;
+ return
+ this.cache == other.cache &&
+ this.field.equals(other.field) &&
+ cachedFieldSourceEquals(other);
+ }
+
+ /*(non-Javadoc) @see java.lang.Object#hashCode() */
+ public final int hashCode() {
+ return
+ cache.hashCode() +
+ field.hashCode() +
+ cachedFieldSourceHashCode();
+ }
+
+ /**
+ * Check if equals to another {@link FieldCacheSource}, already knowing that cache and field are equal.
+ * @see Object#equals(java.lang.Object)
+ */
+ public abstract boolean cachedFieldSourceEquals(FieldCacheSource other);
+
+ /**
+ * Return a hash code of a {@link FieldCacheSource}, without the hash-codes of the field
+ * and the cache (those are already added here).
+ * @see Object#hashCode()
+ */
+ public abstract int cachedFieldSourceHashCode();
+}
Property changes on: src/java/org/apache/lucene/search/function/FieldCacheSource.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/ByteFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/ByteFieldSource.java (revision 0)
+++ src/java/org/apache/lucene/search/function/ByteFieldSource.java (revision 0)
@@ -0,0 +1,100 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.DocValues;
+
+import java.io.IOException;
+
+/**
+ * Expert: obtains single byte field values from the
+ * {@link org.apache.lucene.search.FieldCache FieldCache}
+ * using getBytes() and makes those values
+ * available as other numeric types, casting as needed.
+ *
+ * @see org.apache.lucene.search.function.FieldCacheSource for requirements
+ * on the field.
+ */
+public class ByteFieldSource extends FieldCacheSource {
+ private FieldCache.ByteParser parser;
+
+ /**
+ * Create a cached byte field source with default string-to-byte parser.
+ */
+ public ByteFieldSource(String field) {
+ this(field, null);
+ }
+
+ /**
+ * Create a cached byte field source with a specific string-to-byte parser.
+ */
+ public ByteFieldSource(String field, FieldCache.ByteParser parser) {
+ super(field);
+ this.parser = parser;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.ValueSource#description() */
+ public String description() {
+ return "byte(" + super.description() + ')';
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#getCachedValues(org.apache.lucene.search.FieldCache, java.lang.String, org.apache.lucene.index.IndexReader) */
+ public DocValues getCachedFieldValues (FieldCache cache, String field, IndexReader reader) throws IOException {
+ final byte[] arr = (parser==null) ?
+ cache.getBytes(reader, field) :
+ cache.getBytes(reader, field, parser);
+ return new DocValues() {
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#floatVal(int) */
+ public float floatVal(int doc) {
+ return (float) arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#intVal(int) */
+ public int intVal(int doc) {
+ return arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#toString(int) */
+ public String toString(int doc) {
+ return description() + '=' + intVal(doc);
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#getInnerArray() */
+ Object getInnerArray() {
+ return arr;
+ }
+ };
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceEquals(org.apache.lucene.search.function.FieldCacheSource) */
+ public boolean cachedFieldSourceEquals(FieldCacheSource o) {
+ if (o.getClass() != ByteFieldSource.class) {
+ return false;
+ }
+ ByteFieldSource other = (ByteFieldSource)o;
+ return this.parser==null ?
+ other.parser==null :
+ this.parser.getClass() == other.parser.getClass();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceHashCode() */
+ public int cachedFieldSourceHashCode() {
+ return parser==null ?
+ Byte.class.hashCode() : parser.getClass().hashCode();
+ }
+
+}
Property changes on: src/java/org/apache/lucene/search/function/ByteFieldSource.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/FloatFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/FloatFieldSource.java (revision 0)
+++ src/java/org/apache/lucene/search/function/FloatFieldSource.java (revision 0)
@@ -0,0 +1,97 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.DocValues;
+
+import java.io.IOException;
+
+/**
+ * Expert: obtains float field values from the
+ * {@link org.apache.lucene.search.FieldCache FieldCache}
+ * using getFloats() and makes those values
+ * available as other numeric types, casting as needed.
+ *
+ * @see org.apache.lucene.search.function.FieldCacheSource for requirements
+ * on the field.
+ *
+ * @author yonik
+ */
+public class FloatFieldSource extends FieldCacheSource {
+ private FieldCache.FloatParser parser;
+
+ /**
+ * Create a cached float field source with default string-to-float parser.
+ */
+ public FloatFieldSource(String field) {
+ this(field, null);
+ }
+
+ /**
+ * Create a cached float field source with a specific string-to-float parser.
+ */
+ public FloatFieldSource(String field, FieldCache.FloatParser parser) {
+ super(field);
+ this.parser = parser;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.ValueSource#description() */
+ public String description() {
+ return "float(" + super.description() + ')';
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#getCachedValues(org.apache.lucene.search.FieldCache, java.lang.String, org.apache.lucene.index.IndexReader) */
+ public DocValues getCachedFieldValues (FieldCache cache, String field, IndexReader reader) throws IOException {
+ final float[] arr = (parser==null) ?
+ cache.getFloats(reader, field) :
+ cache.getFloats(reader, field, parser);
+ return new DocValues() {
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#floatVal(int) */
+ public float floatVal(int doc) {
+ return arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#toString(int) */
+ public String toString(int doc) {
+ return description() + '=' + arr[doc];
+ }
+ /*(non-Javadoc) @see org.apache.lucene.search.function.DocValues#getInnerArray() */
+ Object getInnerArray() {
+ return arr;
+ }
+ };
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceEquals(org.apache.lucene.search.function.FieldCacheSource) */
+ public boolean cachedFieldSourceEquals(FieldCacheSource o) {
+ if (o.getClass() != FloatFieldSource.class) {
+ return false;
+ }
+ FloatFieldSource other = (FloatFieldSource)o;
+ return this.parser==null ?
+ other.parser==null :
+ this.parser.getClass() == other.parser.getClass();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.function.FieldCacheSource#cachedFieldSourceHashCode() */
+ public int cachedFieldSourceHashCode() {
+ return parser==null ?
+ Float.class.hashCode() : parser.getClass().hashCode();
+ }
+}
\ No newline at end of file
Property changes on: src/java/org/apache/lucene/search/function/FloatFieldSource.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/java/org/apache/lucene/search/function/CustomScoreQuery.java
===================================================================
--- src/java/org/apache/lucene/search/function/CustomScoreQuery.java (revision 0)
+++ src/java/org/apache/lucene/search/function/CustomScoreQuery.java (revision 0)
@@ -0,0 +1,319 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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.io.IOException;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.ComplexExplanation;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.ToStringUtils;
+
+/**
+ * Query that sets document score as a programmatic function of (up to) two (sub) scores:
+ *
+ *
+ * Subclasses can modify the computation by overriding {@link #customScore(int, float, float)}.
+ */
+public class CustomScoreQuery extends Query {
+
+ private Query subQuery;
+ private ValueSourceQuery valSrcQuery; // optional, can be null
+ private boolean strict = false; // if true, valueSource part of query does not take part in weights normalization.
+
+ /**
+ * Create a CustomScoreQuery over input subQuery.
+ * @param subQuery the sub query whose scored is being customed. Must not be null.
+ */
+ public CustomScoreQuery(Query subQuery) {
+ this(subQuery,null);
+ }
+
+ /**
+ * Create a CustomScoreQuery over input subQuery and a {@link ValueSourceQuery}.
+ * @param subQuery the sub query whose scored is being customed. Must not be null.
+ * @param valSrcQuery a value source query whose scores are used in the custom score
+ * computation. For most simple/convineient use case this would be a
+ * {@link org.apache.lucene.search.function.FieldScoreQuery FieldScoreQuery}.
+ * This parameter is optional - it can be null.
+ */
+ public CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery) {
+ super();
+ this.subQuery = subQuery;
+ this.valSrcQuery = valSrcQuery;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
+ public Query rewrite(IndexReader reader) throws IOException {
+ subQuery = subQuery.rewrite(reader);
+ if (valSrcQuery!=null) {
+ valSrcQuery = (ValueSourceQuery) valSrcQuery.rewrite(reader);
+ }
+ return this;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#extractTerms(java.util.Set) */
+ public void extractTerms(Set terms) {
+ subQuery.extractTerms(terms);
+ if (valSrcQuery!=null) {
+ valSrcQuery.extractTerms(terms);
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#clone() */
+ public Object clone() {
+ CustomScoreQuery clone = (CustomScoreQuery)super.clone();
+ clone.subQuery = (Query) subQuery.clone();
+ if (valSrcQuery!=null) {
+ clone.valSrcQuery = (ValueSourceQuery) valSrcQuery.clone();
+ }
+ return clone;
+ }
+
+ /* (non-Javadoc) @see org.apache.lucene.search.Query#toString(java.lang.String) */
+ public String toString(String field) {
+ StringBuffer sb = new StringBuffer(ToStringUtils.simpleClassName(getClass()));
+ sb.append("{").append(subQuery.toString(field)).append("}");
+ if (valSrcQuery!=null) {
+ sb.append(",{").append(valSrcQuery.toString(field)).append("}");
+ }
+ sb.append(strict?" STRICT" : "");
+ return ToStringUtils.boost(getBoost(),sb.toString());
+ }
+
+ /** Returns true if o is equal to this. */
+ public boolean equals(Object o) {
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ CustomScoreQuery other = (CustomScoreQuery)o;
+ return this.getBoost() == other.getBoost()
+ && this.subQuery.equals(other.subQuery)
+ && (this.valSrcQuery==null ? other.valSrcQuery==null
+ : this.valSrcQuery.equals(other.valSrcQuery));
+ }
+
+ /** Returns a hash code value for this object. */
+ public int hashCode() {
+ int valSrcHash = valSrcQuery==null ? 0 : valSrcQuery.hashCode();
+ return (getClass().hashCode() + subQuery.hashCode() + valSrcHash) ^ Float.floatToIntBits(getBoost());
+ }
+
+ /**
+ * Compute a custom score by the subQuery score and the ValueSourceQuery score.
+ *
+ * ModifiedScore = valSrcScore * subQueryScore.
+ *
+ *
+ * @param doc id of scored doc.
+ * @param subQueryScore score of that doc by the subQuery.
+ * @param valSrcScore score of that doc by the ValueSourceQuery.
+ * @return custom score.
+ */
+ public float customScore(int doc, float subQueryScore, float valSrcScore) {
+ return valSrcScore * subQueryScore;
+ }
+
+ //=========================== W E I G H T ============================
+
+ private class CustomWeight implements Weight {
+ Searcher searcher;
+ Weight subQueryWeight;
+ Weight valSrcWeight; // optional
+ boolean qStrict;
+
+ public CustomWeight(Searcher searcher) throws IOException {
+ this.searcher = searcher;
+ this.subQueryWeight = subQuery.weight(searcher);
+ if (valSrcQuery!=null) {
+ this.valSrcWeight = valSrcQuery.createWeight(searcher);
+ }
+ this.qStrict = strict;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#getQuery() */
+ public Query getQuery() {
+ return CustomScoreQuery.this;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#getValue() */
+ public float getValue() {
+ return getBoost();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#sumOfSquaredWeights() */
+ public float sumOfSquaredWeights() throws IOException {
+ float sum = subQueryWeight.sumOfSquaredWeights();
+ if (valSrcWeight!=null) {
+ if (qStrict) {
+ valSrcWeight.sumOfSquaredWeights(); // score strictly by ValueSource
+ } else {
+ sum += valSrcWeight.sumOfSquaredWeights();
+ }
+ }
+ sum *= getBoost() * getBoost(); // boost each sub-weight
+ return sum ;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#normalize(float) */
+ public void normalize(float norm) {
+ norm *= getBoost(); // incorporate boost
+ subQueryWeight.normalize(norm);
+ if (valSrcWeight!=null) {
+ if (qStrict) {
+ valSrcWeight.normalize(1); // score strictly by ValueSource
+ } else {
+ valSrcWeight.normalize(norm);
+ }
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader) */
+ public Scorer scorer(IndexReader reader) throws IOException {
+ Scorer subQueryScorer = subQueryWeight.scorer(reader);
+ Scorer valSrcScorer = (valSrcWeight==null ? null : valSrcWeight.scorer(reader));
+ return new CustomScorer(getSimilarity(searcher), reader, this, subQueryScorer, valSrcScorer);
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int) */
+ public Explanation explain(IndexReader reader, int doc) throws IOException {
+ return scorer(reader).explain(doc);
+ }
+ }
+
+
+ //=========================== S C O R E R ============================
+
+ /**
+ * A scorer that applies a (callback) function on scores of the subQuery.
+ */
+ private class CustomScorer extends Scorer {
+ private final CustomWeight weight;
+ private final float qWeight;
+ private Scorer subQueryScorer;
+ private Scorer valSrcScorer; // optional
+ private IndexReader reader;
+
+ // constructor
+ private CustomScorer(Similarity similarity, IndexReader reader, CustomWeight w,
+ Scorer subQueryScorer, Scorer valSrcScorer) throws IOException {
+ super(similarity);
+ this.weight = w;
+ this.qWeight = w.getValue();
+ this.subQueryScorer = subQueryScorer;
+ this.valSrcScorer = valSrcScorer;
+ this.reader = reader;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#next() */
+ public boolean next() throws IOException {
+ boolean hasNext = subQueryScorer.next();
+ if (valSrcScorer!=null && hasNext) {
+ valSrcScorer.skipTo(subQueryScorer.doc());
+ }
+ return hasNext;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#doc() */
+ public int doc() {
+ return subQueryScorer.doc();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#score() */
+ public float score() throws IOException {
+ float valSrcScore = (valSrcScorer==null ? 1 : valSrcScorer.score());
+ return qWeight * customScore(subQueryScorer.doc(), subQueryScorer.score(), valSrcScore);
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#skipTo(int) */
+ public boolean skipTo(int target) throws IOException {
+ boolean hasNext = subQueryScorer.skipTo(target);
+ if (valSrcScorer!=null && hasNext) {
+ valSrcScorer.skipTo(subQueryScorer.doc());
+ }
+ return hasNext;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#explain(int) */
+ public Explanation explain(int doc) throws IOException {
+ Explanation subQueryExpl = weight.subQueryWeight.explain(reader,doc);
+ if (!subQueryExpl.isMatch()) {
+ return subQueryExpl;
+ }
+ // match
+ float subQueryScore = subQueryExpl.getValue();
+ Explanation valSrcExpl = valSrcScorer==null ? null : valSrcScorer.explain(doc);
+ float valSrcScore = valSrcExpl==null ? 1 : valSrcExpl.getValue();
+ float customScore = customScore(doc,subQueryScore,valSrcScore);
+ Explanation customExp = new ComplexExplanation(true, customScore, "customScore of:");
+
+ customExp.addDetail(subQueryExpl);
+ if (valSrcExpl!=null) {
+ customExp.addDetail(valSrcExpl);
+ }
+ float sc = qWeight * customScore;
+ Explanation res = new ComplexExplanation(
+ true, sc, CustomScoreQuery.this.toString() + ", product of:");
+ res.addDetail(customExp);
+ res.addDetail(new Explanation(qWeight, "queryBoost")); // actually using the q boost as q weight (== weight value)
+ return res;
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) */
+ protected Weight createWeight(Searcher searcher) throws IOException {
+ return new CustomWeight(searcher);
+ }
+
+ /**
+ * Checks if this is strict custom scoring.
+ * In strict custmo scoring, the ValueSource part of does not participate in weight normalization.
+ * This may be useful when one wants full control over how scores are modified, and does
+ * not care about normalizing by the ValueSource part.
+ * One particular case where this is useful if for testing this query.
+ * function package provides tight control over documents scores.
+
+
+
+
+
+
+
+
+
+
+
+ f = new Field("score", "7", Field.Store.NO, Field.Index.UN_TOKENIZED);
+ f.setOmitNorms(true);
+ d1.add(f);
+
+
+ Query q = new FieldScoreQuery("score", FieldScoreQuery.Type.BYTE);
+
+ Document d1 above would get a score of 7.
+
+ Query q = queryParser.parse("my query text");
+ CustomScoreQuery customQ = new CustomScoreQuery(q) {
+ public float customScore(int doc, float subQueryScore, float valSrcScore) {
+ return subQueryScore / Math.sqrt(docid);
+ }
+ };
+
+
+ CustomScoreQuery customQ = new CustomScoreQuery(q) {
+ public float customScore(int doc, float subQueryScore, float valSrcScore) {
+ return subQueryScore / Math.sqrt(docid);
+ }
+ public String toString(String field) {
+ return super.toString(field)+"(1/sqrt(docid))";
+ }
+ };
+
+
+ Query q = queryParser.parse("my query text");
+ FieldScoreQuery qf = new FieldScoreQuery("shortScore", FieldScoreQuery.Type.SHORT);
+ CustomScoreQuery customQ = new CustomScoreQuery(q,qf) {
+ public float customScore(int doc, float subQueryScore, float valSrcScore) {
+ return Math.sqrt(subQueryScore) * valSrcScore;
+ }
+ public String toString(String field) {
+ return super.toString(field)+"(sqrt(score) * shortScore)";
+ }
+ };
+
+
+