/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using IndexReader = Lucene.Net.Index.IndexReader;
using Term = Lucene.Net.Index.Term;
using TermPositions = Lucene.Net.Index.TermPositions;
using ComplexExplanation = Lucene.Net.Search.ComplexExplanation;
using Explanation = Lucene.Net.Search.Explanation;
using Scorer = Lucene.Net.Search.Scorer;
using Searcher = Lucene.Net.Search.Searcher;
using Similarity = Lucene.Net.Search.Similarity;
using Weight = Lucene.Net.Search.Weight;
using SpanScorer = Lucene.Net.Search.Spans.SpanScorer;
using SpanTermQuery = Lucene.Net.Search.Spans.SpanTermQuery;
using SpanWeight = Lucene.Net.Search.Spans.SpanWeight;
using TermSpans = Lucene.Net.Search.Spans.TermSpans;
namespace Lucene.Net.Search.Payloads
{
/// This class is very similar to
/// except that it factors
/// in the value of the payload located at each of the positions where the
/// occurs.
///
/// In order to take advantage of this, you must override
///
/// which returns 1 by default.
///
/// Payload scores are aggregated using a pluggable .
///
///
[Serializable]
public class PayloadTermQuery:SpanTermQuery
{
protected internal PayloadFunction function;
private bool includeSpanScore;
public PayloadTermQuery(Term term, PayloadFunction function):this(term, function, true)
{
}
public PayloadTermQuery(Term term, PayloadFunction function, bool includeSpanScore):base(term)
{
this.function = function;
this.includeSpanScore = includeSpanScore;
}
public override Weight CreateWeight(Searcher searcher)
{
return new PayloadTermWeight(this, this, searcher);
}
[Serializable]
protected internal class PayloadTermWeight:SpanWeight
{
private void InitBlock(PayloadTermQuery enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private PayloadTermQuery enclosingInstance;
public PayloadTermQuery Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
public PayloadTermWeight(PayloadTermQuery enclosingInstance, PayloadTermQuery query, Searcher searcher):base(query, searcher)
{
InitBlock(enclosingInstance);
}
public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
{
return new PayloadTermSpanScorer(this, (TermSpans) internalQuery.GetSpans(reader), this, similarity, reader.Norms(internalQuery.Field));
}
protected internal class PayloadTermSpanScorer:SpanScorer
{
private void InitBlock(PayloadTermWeight enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private PayloadTermWeight enclosingInstance;
public PayloadTermWeight Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
// TODO: is this the best way to allocate this?
protected internal byte[] payload = new byte[256];
protected internal TermPositions positions;
protected internal float payloadScore;
protected internal int payloadsSeen;
public PayloadTermSpanScorer(PayloadTermWeight enclosingInstance, TermSpans spans, Weight weight, Similarity similarity, byte[] norms):base(spans, weight, similarity, norms)
{
InitBlock(enclosingInstance);
positions = spans.Positions;
}
public /*protected internal*/ override bool SetFreqCurrentDoc()
{
if (!more)
{
return false;
}
doc = spans.Doc();
freq = 0.0f;
payloadScore = 0;
payloadsSeen = 0;
Similarity similarity1 = Similarity;
while (more && doc == spans.Doc())
{
int matchLength = spans.End() - spans.Start();
freq += similarity1.SloppyFreq(matchLength);
ProcessPayload(similarity1);
more = spans.Next(); // this moves positions to the next match in this
// document
}
return more || (freq != 0);
}
protected internal virtual void ProcessPayload(Similarity similarity)
{
if (positions.IsPayloadAvailable)
{
payload = positions.GetPayload(payload, 0);
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));
payloadsSeen++;
}
else
{
// zero out the payload?
}
}
///
/// *
///
/// IOException
public override float Score()
{
return Enclosing_Instance.Enclosing_Instance.includeSpanScore?GetSpanScore() * GetPayloadScore():GetPayloadScore();
}
/// Returns the SpanScorer score only.
///
/// Should not be overriden without good cause!
///
///
/// the score for just the Span part w/o the payload
///
/// IOException
///
///
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected internal virtual float GetSpanScore()
{
return base.Score();
}
/// The score for the payload
///
///
/// The score, as calculated by
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected internal virtual float GetPayloadScore()
{
return Enclosing_Instance.Enclosing_Instance.function.DocScore(doc, Enclosing_Instance.Enclosing_Instance.internalTerm.Field, payloadsSeen, payloadScore);
}
protected internal override Explanation Explain(int doc)
{
ComplexExplanation result = new ComplexExplanation();
Explanation nonPayloadExpl = base.Explain(doc);
result.AddDetail(nonPayloadExpl);
// QUESTION: Is there a way to avoid this skipTo call? We need to know
// whether to load the payload or not
Explanation payloadBoost = new Explanation();
result.AddDetail(payloadBoost);
float payloadScore = GetPayloadScore();
payloadBoost.Value = payloadScore;
// GSI: I suppose we could toString the payload, but I don't think that
// would be a good idea
payloadBoost.Description = "scorePayload(...)";
result.Value = nonPayloadExpl.Value * payloadScore;
result.Description = "btq, product of:";
result.Match = nonPayloadExpl.Value == 0?false:true; // LUCENE-1303
return result;
}
}
}
public override int GetHashCode()
{
int prime = 31;
int result = base.GetHashCode();
result = prime * result + ((function == null)?0:function.GetHashCode());
result = prime * result + (includeSpanScore?1231:1237);
return result;
}
public override bool Equals(System.Object obj)
{
if (this == obj)
return true;
if (!base.Equals(obj))
return false;
if (GetType() != obj.GetType())
return false;
PayloadTermQuery other = (PayloadTermQuery) obj;
if (function == null)
{
if (other.function != null)
return false;
}
else if (!function.Equals(other.function))
return false;
if (includeSpanScore != other.includeSpanScore)
return false;
return true;
}
}
}