001: package org.apache.lucene.search.function;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import org.apache.lucene.index.IndexReader;
021: import org.apache.lucene.search.*;
022: import org.apache.lucene.util.ToStringUtils;
023:
024: import java.io.IOException;
025: import java.util.Set;
026:
027: /**
028: * Expert: A Query that sets the scores of document to the
029: * values obtained from a {@link org.apache.lucene.search.function.ValueSource ValueSource}.
030: * <p>
031: * The value source can be based on a (cached) value of an indexd field, but it
032: * can also be based on an external source, e.g. values read from an external database.
033: * <p>
034: * Score is set as: Score(doc,query) = query.getBoost()<sup>2</sup> * valueSource(doc).
035: *
036: * <p><font color="#FF0000">
037: * WARNING: The status of the <b>search.function</b> package is experimental.
038: * The APIs introduced here might change in the future and will not be
039: * supported anymore in such a case.</font>
040: *
041: * @author yonik
042: */
043: public class ValueSourceQuery extends Query {
044: ValueSource valSrc;
045:
046: /**
047: * Create a value source query
048: * @param valSrc provides the values defines the function to be used for scoring
049: */
050: public ValueSourceQuery(ValueSource valSrc) {
051: this .valSrc = valSrc;
052: }
053:
054: /*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
055: public Query rewrite(IndexReader reader) throws IOException {
056: return this ;
057: }
058:
059: /*(non-Javadoc) @see org.apache.lucene.search.Query#extractTerms(java.util.Set) */
060: public void extractTerms(Set terms) {
061: // no terms involved here
062: }
063:
064: private class ValueSourceWeight implements Weight {
065: Similarity similarity;
066: float queryNorm;
067: float queryWeight;
068:
069: public ValueSourceWeight(Searcher searcher) {
070: this .similarity = getSimilarity(searcher);
071: }
072:
073: /*(non-Javadoc) @see org.apache.lucene.search.Weight#getQuery() */
074: public Query getQuery() {
075: return ValueSourceQuery.this ;
076: }
077:
078: /*(non-Javadoc) @see org.apache.lucene.search.Weight#getValue() */
079: public float getValue() {
080: return queryWeight;
081: }
082:
083: /*(non-Javadoc) @see org.apache.lucene.search.Weight#sumOfSquaredWeights() */
084: public float sumOfSquaredWeights() throws IOException {
085: queryWeight = getBoost();
086: return queryWeight * queryWeight;
087: }
088:
089: /*(non-Javadoc) @see org.apache.lucene.search.Weight#normalize(float) */
090: public void normalize(float norm) {
091: this .queryNorm = norm;
092: queryWeight *= this .queryNorm;
093: }
094:
095: /*(non-Javadoc) @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader) */
096: public Scorer scorer(IndexReader reader) throws IOException {
097: return new ValueSourceScorer(similarity, reader, this );
098: }
099:
100: /*(non-Javadoc) @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int) */
101: public Explanation explain(IndexReader reader, int doc)
102: throws IOException {
103: return scorer(reader).explain(doc);
104: }
105: }
106:
107: /**
108: * A scorer that (simply) matches all documents, and scores each document with
109: * the value of the value soure in effect. As an example, if the value source
110: * is a (cached) field source, then value of that field in that document will
111: * be used. (assuming field is indexed for this doc, with a single token.)
112: */
113: private class ValueSourceScorer extends Scorer {
114: private final IndexReader reader;
115: private final ValueSourceWeight weight;
116: private final int maxDoc;
117: private final float qWeight;
118: private int doc = -1;
119: private final DocValues vals;
120:
121: // constructor
122: private ValueSourceScorer(Similarity similarity,
123: IndexReader reader, ValueSourceWeight w)
124: throws IOException {
125: super (similarity);
126: this .weight = w;
127: this .qWeight = w.getValue();
128: this .reader = reader;
129: this .maxDoc = reader.maxDoc();
130: // this is when/where the values are first created.
131: vals = valSrc.getValues(reader);
132: }
133:
134: /*(non-Javadoc) @see org.apache.lucene.search.Scorer#next() */
135: public boolean next() throws IOException {
136: for (;;) {
137: ++doc;
138: if (doc >= maxDoc) {
139: return false;
140: }
141: if (reader.isDeleted(doc)) {
142: continue;
143: }
144: return true;
145: }
146: }
147:
148: /*(non-Javadoc) @see org.apache.lucene.search.Scorer#doc()
149: */
150: public int doc() {
151: return doc;
152: }
153:
154: /*(non-Javadoc) @see org.apache.lucene.search.Scorer#score() */
155: public float score() throws IOException {
156: return qWeight * vals.floatVal(doc);
157: }
158:
159: /*(non-Javadoc) @see org.apache.lucene.search.Scorer#skipTo(int) */
160: public boolean skipTo(int target) throws IOException {
161: doc = target - 1;
162: return next();
163: }
164:
165: /*(non-Javadoc) @see org.apache.lucene.search.Scorer#explain(int) */
166: public Explanation explain(int doc) throws IOException {
167: float sc = qWeight * vals.floatVal(doc);
168:
169: Explanation result = new ComplexExplanation(true, sc,
170: ValueSourceQuery.this .toString() + ", product of:");
171:
172: result.addDetail(vals.explain(doc));
173: result.addDetail(new Explanation(getBoost(), "boost"));
174: result.addDetail(new Explanation(weight.queryNorm,
175: "queryNorm"));
176: return result;
177: }
178: }
179:
180: /*(non-Javadoc) @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) */
181: protected Weight createWeight(Searcher searcher) {
182: return new ValueSourceQuery.ValueSourceWeight(searcher);
183: }
184:
185: /* (non-Javadoc) @see org.apache.lucene.search.Query#toString(java.lang.String) */
186: public String toString(String field) {
187: return valSrc.toString() + ToStringUtils.boost(getBoost());
188: }
189:
190: /** Returns true if <code>o</code> is equal to this. */
191: public boolean equals(Object o) {
192: if (getClass() != o.getClass()) {
193: return false;
194: }
195: ValueSourceQuery other = (ValueSourceQuery) o;
196: return this .getBoost() == other.getBoost()
197: && this .valSrc.equals(other.valSrc);
198: }
199:
200: /** Returns a hash code value for this object. */
201: public int hashCode() {
202: return (getClass().hashCode() + valSrc.hashCode())
203: ^ Float.floatToIntBits(getBoost());
204: }
205:
206: }
|