pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / payloads / PayloadTermQuery.java
1 package org.apache.lucene.search.payloads;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import org.apache.lucene.index.Term;
21 import org.apache.lucene.index.IndexReader;
22 import org.apache.lucene.index.TermPositions;
23 import org.apache.lucene.search.Searcher;
24 import org.apache.lucene.search.Scorer;
25 import org.apache.lucene.search.Weight;
26 import org.apache.lucene.search.Similarity;
27 import org.apache.lucene.search.Explanation;
28 import org.apache.lucene.search.ComplexExplanation;
29 import org.apache.lucene.search.spans.TermSpans;
30 import org.apache.lucene.search.spans.SpanTermQuery;
31 import org.apache.lucene.search.spans.SpanWeight;
32 import org.apache.lucene.search.spans.SpanScorer;
33
34 import java.io.IOException;
35
36 /**
37  * This class is very similar to
38  * {@link org.apache.lucene.search.spans.SpanTermQuery} except that it factors
39  * in the value of the payload located at each of the positions where the
40  * {@link org.apache.lucene.index.Term} occurs.
41  * <p>
42  * In order to take advantage of this, you must override
43  * {@link org.apache.lucene.search.Similarity#scorePayload(int, String, int, int, byte[],int,int)}
44  * which returns 1 by default.
45  * <p>
46  * Payload scores are aggregated using a pluggable {@link PayloadFunction}.
47  **/
48 public class PayloadTermQuery extends SpanTermQuery {
49   protected PayloadFunction function;
50   private boolean includeSpanScore;
51
52   public PayloadTermQuery(Term term, PayloadFunction function) {
53     this(term, function, true);
54   }
55
56   public PayloadTermQuery(Term term, PayloadFunction function,
57       boolean includeSpanScore) {
58     super(term);
59     this.function = function;
60     this.includeSpanScore = includeSpanScore;
61   }
62
63   @Override
64   public Weight createWeight(Searcher searcher) throws IOException {
65     return new PayloadTermWeight(this, searcher);
66   }
67
68   protected class PayloadTermWeight extends SpanWeight {
69
70     public PayloadTermWeight(PayloadTermQuery query, Searcher searcher)
71         throws IOException {
72       super(query, searcher);
73     }
74
75     @Override
76     public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder,
77         boolean topScorer) throws IOException {
78       return new PayloadTermSpanScorer((TermSpans) query.getSpans(reader),
79           this, similarity, reader.norms(query.getField()));
80     }
81     
82     @Override
83     public Explanation explain(IndexReader reader, int doc) throws IOException {
84       if (includeSpanScore) {
85         return super.explain(reader, doc);
86       } else {
87         // if we don't include the span score, we need to return our scorer's explanation only
88         PayloadTermSpanScorer scorer = (PayloadTermSpanScorer) scorer(reader, true, false);
89         return scorer.explain(doc);
90       }
91     }
92
93     protected class PayloadTermSpanScorer extends SpanScorer {
94       // TODO: is this the best way to allocate this?
95       protected byte[] payload = new byte[256];
96       protected TermPositions positions;
97       protected float payloadScore;
98       protected int payloadsSeen;
99
100       public PayloadTermSpanScorer(TermSpans spans, Weight weight,
101           Similarity similarity, byte[] norms) throws IOException {
102         super(spans, weight, similarity, norms);
103         positions = spans.getPositions();
104       }
105
106       @Override
107       protected boolean setFreqCurrentDoc() throws IOException {
108         if (!more) {
109           return false;
110         }
111         doc = spans.doc();
112         freq = 0.0f;
113         payloadScore = 0;
114         payloadsSeen = 0;
115         Similarity similarity1 = getSimilarity();
116         while (more && doc == spans.doc()) {
117           int matchLength = spans.end() - spans.start();
118
119           freq += similarity1.sloppyFreq(matchLength);
120           processPayload(similarity1);
121
122           more = spans.next();// this moves positions to the next match in this
123                               // document
124         }
125         return more || (freq != 0);
126       }
127
128       protected void processPayload(Similarity similarity) throws IOException {
129         if (positions.isPayloadAvailable()) {
130           payload = positions.getPayload(payload, 0);
131           payloadScore = function.currentScore(doc, term.field(),
132               spans.start(), spans.end(), payloadsSeen, payloadScore,
133               similarity.scorePayload(doc, term.field(), spans.start(), spans
134                   .end(), payload, 0, positions.getPayloadLength()));
135           payloadsSeen++;
136
137         } else {
138           // zero out the payload?
139         }
140       }
141
142       /**
143        * 
144        * @return {@link #getSpanScore()} * {@link #getPayloadScore()}
145        * @throws IOException
146        */
147       @Override
148       public float score() throws IOException {
149
150         return includeSpanScore ? getSpanScore() * getPayloadScore()
151             : getPayloadScore();
152       }
153
154       /**
155        * Returns the SpanScorer score only.
156        * <p/>
157        * Should not be overridden without good cause!
158        * 
159        * @return the score for just the Span part w/o the payload
160        * @throws IOException
161        * 
162        * @see #score()
163        */
164       protected float getSpanScore() throws IOException {
165         return super.score();
166       }
167
168       /**
169        * The score for the payload
170        * 
171        * @return The score, as calculated by
172        *         {@link PayloadFunction#docScore(int, String, int, float)}
173        */
174       protected float getPayloadScore() {
175         return function.docScore(doc, term.field(), payloadsSeen, payloadScore);
176       }
177
178       @Override
179       protected Explanation explain(final int doc) throws IOException {
180         Explanation nonPayloadExpl = super.explain(doc);
181         
182         // QUESTION: Is there a way to avoid this skipTo call? We need to know
183         // whether to load the payload or not
184         Explanation payloadBoost = new Explanation();
185
186         float payloadScore = getPayloadScore();
187         payloadBoost.setValue(payloadScore);
188         // GSI: I suppose we could toString the payload, but I don't think that
189         // would be a good idea
190         payloadBoost.setDescription("scorePayload(...)");
191         
192         ComplexExplanation result = new ComplexExplanation();
193         if (includeSpanScore) {
194           result.addDetail(nonPayloadExpl);
195           result.addDetail(payloadBoost);
196           result.setValue(nonPayloadExpl.getValue() * payloadScore);
197           result.setDescription("btq, product of:");
198         } else {
199           result.addDetail(payloadBoost);
200           result.setValue(payloadScore);
201           result.setDescription("btq(includeSpanScore=false), result of:");
202         }
203         result.setMatch(nonPayloadExpl.getValue() == 0 ? Boolean.FALSE
204             : Boolean.TRUE); // LUCENE-1303
205         return result;
206       }
207
208     }
209   }
210
211   @Override
212   public int hashCode() {
213     final int prime = 31;
214     int result = super.hashCode();
215     result = prime * result + ((function == null) ? 0 : function.hashCode());
216     result = prime * result + (includeSpanScore ? 1231 : 1237);
217     return result;
218   }
219
220   @Override
221   public boolean equals(Object obj) {
222     if (this == obj)
223       return true;
224     if (!super.equals(obj))
225       return false;
226     if (getClass() != obj.getClass())
227       return false;
228     PayloadTermQuery other = (PayloadTermQuery) obj;
229     if (function == null) {
230       if (other.function != null)
231         return false;
232     } else if (!function.equals(other.function))
233       return false;
234     if (includeSpanScore != other.includeSpanScore)
235       return false;
236     return true;
237   }
238
239 }