add --shared
[pylucene.git] / lucene-java-3.4.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     protected class PayloadTermSpanScorer extends SpanScorer {
83       // TODO: is this the best way to allocate this?
84       protected byte[] payload = new byte[256];
85       protected TermPositions positions;
86       protected float payloadScore;
87       protected int payloadsSeen;
88
89       public PayloadTermSpanScorer(TermSpans spans, Weight weight,
90           Similarity similarity, byte[] norms) throws IOException {
91         super(spans, weight, similarity, norms);
92         positions = spans.getPositions();
93       }
94
95       @Override
96       protected boolean setFreqCurrentDoc() throws IOException {
97         if (!more) {
98           return false;
99         }
100         doc = spans.doc();
101         freq = 0.0f;
102         payloadScore = 0;
103         payloadsSeen = 0;
104         Similarity similarity1 = getSimilarity();
105         while (more && doc == spans.doc()) {
106           int matchLength = spans.end() - spans.start();
107
108           freq += similarity1.sloppyFreq(matchLength);
109           processPayload(similarity1);
110
111           more = spans.next();// this moves positions to the next match in this
112                               // document
113         }
114         return more || (freq != 0);
115       }
116
117       protected void processPayload(Similarity similarity) throws IOException {
118         if (positions.isPayloadAvailable()) {
119           payload = positions.getPayload(payload, 0);
120           payloadScore = function.currentScore(doc, term.field(),
121               spans.start(), spans.end(), payloadsSeen, payloadScore,
122               similarity.scorePayload(doc, term.field(), spans.start(), spans
123                   .end(), payload, 0, positions.getPayloadLength()));
124           payloadsSeen++;
125
126         } else {
127           // zero out the payload?
128         }
129       }
130
131       /**
132        * 
133        * @return {@link #getSpanScore()} * {@link #getPayloadScore()}
134        * @throws IOException
135        */
136       @Override
137       public float score() throws IOException {
138
139         return includeSpanScore ? getSpanScore() * getPayloadScore()
140             : getPayloadScore();
141       }
142
143       /**
144        * Returns the SpanScorer score only.
145        * <p/>
146        * Should not be overridden without good cause!
147        * 
148        * @return the score for just the Span part w/o the payload
149        * @throws IOException
150        * 
151        * @see #score()
152        */
153       protected float getSpanScore() throws IOException {
154         return super.score();
155       }
156
157       /**
158        * The score for the payload
159        * 
160        * @return The score, as calculated by
161        *         {@link PayloadFunction#docScore(int, String, int, float)}
162        */
163       protected float getPayloadScore() {
164         return function.docScore(doc, term.field(), payloadsSeen, payloadScore);
165       }
166
167       @Override
168       protected Explanation explain(final int doc) throws IOException {
169         ComplexExplanation result = new ComplexExplanation();
170         Explanation nonPayloadExpl = super.explain(doc);
171         result.addDetail(nonPayloadExpl);
172         // QUESTION: Is there a way to avoid this skipTo call? We need to know
173         // whether to load the payload or not
174         Explanation payloadBoost = new Explanation();
175         result.addDetail(payloadBoost);
176
177         float payloadScore = getPayloadScore();
178         payloadBoost.setValue(payloadScore);
179         // GSI: I suppose we could toString the payload, but I don't think that
180         // would be a good idea
181         payloadBoost.setDescription("scorePayload(...)");
182         result.setValue(nonPayloadExpl.getValue() * payloadScore);
183         result.setDescription("btq, product of:");
184         result.setMatch(nonPayloadExpl.getValue() == 0 ? Boolean.FALSE
185             : Boolean.TRUE); // LUCENE-1303
186         return result;
187       }
188
189     }
190   }
191
192   @Override
193   public int hashCode() {
194     final int prime = 31;
195     int result = super.hashCode();
196     result = prime * result + ((function == null) ? 0 : function.hashCode());
197     result = prime * result + (includeSpanScore ? 1231 : 1237);
198     return result;
199   }
200
201   @Override
202   public boolean equals(Object obj) {
203     if (this == obj)
204       return true;
205     if (!super.equals(obj))
206       return false;
207     if (getClass() != obj.getClass())
208       return false;
209     PayloadTermQuery other = (PayloadTermQuery) obj;
210     if (function == null) {
211       if (other.function != null)
212         return false;
213     } else if (!function.equals(other.function))
214       return false;
215     if (includeSpanScore != other.includeSpanScore)
216       return false;
217     return true;
218   }
219
220 }