/*
* 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 System.Linq;
using Lucene.Net.Index;
using IndexReader = Lucene.Net.Index.IndexReader;
using ToStringUtils = Lucene.Net.Util.ToStringUtils;
using ComplexExplanation = Lucene.Net.Search.ComplexExplanation;
using Explanation = Lucene.Net.Search.Explanation;
using Query = Lucene.Net.Search.Query;
using Scorer = Lucene.Net.Search.Scorer;
using Searcher = Lucene.Net.Search.Searcher;
using Similarity = Lucene.Net.Search.Similarity;
using Weight = Lucene.Net.Search.Weight;
namespace Lucene.Net.Search.Function
{
/// Query that sets document score as a programmatic function of several (sub) scores:
///
/// - the score of its subQuery (any query)
/// - (optional) the score of its ValueSourceQuery (or queries).
/// For most simple/convenient use cases this query is likely to be a
/// FieldScoreQuery
///
/// Subclasses can modify the computation by overriding .
///
///
/// WARNING: The status of the Search.Function package is experimental.
/// The APIs introduced here might change in the future and will not be
/// supported anymore in such a case.
///
[Serializable]
public class CustomScoreQuery:Query, System.ICloneable
{
private Query subQuery;
private ValueSourceQuery[] valSrcQueries; // never null (empty array if there are no valSrcQueries).
private bool strict = false; // if true, valueSource part of query does not take part in weights normalization.
/// Create a CustomScoreQuery over input subQuery.
/// the sub query whose scored is being customed. Must not be null.
///
public CustomScoreQuery(Query subQuery):this(subQuery, new ValueSourceQuery[0])
{
}
/// Create a CustomScoreQuery over input subQuery and a .
/// the sub query whose score is being customed. Must not be null.
///
/// a value source query whose scores are used in the custom score
/// computation. For most simple/convineient use case this would be a
/// FieldScoreQuery.
/// This parameter is optional - it can be null or even an empty array.
///
public CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery):this(subQuery, valSrcQuery != null?new ValueSourceQuery[]{valSrcQuery}:new ValueSourceQuery[0])
{
}
/// Create a CustomScoreQuery over input subQuery and a .
/// the sub query whose score is being customized. Must not be null.
///
/// value source queries whose scores are used in the custom score
/// computation. For most simple/convenient use case these would be
/// FieldScoreQueries.
/// This parameter is optional - it can be null or even an empty array.
///
public CustomScoreQuery(Query subQuery, params ValueSourceQuery[] valSrcQueries)
{
this.subQuery = subQuery;
this.valSrcQueries = valSrcQueries != null?valSrcQueries:new ValueSourceQuery[0];
if (subQuery == null)
throw new System.ArgumentException(" must not be null!");
}
/*(non-Javadoc) terms)
{
subQuery.ExtractTerms(terms);
for (int i = 0; i < valSrcQueries.Length; i++)
{
valSrcQueries[i].ExtractTerms(terms);
}
}
/*(non-Javadoc) Returns true if o is equal to this.
public override bool Equals(System.Object o)
{
if (GetType() != o.GetType())
{
return false;
}
CustomScoreQuery other = (CustomScoreQuery) o;
if (this.Boost != other.Boost ||
!this.subQuery.Equals(other.subQuery) ||
this.strict != other.strict ||
this.valSrcQueries.Length != other.valSrcQueries.Length)
{
return false;
}
// SequenceEqual should properly mimic java's Array.equals()
return valSrcQueries.SequenceEqual(other.valSrcQueries);
}
/// Returns a hash code value for this object.
public override int GetHashCode()
{
int valSrcHash = 0;
for (int i = 0; i < valSrcQueries.Length; i++)
{
// TODO: Simplify this hash code generation
valSrcHash += valSrcQueries[i].GetHashCode();
}
return (GetType().GetHashCode() + subQuery.GetHashCode() + valSrcHash) ^
BitConverter.ToInt32(BitConverter.GetBytes(Boost), 0) ^ (strict ? 1234 : 4321);
}
///
/// Returns a that calculates the custom scores
/// for the given . The default implementation returns a default
/// implementation as specified in the docs of .
///
protected virtual CustomScoreProvider GetCustomScoreProvider(IndexReader reader)
{
// when deprecated methods are removed, do not extend class here, just return new default CustomScoreProvider
return new AnonymousCustomScoreProvider(this, reader);
}
class AnonymousCustomScoreProvider : CustomScoreProvider
{
CustomScoreQuery parent;
public AnonymousCustomScoreProvider(CustomScoreQuery parent, IndexReader reader) : base(reader)
{
this.parent = parent;
}
public override float CustomScore(int doc, float subQueryScore, float[] valSrcScores)
{
return parent.CustomScore(doc, subQueryScore, valSrcScores);
}
public override float CustomScore(int doc, float subQueryScore, float valSrcScore)
{
return parent.CustomScore(doc, subQueryScore, valSrcScore);
}
public override Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation[] valSrcExpls)
{
return parent.CustomExplain(doc, subQueryExpl, valSrcExpls);
}
public override Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl)
{
return parent.CustomExplain(doc, subQueryExpl, valSrcExpl);
}
}
///
/// Compute a custom score by the subQuery score and a number of
/// ValueSourceQuery scores.
///
/// The doc is relative to the current reader, which is
/// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9).
/// Please override and return a subclass
/// of for the given .
/// see CustomScoreProvider#customScore(int,float,float[])
///
[Obsolete("Will be removed in Lucene 3.1")]
public virtual float CustomScore(int doc, float subQueryScore, float[] valSrcScores)
{
if (valSrcScores.Length == 1)
{
return CustomScore(doc, subQueryScore, valSrcScores[0]);
}
if (valSrcScores.Length == 0)
{
return CustomScore(doc, subQueryScore, 1);
}
float score = subQueryScore;
for (int i = 0; i < valSrcScores.Length; i++)
{
score *= valSrcScores[i];
}
return score;
}
/// Compute a custom score by the subQuery score and the ValueSourceQuery score.
///
/// The doc is relative to the current reader, which is
/// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9).
/// Please override and return a subclass
/// of for the given .
///
///
[Obsolete("Will be removed in Lucene 3.1")]
public virtual float CustomScore(int doc, float subQueryScore, float valSrcScore)
{
return subQueryScore * valSrcScore;
}
/// Explain the custom score.
///
/// The doc is relative to the current reader, which is
/// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9).
/// Please override and return a subclass
/// of for the given .
///
[Obsolete("Will be removed in Lucene 3.1")]
public virtual Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation[] valSrcExpls)
{
if (valSrcExpls.Length == 1)
{
return CustomExplain(doc, subQueryExpl, valSrcExpls[0]);
}
if (valSrcExpls.Length == 0)
{
return subQueryExpl;
}
float valSrcScore = 1;
for (int i = 0; i < valSrcExpls.Length; i++)
{
valSrcScore *= valSrcExpls[i].Value;
}
Explanation exp = new Explanation(valSrcScore * subQueryExpl.Value, "custom score: product of:");
exp.AddDetail(subQueryExpl);
for (int i = 0; i < valSrcExpls.Length; i++)
{
exp.AddDetail(valSrcExpls[i]);
}
return exp;
}
/// Explain the custom score.
/// The doc is relative to the current reader, which is
/// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9).
/// Please override and return a subclass
/// of for the given .
///
[Obsolete("Will be removed in Lucene 3.1")]
public virtual Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl)
{
float valSrcScore = 1;
if (valSrcExpl != null)
{
valSrcScore *= valSrcExpl.Value;
}
Explanation exp = new Explanation(valSrcScore * subQueryExpl.Value, "custom score: product of:");
exp.AddDetail(subQueryExpl);
exp.AddDetail(valSrcExpl);
return exp;
}
//=========================== W E I G H T ============================
[Serializable]
private class CustomWeight:Weight
{
private void InitBlock(CustomScoreQuery enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private CustomScoreQuery enclosingInstance;
public CustomScoreQuery Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
internal Similarity similarity;
internal Weight subQueryWeight;
internal Weight[] valSrcWeights;
internal bool qStrict;
public CustomWeight(CustomScoreQuery enclosingInstance, Searcher searcher)
{
InitBlock(enclosingInstance);
this.similarity = Enclosing_Instance.GetSimilarity(searcher);
this.subQueryWeight = Enclosing_Instance.subQuery.Weight(searcher);
this.valSrcWeights = new Weight[Enclosing_Instance.valSrcQueries.Length];
for (int i = 0; i < Enclosing_Instance.valSrcQueries.Length; i++)
{
this.valSrcWeights[i] = Enclosing_Instance.valSrcQueries[i].CreateWeight(searcher);
}
this.qStrict = Enclosing_Instance.strict;
}
/*(non-Javadoc) A scorer that applies a (callback) function on scores of the subQuery.
private class CustomScorer:Scorer
{
private void InitBlock(CustomScoreQuery enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private CustomScoreQuery enclosingInstance;
public CustomScoreQuery Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
private float qWeight;
private Scorer subQueryScorer;
private Scorer[] valSrcScorers;
private IndexReader reader;
private CustomScoreProvider provider;
private float[] vScores; // reused in score() to avoid allocating this array for each doc
// constructor
internal CustomScorer(CustomScoreQuery enclosingInstance, Similarity similarity, IndexReader reader, CustomWeight w, Scorer subQueryScorer, Scorer[] valSrcScorers):base(similarity)
{
InitBlock(enclosingInstance);
this.qWeight = w.Value;
this.subQueryScorer = subQueryScorer;
this.valSrcScorers = valSrcScorers;
this.reader = reader;
this.vScores = new float[valSrcScorers.Length];
this.provider = this.Enclosing_Instance.GetCustomScoreProvider(reader);
}
public override int NextDoc()
{
int doc = subQueryScorer.NextDoc();
if (doc != NO_MORE_DOCS)
{
for (int i = 0; i < valSrcScorers.Length; i++)
{
valSrcScorers[i].Advance(doc);
}
}
return doc;
}
public override int DocID()
{
return subQueryScorer.DocID();
}
/*(non-Javadoc) The strict mode to set.
///
///
///
public virtual void SetStrict(bool strict)
{
this.strict = strict;
}
/// A short name of this query, used in .
public virtual System.String Name()
{
return "custom";
}
}
}