001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.search.function;
017:
018: import org.apache.lucene.index.IndexReader;
019: import org.apache.lucene.search.*;
020: import java.io.IOException;
021: import java.util.Set;
022:
023: /**
024: * Returns a score for each document based on a ValueSource,
025: * often some function of the value of a field.
026: *
027: * @author yonik
028: * @version $Id: FunctionQuery.java 472574 2006-11-08 18:25:52Z yonik $
029: */
030: public class FunctionQuery extends Query {
031: ValueSource func;
032:
033: /**
034: *
035: * @param func defines the function to be used for scoring
036: */
037: public FunctionQuery(ValueSource func) {
038: this .func = func;
039: }
040:
041: public Query rewrite(IndexReader reader) throws IOException {
042: return this ;
043: }
044:
045: public void extractTerms(Set terms) {
046: }
047:
048: protected class FunctionWeight implements Weight {
049: Searcher searcher;
050: float queryNorm;
051: float queryWeight;
052:
053: public FunctionWeight(Searcher searcher) {
054: this .searcher = searcher;
055: }
056:
057: public Query getQuery() {
058: return FunctionQuery.this ;
059: }
060:
061: public float getValue() {
062: return queryWeight;
063: }
064:
065: public float sumOfSquaredWeights() throws IOException {
066: queryWeight = getBoost();
067: return queryWeight * queryWeight;
068: }
069:
070: public void normalize(float norm) {
071: this .queryNorm = norm;
072: queryWeight *= this .queryNorm;
073: }
074:
075: public Scorer scorer(IndexReader reader) throws IOException {
076: return new AllScorer(getSimilarity(searcher), reader, this );
077: }
078:
079: public Explanation explain(IndexReader reader, int doc)
080: throws IOException {
081: return scorer(reader).explain(doc);
082: }
083: }
084:
085: protected class AllScorer extends Scorer {
086: final IndexReader reader;
087: final FunctionWeight weight;
088: final int maxDoc;
089: final float qWeight;
090: int doc = -1;
091: final DocValues vals;
092:
093: public AllScorer(Similarity similarity, IndexReader reader,
094: FunctionWeight w) throws IOException {
095: super (similarity);
096: this .weight = w;
097: this .qWeight = w.getValue();
098: this .reader = reader;
099: this .maxDoc = reader.maxDoc();
100: vals = func.getValues(reader);
101: }
102:
103: // instead of matching all docs, we could also embed a query.
104: // the score could either ignore the subscore, or boost it.
105: // Containment: floatline(foo:myTerm, "myFloatField", 1.0, 0.0f)
106: // Boost: foo:myTerm^floatline("myFloatField",1.0,0.0f)
107: public boolean next() throws IOException {
108: for (;;) {
109: ++doc;
110: if (doc >= maxDoc) {
111: return false;
112: }
113: if (reader.isDeleted(doc))
114: continue;
115: // todo: maybe allow score() to throw a specific exception
116: // and continue on to the next document if it is thrown...
117: // that may be useful, but exceptions aren't really good
118: // for flow control.
119: return true;
120: }
121: }
122:
123: public int doc() {
124: return doc;
125: }
126:
127: public float score() throws IOException {
128: return qWeight * vals.floatVal(doc);
129: }
130:
131: public boolean skipTo(int target) throws IOException {
132: doc = target - 1;
133: return next();
134: }
135:
136: public Explanation explain(int doc) throws IOException {
137: float sc = qWeight * vals.floatVal(doc);
138:
139: Explanation result = new ComplexExplanation(true, sc,
140: "FunctionQuery(" + func + "), product of:");
141:
142: result.addDetail(vals.explain(doc));
143: result.addDetail(new Explanation(getBoost(), "boost"));
144: result.addDetail(new Explanation(weight.queryNorm,
145: "queryNorm"));
146: return result;
147: }
148: }
149:
150: protected Weight createWeight(Searcher searcher) {
151: return new FunctionQuery.FunctionWeight(searcher);
152: }
153:
154: /** Prints a user-readable version of this query. */
155: public String toString(String field) {
156: float boost = getBoost();
157: return (boost != 1.0 ? "(" : "") + func.toString()
158: + (getBoost() == 0 ? "" : ")^" + getBoost());
159: }
160:
161: /** Returns true if <code>o</code> is equal to this. */
162: public boolean equals(Object o) {
163: if (FunctionQuery.class != o.getClass())
164: return false;
165: FunctionQuery other = (FunctionQuery) o;
166: return this .getBoost() == other.getBoost()
167: && this .func.equals(other.func);
168: }
169:
170: /** Returns a hash code value for this object. */
171: public int hashCode() {
172: return func.hashCode() ^ Float.floatToIntBits(getBoost());
173: }
174:
175: }
|