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 }