Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/Lucene.Net.Light.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/Search/Function/CustomScoreQuery.cs')
-rw-r--r--src/core/Search/Function/CustomScoreQuery.cs579
1 files changed, 579 insertions, 0 deletions
diff --git a/src/core/Search/Function/CustomScoreQuery.cs b/src/core/Search/Function/CustomScoreQuery.cs
new file mode 100644
index 0000000..cd6f2b2
--- /dev/null
+++ b/src/core/Search/Function/CustomScoreQuery.cs
@@ -0,0 +1,579 @@
+/*
+ * 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
+{
+
+ /// <summary> Query that sets document score as a programmatic function of several (sub) scores:
+ /// <list type="bullet">
+ /// <item>the score of its subQuery (any query)</item>
+ /// <item>(optional) the score of its ValueSourceQuery (or queries).
+ /// For most simple/convenient use cases this query is likely to be a
+ /// <see cref="Lucene.Net.Search.Function.FieldScoreQuery">FieldScoreQuery</see></item>
+ /// </list>
+ /// Subclasses can modify the computation by overriding <see cref="GetCustomScoreProvider" />.
+ ///
+ /// <p/><font color="#FF0000">
+ /// WARNING: The status of the <b>Search.Function</b> package is experimental.
+ /// The APIs introduced here might change in the future and will not be
+ /// supported anymore in such a case.</font>
+ /// </summary>
+ [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.
+
+ /// <summary> Create a CustomScoreQuery over input subQuery.</summary>
+ /// <param name="subQuery">the sub query whose scored is being customed. Must not be null.
+ /// </param>
+ public CustomScoreQuery(Query subQuery):this(subQuery, new ValueSourceQuery[0])
+ {
+ }
+
+ /// <summary> Create a CustomScoreQuery over input subQuery and a <see cref="ValueSourceQuery" />.</summary>
+ /// <param name="subQuery">the sub query whose score is being customed. Must not be null.
+ /// </param>
+ /// <param name="valSrcQuery">a value source query whose scores are used in the custom score
+ /// computation. For most simple/convineient use case this would be a
+ /// <see cref="Lucene.Net.Search.Function.FieldScoreQuery">FieldScoreQuery</see>.
+ /// This parameter is optional - it can be null or even an empty array.
+ /// </param>
+ public CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery):this(subQuery, valSrcQuery != null?new ValueSourceQuery[]{valSrcQuery}:new ValueSourceQuery[0])
+ {
+ }
+
+ /// <summary> Create a CustomScoreQuery over input subQuery and a <see cref="ValueSourceQuery" />.</summary>
+ /// <param name="subQuery">the sub query whose score is being customized. Must not be null.
+ /// </param>
+ /// <param name="valSrcQueries">value source queries whose scores are used in the custom score
+ /// computation. For most simple/convenient use case these would be
+ /// <see cref="Lucene.Net.Search.Function.FieldScoreQuery">FieldScoreQueries</see>.
+ /// This parameter is optional - it can be null or even an empty array.
+ /// </param>
+ 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("<subquery> must not be null!");
+ }
+
+ /*(non-Javadoc) <see cref="Lucene.Net.Search.Query.rewrite(Lucene.Net.Index.IndexReader) */
+ public override Query Rewrite(IndexReader reader)
+ {
+ CustomScoreQuery clone = null;
+
+ Query sq = subQuery.Rewrite(reader);
+ if (sq != subQuery)
+ {
+ clone = (CustomScoreQuery)Clone();
+ clone.subQuery = sq;
+ }
+
+ for (int i = 0; i < valSrcQueries.Length; i++)
+ {
+ ValueSourceQuery v = (ValueSourceQuery)valSrcQueries[i].Rewrite(reader);
+ if (v != valSrcQueries[i])
+ {
+ if (clone == null) clone = (CustomScoreQuery)Clone();
+ clone.valSrcQueries[i] = v;
+ }
+ }
+
+ return (clone == null) ? this : clone;
+ }
+
+ /*(non-Javadoc) <see cref="Lucene.Net.Search.Query.extractTerms(java.util.Set) */
+ public override void ExtractTerms(System.Collections.Generic.ISet<Term> terms)
+ {
+ subQuery.ExtractTerms(terms);
+ for (int i = 0; i < valSrcQueries.Length; i++)
+ {
+ valSrcQueries[i].ExtractTerms(terms);
+ }
+ }
+
+ /*(non-Javadoc) <see cref="Lucene.Net.Search.Query.clone() */
+ public override System.Object Clone()
+ {
+ CustomScoreQuery clone = (CustomScoreQuery) base.Clone();
+ clone.subQuery = (Query) subQuery.Clone();
+ clone.valSrcQueries = new ValueSourceQuery[valSrcQueries.Length];
+ for (int i = 0; i < valSrcQueries.Length; i++)
+ {
+ clone.valSrcQueries[i] = (ValueSourceQuery) valSrcQueries[i].Clone();
+ }
+ return clone;
+ }
+
+ /* (non-Javadoc) <see cref="Lucene.Net.Search.Query.toString(java.lang.String) */
+ public override System.String ToString(System.String field)
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder(Name()).Append("(");
+ sb.Append(subQuery.ToString(field));
+ for (int i = 0; i < valSrcQueries.Length; i++)
+ {
+ sb.Append(", ").Append(valSrcQueries[i].ToString(field));
+ }
+ sb.Append(")");
+ sb.Append(strict?" STRICT":"");
+ return sb.ToString() + ToStringUtils.Boost(Boost);
+ }
+
+ /// <summary>Returns true if <c>o</c> is equal to this. </summary>
+ 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);
+ }
+
+ /// <summary>Returns a hash code value for this object. </summary>
+ 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);
+
+ }
+
+ /// <summary>
+ /// Returns a <see cref="CustomScoreProvider" /> that calculates the custom scores
+ /// for the given <see cref="IndexReader" />. The default implementation returns a default
+ /// implementation as specified in the docs of <see cref="CustomScoreProvider" />.
+ /// </summary>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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 <see cref="GetCustomScoreProvider" /> and return a subclass
+ /// of <see cref="CustomScoreProvider" /> for the given <see cref="IndexReader" />.
+ /// see CustomScoreProvider#customScore(int,float,float[])
+ /// </summary>
+ [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;
+ }
+
+ /// <summary> 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 <see cref="GetCustomScoreProvider" /> and return a subclass
+ /// of <see cref="CustomScoreProvider" /> for the given <see cref="IndexReader" />.
+ /// </summary>
+ /// <seealso cref="CustomScoreProvider.CustomScore(int,float,float)" />
+ [Obsolete("Will be removed in Lucene 3.1")]
+ public virtual float CustomScore(int doc, float subQueryScore, float valSrcScore)
+ {
+ return subQueryScore * valSrcScore;
+ }
+
+
+
+ /// <summary> 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 <see cref="GetCustomScoreProvider(IndexReader)" /> and return a subclass
+ /// of <see cref="CustomScoreProvider" /> for the given <see cref="IndexReader" />.
+ /// </summary>
+ [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;
+ }
+
+ /// <summary> 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 <see cref="GetCustomScoreProvider" /> and return a subclass
+ /// of <see cref="CustomScoreProvider" /> for the given <see cref="IndexReader" />.
+ /// </summary>
+ [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) <see cref="Lucene.Net.Search.Weight.getQuery() */
+
+ public override Query Query
+ {
+ get { return Enclosing_Instance; }
+ }
+
+ /*(non-Javadoc) <see cref="Lucene.Net.Search.Weight.getValue() */
+
+ public override float Value
+ {
+ get { return Enclosing_Instance.Boost; }
+ }
+
+ /*(non-Javadoc) <see cref="Lucene.Net.Search.Weight.sumOfSquaredWeights() */
+
+ public override float GetSumOfSquaredWeights()
+ {
+ float sum = subQueryWeight.GetSumOfSquaredWeights();
+ for (int i = 0; i < valSrcWeights.Length; i++)
+ {
+ if (qStrict)
+ {
+ var sumsq = valSrcWeights[i].GetSumOfSquaredWeights();
+ // do not include ValueSource part in the query normalization
+ }
+ else
+ {
+ sum += valSrcWeights[i].GetSumOfSquaredWeights();
+ }
+ }
+ sum *= Enclosing_Instance.Boost*Enclosing_Instance.Boost; // boost each sub-weight
+ return sum;
+ }
+
+ /*(non-Javadoc) <see cref="Lucene.Net.Search.Weight.normalize(float) */
+ public override void Normalize(float norm)
+ {
+ norm *= Enclosing_Instance.Boost; // incorporate boost
+ subQueryWeight.Normalize(norm);
+ for (int i = 0; i < valSrcWeights.Length; i++)
+ {
+ if (qStrict)
+ {
+ valSrcWeights[i].Normalize(1); // do not normalize the ValueSource part
+ }
+ else
+ {
+ valSrcWeights[i].Normalize(norm);
+ }
+ }
+ }
+
+ public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
+ {
+ // Pass true for "scoresDocsInOrder", because we
+ // require in-order scoring, even if caller does not,
+ // since we call advance on the valSrcScorers. Pass
+ // false for "topScorer" because we will not invoke
+ // score(Collector) on these scorers:
+ Scorer subQueryScorer = subQueryWeight.Scorer(reader, true, false);
+ if (subQueryScorer == null)
+ {
+ return null;
+ }
+ Scorer[] valSrcScorers = new Scorer[valSrcWeights.Length];
+ for (int i = 0; i < valSrcScorers.Length; i++)
+ {
+ valSrcScorers[i] = valSrcWeights[i].Scorer(reader, true, topScorer);
+ }
+ return new CustomScorer(enclosingInstance, similarity, reader, this, subQueryScorer, valSrcScorers);
+ }
+
+ public override Explanation Explain(IndexReader reader, int doc)
+ {
+ Explanation explain = DoExplain(reader, doc);
+ return explain == null?new Explanation(0.0f, "no matching docs"):explain;
+ }
+
+ private Explanation DoExplain(IndexReader reader, int doc)
+ {
+ Explanation subQueryExpl = subQueryWeight.Explain(reader, doc);
+ if (!subQueryExpl.IsMatch)
+ {
+ return subQueryExpl;
+ }
+ // match
+ Explanation[] valSrcExpls = new Explanation[valSrcWeights.Length];
+ for (int i = 0; i < valSrcWeights.Length; i++)
+ {
+ valSrcExpls[i] = valSrcWeights[i].Explain(reader, doc);
+ }
+ Explanation customExp = Enclosing_Instance.GetCustomScoreProvider(reader).CustomExplain(doc, subQueryExpl, valSrcExpls);
+ float sc = Value * customExp.Value;
+ Explanation res = new ComplexExplanation(true, sc, Enclosing_Instance.ToString() + ", product of:");
+ res.AddDetail(customExp);
+ res.AddDetail(new Explanation(Value, "queryBoost")); // actually using the q boost as q weight (== weight value)
+ return res;
+ }
+
+ public override bool GetScoresDocsOutOfOrder()
+ {
+ return false;
+ }
+ }
+
+
+ //=========================== S C O R E R ============================
+
+ /// <summary> A scorer that applies a (callback) function on scores of the subQuery.</summary>
+ 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) <see cref="Lucene.Net.Search.Scorer.score() */
+ public override float Score()
+ {
+ for (int i = 0; i < valSrcScorers.Length; i++)
+ {
+ vScores[i] = valSrcScorers[i].Score();
+ }
+ return qWeight * provider.CustomScore(subQueryScorer.DocID(), subQueryScorer.Score(), vScores);
+ }
+
+ public override int Advance(int target)
+ {
+ int doc = subQueryScorer.Advance(target);
+ if (doc != NO_MORE_DOCS)
+ {
+ for (int i = 0; i < valSrcScorers.Length; i++)
+ {
+ valSrcScorers[i].Advance(doc);
+ }
+ }
+ return doc;
+ }
+ }
+
+ public override Weight CreateWeight(Searcher searcher)
+ {
+ return new CustomWeight(this, searcher);
+ }
+
+ /// <summary> Checks if this is strict custom scoring.
+ /// In strict custom scoring, the ValueSource part does not participate in weight normalization.
+ /// This may be useful when one wants full control over how scores are modified, and does
+ /// not care about normalizing by the ValueSource part.
+ /// One particular case where this is useful if for testing this query.
+ /// <p/>
+ /// Note: only has effect when the ValueSource part is not null.
+ /// </summary>
+ public virtual bool IsStrict()
+ {
+ return strict;
+ }
+
+ /// <summary> Set the strict mode of this query. </summary>
+ /// <param name="strict">The strict mode to set.
+ /// </param>
+ /// <seealso cref="IsStrict()">
+ /// </seealso>
+ public virtual void SetStrict(bool strict)
+ {
+ this.strict = strict;
+ }
+
+ /// <summary> A short name of this query, used in <see cref="ToString(String)" />.</summary>
+ public virtual System.String Name()
+ {
+ return "custom";
+ }
+ }
+} \ No newline at end of file