1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 
20 using IndexReader = Lucene.Net.Index.IndexReader;
21 using Term = Lucene.Net.Index.Term;
22 using TermPositions = Lucene.Net.Index.TermPositions;
23 using ComplexExplanation = Lucene.Net.Search.ComplexExplanation;
24 using Explanation = Lucene.Net.Search.Explanation;
25 using Scorer = Lucene.Net.Search.Scorer;
26 using Searcher = Lucene.Net.Search.Searcher;
27 using Similarity = Lucene.Net.Search.Similarity;
28 using Weight = Lucene.Net.Search.Weight;
29 using SpanScorer = Lucene.Net.Search.Spans.SpanScorer;
30 using SpanTermQuery = Lucene.Net.Search.Spans.SpanTermQuery;
31 using SpanWeight = Lucene.Net.Search.Spans.SpanWeight;
32 using TermSpans = Lucene.Net.Search.Spans.TermSpans;
33 
34 namespace Lucene.Net.Search.Payloads
35 {
36 
37 	/// <summary> This class is very similar to
38 	/// <see cref="Lucene.Net.Search.Spans.SpanTermQuery" /> except that it factors
39 	/// in the value of the payload located at each of the positions where the
40 	/// <see cref="Lucene.Net.Index.Term" /> occurs.
41 	/// <p/>
42 	/// In order to take advantage of this, you must override
43     /// <see cref="Lucene.Net.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 <see cref="PayloadFunction" />.
47 	///
48 	/// </summary>
49 	[Serializable]
50 	public class PayloadTermQuery:SpanTermQuery
51 	{
52 		protected internal PayloadFunction function;
53 		private bool includeSpanScore;
54 
PayloadTermQuery(Term term, PayloadFunction function)55 		public PayloadTermQuery(Term term, PayloadFunction function):this(term, function, true)
56 		{
57 		}
58 
PayloadTermQuery(Term term, PayloadFunction function, bool includeSpanScore)59 		public PayloadTermQuery(Term term, PayloadFunction function, bool includeSpanScore):base(term)
60 		{
61 			this.function = function;
62 			this.includeSpanScore = includeSpanScore;
63 		}
64 
CreateWeight(Searcher searcher)65 		public override Weight CreateWeight(Searcher searcher)
66 		{
67 			return new PayloadTermWeight(this, this, searcher);
68 		}
69 
70 		[Serializable]
71 		protected internal class PayloadTermWeight:SpanWeight
72 		{
InitBlock(PayloadTermQuery enclosingInstance)73 			private void  InitBlock(PayloadTermQuery enclosingInstance)
74 			{
75 				this.enclosingInstance = enclosingInstance;
76 			}
77 			private PayloadTermQuery enclosingInstance;
78 			public PayloadTermQuery Enclosing_Instance
79 			{
80 				get
81 				{
82 					return enclosingInstance;
83 				}
84 
85 			}
86 
PayloadTermWeight(PayloadTermQuery enclosingInstance, PayloadTermQuery query, Searcher searcher)87 			public PayloadTermWeight(PayloadTermQuery enclosingInstance, PayloadTermQuery query, Searcher searcher):base(query, searcher)
88 			{
89 				InitBlock(enclosingInstance);
90 			}
91 
Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)92 			public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
93 			{
94 				return new PayloadTermSpanScorer(this, (TermSpans) internalQuery.GetSpans(reader), this, similarity, reader.Norms(internalQuery.Field));
95 			}
96 
97 			protected internal class PayloadTermSpanScorer:SpanScorer
98 			{
InitBlock(PayloadTermWeight enclosingInstance)99 				private void  InitBlock(PayloadTermWeight enclosingInstance)
100 				{
101 					this.enclosingInstance = enclosingInstance;
102 				}
103 				private PayloadTermWeight enclosingInstance;
104 				public PayloadTermWeight Enclosing_Instance
105 				{
106 					get
107 					{
108 						return enclosingInstance;
109 					}
110 
111 				}
112 				// TODO: is this the best way to allocate this?
113 				protected internal byte[] payload = new byte[256];
114 				protected internal TermPositions positions;
115 				protected internal float payloadScore;
116 				protected internal int payloadsSeen;
117 
PayloadTermSpanScorer(PayloadTermWeight enclosingInstance, TermSpans spans, Weight weight, Similarity similarity, byte[] norms)118 				public PayloadTermSpanScorer(PayloadTermWeight enclosingInstance, TermSpans spans, Weight weight, Similarity similarity, byte[] norms):base(spans, weight, similarity, norms)
119 				{
120 					InitBlock(enclosingInstance);
121 					positions = spans.Positions;
122 				}
123 
SetFreqCurrentDoc()124 				public /*protected internal*/ override bool SetFreqCurrentDoc()
125 				{
126 					if (!more)
127 					{
128 						return false;
129 					}
130 					doc = spans.Doc();
131 					freq = 0.0f;
132 					payloadScore = 0;
133 					payloadsSeen = 0;
134 					Similarity similarity1 = Similarity;
135 					while (more && doc == spans.Doc())
136 					{
137 						int matchLength = spans.End() - spans.Start();
138 
139 						freq += similarity1.SloppyFreq(matchLength);
140 						ProcessPayload(similarity1);
141 
142 						more = spans.Next(); // this moves positions to the next match in this
143 						// document
144 					}
145 					return more || (freq != 0);
146 				}
147 
ProcessPayload(Similarity similarity)148 				protected internal virtual void  ProcessPayload(Similarity similarity)
149 				{
150 					if (positions.IsPayloadAvailable)
151 					{
152 						payload = positions.GetPayload(payload, 0);
153 						payloadScore = Enclosing_Instance.Enclosing_Instance.function.CurrentScore(doc, Enclosing_Instance.Enclosing_Instance.internalTerm.Field, spans.Start(), spans.End(), payloadsSeen, payloadScore, similarity.ScorePayload(doc, Enclosing_Instance.Enclosing_Instance.internalTerm.Field, spans.Start(), spans.End(), payload, 0, positions.PayloadLength));
154 						payloadsSeen++;
155 					}
156 					else
157 					{
158 						// zero out the payload?
159 					}
160 				}
161 
162 				/// <summary> </summary>
163 				/// <returns> <see cref="GetSpanScore()" /> * <see cref="GetPayloadScore()" />
164 				/// </returns>
165 				/// <throws>  IOException </throws>
Score()166 				public override float Score()
167 				{
168 
169 					return Enclosing_Instance.Enclosing_Instance.includeSpanScore?GetSpanScore() * GetPayloadScore():GetPayloadScore();
170 				}
171 
172 				/// <summary> Returns the SpanScorer score only.
173 				/// <p/>
174 				/// Should not be overriden without good cause!
175 				///
176 				/// </summary>
177 				/// <returns> the score for just the Span part w/o the payload
178 				/// </returns>
179 				/// <throws>  IOException </throws>
180 				/// <summary>
181 				/// </summary>
182 				/// <seealso cref="Score()">
183 				/// </seealso>
184                 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
GetSpanScore()185                 protected internal virtual float GetSpanScore()
186 				{
187 					return base.Score();
188 				}
189 
190 				/// <summary> The score for the payload
191 				///
192 				/// </summary>
193 				/// <returns> The score, as calculated by
194 				/// <see cref="PayloadFunction.DocScore(int, String, int, float)" />
195 				/// </returns>
196                 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
GetPayloadScore()197                 protected internal virtual float GetPayloadScore()
198 				{
199 					return Enclosing_Instance.Enclosing_Instance.function.DocScore(doc, Enclosing_Instance.Enclosing_Instance.internalTerm.Field, payloadsSeen, payloadScore);
200 				}
201 
Explain(int doc)202 				protected internal override Explanation Explain(int doc)
203 				{
204 					ComplexExplanation result = new ComplexExplanation();
205 					Explanation nonPayloadExpl = base.Explain(doc);
206 					result.AddDetail(nonPayloadExpl);
207 					// QUESTION: Is there a way to avoid this skipTo call? We need to know
208 					// whether to load the payload or not
209 					Explanation payloadBoost = new Explanation();
210 					result.AddDetail(payloadBoost);
211 
212 					float payloadScore = GetPayloadScore();
213 					payloadBoost.Value = payloadScore;
214 					// GSI: I suppose we could toString the payload, but I don't think that
215 					// would be a good idea
216 					payloadBoost.Description = "scorePayload(...)";
217 					result.Value = nonPayloadExpl.Value * payloadScore;
218 					result.Description = "btq, product of:";
219 					result.Match = nonPayloadExpl.Value == 0?false:true; // LUCENE-1303
220 					return result;
221 				}
222 			}
223 		}
224 
GetHashCode()225 		public override int GetHashCode()
226 		{
227 			int prime = 31;
228 			int result = base.GetHashCode();
229 			result = prime * result + ((function == null)?0:function.GetHashCode());
230 			result = prime * result + (includeSpanScore?1231:1237);
231 			return result;
232 		}
233 
Equals(System.Object obj)234 		public  override bool Equals(System.Object obj)
235 		{
236 			if (this == obj)
237 				return true;
238 			if (!base.Equals(obj))
239 				return false;
240 			if (GetType() != obj.GetType())
241 				return false;
242 			PayloadTermQuery other = (PayloadTermQuery) obj;
243 			if (function == null)
244 			{
245 				if (other.function != null)
246 					return false;
247 			}
248 			else if (!function.Equals(other.function))
249 				return false;
250 			if (includeSpanScore != other.includeSpanScore)
251 				return false;
252 			return true;
253 		}
254 	}
255 }